-
Notifications
You must be signed in to change notification settings - Fork 0
/
rptmux
executable file
·233 lines (206 loc) · 6.41 KB
/
rptmux
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/usr/bin/env bash
#
# rptmux
# adds ratpoison keymaps to generate tmux-bound keypresses off Super modkey
#
# desc:
# - queries running tmux for its keybinds (not needed, see todo)
# - generates ratpoison binds off super for the unprefixed tmux key
# - emits the keys by executing 'rpmeta' wrapper
# - writes the sourceable ratpoison bindings to tmpfile
# - invokes ratpoison to source the file, which adds the mappings
#
# todo:
# - processes only 'prefix' keymap from tmux, ignores rest
# - tmux-2.2: "list-keys and list-commands can be run without starting the
# tmux server" so use that, i.e. no longer need to run within tmux
#
# note:
# old way we did this was to actually execute the tmux command; we do
# not do that anymore because it doesn't know which session to target,
# so if we have multiple clients attached to different sessions, only
# the first one will get it, which would require that we look up the
# terminal -> pid -> client -> session mapping, which is more
# difficult, so we just emit a keypress instead (much easier) which
# works with whatever client we are connected with at present moment
#
# scott@smemsh.net
# https://github.com/smemsh/utilx/
# https://spdx.org/licenses/GPL-2.0
#
##############################################################################
# don't expand pathnames in this script, interacts poorly with emit_mapping()
set -f
# maps between tmux charnames and ratpoison/xorg keysym names
#
declare -A charmap # keychars -> keysyms
declare -A shiftmap # as charmap if needs shifting, cf add_maps_puncts()
# ratpoison keymap to enter the bindings into
#
rpmap=top
# name of "ratpoison -c meta" command wrapper
#
rpmetacmd=rpmeta
# string prefix to insert before the bound keysym, eg a
# ratpoison modifier (in this case we prefix 'Super' key)
# todo: specify it, either as arg, env, rcfile
#
rppfx='s-'
# this is the '-T <map>' tmux map that we rebind using the
# Super prefix; any tmux keys in other maps (ie root map) will
# be ignored for the time being
#
tmuxmap='prefix'
# prefix we want to emit (give to rpmeta), this should
# correspond to the tmux prefix in ratpoison parlance (todo:
# query it from tmux instead of having to specify it here)
#
tmuxpfx='C-q'
##############################################################################
#
# tmux -> xorg
# key lookup table
#
# mappings created manually, starting from:
# xmodmap -pke \
# | awk '{for (i = 4; i < NF; i++) printf("%s\n", $i)}' \
# | sort -u \
# | grep -v -e brokenbar -e plusminus \
# | grep -E '\<[a-z]{2,}$'
#
add_maps_puncts ()
{
local arraykey
charmap[';']=semicolon
charmap['/']=slash
charmap['.']=period
charmap['-']=minus
charmap['`']=grave
charmap['=']=equal
charmap[',']=comma
charmap['[']=bracketleft
charmap[']']=bracketright
charmap["'"]=apostrophe
charmap['\']=backslash
# ratpoison acts on "meta" using the following sequence:
#
# (1) reads what keysym name given to "meta"
# (2) looks it up with XStringToKeysym()
# (example "quotedbl" -> 34 (0x22), see <X11/keysymdef.h>)
# (3) gets a platform keycode using XKeysymToKeycode()
# (34 -> 48, which can be seem with "xmodmap -pke")
# (4) uses this to issue an XSendEvent()
#
# however, it isn't smart enough to figure out when it has to
# send shifted ones, so it always issues the very first
# (unmodified) keycode. example:
#
# $ xmodmap -pke | grep quotedbl
# keycode 48 = apostrophe quotedbl apostrophe quotedbl
#
# so "ratpoison -c 'meta quotedbl'" will inject an apostrophe,
# not a quotedbl. to fix this, we have to manually add shift
# prefixes ourselves for those keys which hang off the shift
# modifier, and we do this by using a separate dictionary for
# shifted keys only
#
# TODO ideally this would be fixed in ratpoison, because
# "S-quotedbl" is redundant (it's the same as "S-apostrophe",
# but "quotedbl" is the same as apostrophe according to
# ratpoison, it does not have enough awareness)
#
shiftmap['&']=ampersand
shiftmap['^']=asciicircum
shiftmap['~']=asciitilde
shiftmap['*']=asterisk
shiftmap['@']=at
shiftmap['|']=bar
shiftmap['{']=braceleft
shiftmap['}']=braceright
shiftmap[':']=colon
shiftmap['$']=dollar
shiftmap['!']=exclam
shiftmap['>']=greater
shiftmap['<']=less
shiftmap['#']=numbersign
shiftmap['(']=parenleft
shiftmap[')']=parenright
shiftmap['%']=percent
shiftmap['+']=plus
shiftmap['?']=question
shiftmap['"']=quotedbl
shiftmap['_']=underscore
shiftmap['Space']=space
}
# map also the rest of the normal keyboard keys
# (just ordinary characters bound to themselves)
#
add_maps_alnums ()
{
local c
for c in {0..9} {a..z}; do
charmap[$c]=$c; done
for c in {A..Z}; do
shiftmap[$c]=$c; done
}
##############################################################################
emit_mapping ()
{
local key
local cmd
local mapped
# eat the initial 'bind-key' from tmux list-keys
shift
# first arg may be a 'bind-key' argument (like '-r')
# which we do not support, only simple binds, no flags
# supported
# UPDATE: in v2.1, tmux adds '-T <keymap>' to every
# command, we disregard this from the input by
# shifting past it (and only keymap we want to
# interact with at all is '-T prefix', the rest we
# ignore altogether)
# TODO: if ever there is "-T map -r" instead of "-r -T
# map" this would break... can this happen?)
#
if [[ "$1 $2" == "-T $tmuxmap" ]]
then key=$3
else return; fi
# we only handle single-char mappings; in our tmuxrc,
# only a couple rarely-used binds are chords or
# symbolics, so those mappings can be skipped for now;
#
# TODO: can ratpoison even emit eg control-x ? we should handle
# arbitrary key sequences if so
#
if ((${#key} > 1)) && [[ $key != Space ]]; then
return; fi
# new way calls our 'rpmeta' command, which simply emits the key
# (with added 'S-' prefix if need be; see add_maps_puncts())
#
mapped=${charmap[$key]:-${shiftmap[$key]}}
shiftmapped=${shiftmap[$key]:+'S-'}$mapped
execmd="$rpmetacmd $tmuxpfx $shiftmapped"
definecmd="definekey $rpmap $rppfx$mapped exec $execmd"
echo "$definecmd"
}
emit_mappings ()
{
local line
tmux list-keys \
| while read line; do
emit_mapping $line; done
}
##############################################################################
# generate the mappings and source them into ratpoison
#
main ()
{
local tmpfile
add_maps_puncts
add_maps_alnums
tmpfile=`mktemp`
emit_mappings >| $tmpfile
rp source $tmpfile
rm $tmpfile
}
main "$@"