@@ -2,7 +2,7 @@ vim9script
22
33# Script to get various codes that keys send, depending on the protocol used.
44#
5- # Usage: vim - u keycode_check.vim
5+ # Usage: vim - u NONE - S keycode_check.vim
66#
77# Author: Bram Moolenaar
88# Last Update: 2022 Nov 15
@@ -76,6 +76,54 @@ var key_entries = [
7676 [' Alt-Space' , ' A-Space' ],
7777 ]
7878
79+ # Given a terminal name and a item name, return the text to display .
80+ def GetItemDisplay (term : string , item: string ): string
81+ var val = get (keycodes[term ], item, ' ' )
82+
83+ # see if we can pretty- print this one
84+ var pretty = val
85+ if val[0 : 1 ] == ' 1b'
86+ pretty = ' ESC'
87+ var idx = 2
88+
89+ if val[0 : 3 ] == ' 1b5b'
90+ pretty = ' CSI'
91+ idx = 4
92+ endif
93+
94+ var digits = false
95+ while idx < len (val)
96+ var cc = val[idx : idx + 1 ]
97+ var nr = str2nr (' 0x' .. cc , 16 )
98+ idx += 2
99+ if nr >= char2nr (' 0' ) && nr <= char2nr (' 9' )
100+ if ! digits
101+ pretty ..= ' '
102+ endif
103+ digits = true
104+ pretty ..= cc [1 ]
105+ else
106+ if nr == char2nr (' ;' ) && digits
107+ # don't use space between semicolon and digits to keep it short
108+ pretty ..= ' ;'
109+ else
110+ digits = false
111+ if nr >= char2nr (' ' ) && nr <= char2nr (' ~' )
112+ # printable character
113+ pretty ..= ' ' .. printf (' %c' , nr)
114+ else
115+ # non- printable, use hex code
116+ pretty = val
117+ break
118+ endif
119+ endif
120+ endif
121+ endwhile
122+ endif
123+
124+ return pretty
125+ enddef
126+
79127
80128# Action: list the information in " keycodes" in a more or less nice way.
81129def ActionList ()
@@ -84,66 +132,54 @@ def ActionList()
84132 echo ' No terminal results yet'
85133 return
86134 endif
135+ sort (terms)
87136
88- # Use one column of width 10 for the item name, then columns of 20
89- # characters to fit most codes. You will need to increase the terminal
90- # width to avoid wrapping.
91- echon printf (' ' )
92- for term in terms
93- echon printf (' %-20s' , term )
94- endfor
95- echo " \n "
137+ var items = [' protocol' , ' version' , ' status' ]
138+ + key_entries- >copy ()- >map ((_, v ) = > v [1 ])
96139
97- var items = [' protocol' ] + key_entries- >copy ()- >map ((_, v ) = > v [1 ])
140+ # For each terminal compute the needed width, add two.
141+ # You may need to increase the terminal width to avoid wrapping.
142+ var widths = []
143+ for [idx, term ] in items (terms)
144+ widths[idx] = len (term ) + 2
145+ endfor
98146
99147 for item in items
100- echon printf (' %8s ' , item)
101- for term in terms
102- var val = get (keycodes[term ], item, ' ' )
103-
104- # see if we can pretty- print this one
105- var pretty = val
106- if val[0 : 1 ] == ' 1b'
107- pretty = ' ESC'
108- var idx = 2
109-
110- if val[0 : 3 ] == ' 1b5b'
111- pretty = ' CSI'
112- idx = 4
113- endif
114-
115- var digits = false
116- while idx < len (val)
117- var cc = val[idx : idx + 1 ]
118- var nr = str2nr (' 0x' .. cc , 16 )
119- idx += 2
120- if nr >= char2nr (' 0' ) && nr <= char2nr (' 9' )
121- if ! digits
122- pretty ..= ' '
123- endif
124- digits = true
125- pretty ..= cc [1 ]
126- else
127- digits = false
128- if nr >= char2nr (' ' ) && nr <= char2nr (' ~' )
129- # printable character
130- pretty ..= ' ' .. printf (' %c' , nr)
131- else
132- # non- printable, use hex code
133- pretty = val
134- break
135- endif
136- endif
137- endwhile
148+ for [idx, term ] in items (terms)
149+ var l = len (GetItemDisplay (term , item))
150+ if widths[idx] < l + 2
151+ widths[idx] = l + 2
138152 endif
153+ endfor
154+ endfor
155+
156+ # Use one column of width 10 for the item name.
157+ echo " \n "
158+ echon ' '
159+ for [idx, term ] in items (terms)
160+ echon printf (' %-' .. widths[idx] .. ' s' , term )
161+ endfor
162+ echo " \n "
139163
140- echon printf (' %-20s' , pretty)
164+ for item in items
165+ echon printf (' %8s ' , item)
166+ for [idx, term ] in items (terms)
167+ echon printf (' %-' .. widths[idx] .. ' s' , GetItemDisplay (term , item))
141168 endfor
142169 echo ' '
143170 endfor
144171 echo " \n "
145172enddef
146173
174+ # Convert the literal string after " raw key input" into hex form.
175+ def Literal2hex (code: string ): string
176+ var hex = ' '
177+ for i in range (len (code))
178+ hex ..= printf (' %02x' , char2nr (code[i ]))
179+ endfor
180+ return hex
181+ enddef
182+
147183def GetTermName (): string
148184 var name = input (' Enter the name of the terminal: ' )
149185 return name
@@ -162,36 +198,112 @@ def DoTerm(name: string)
162198 if proto == 1
163199 &t_TI = " "
164200 elseif proto == 2
201+ # Enable modifyOtherKeys level 2 - no status is reported
165202 &t_TI = " \<Esc> [>4;2m"
166203 proto_name = ' mok2'
167204 elseif proto == 3
168- &t_TI = " \<Esc> [>1u"
205+ # Enable Kitty keyboard protocol and request the status
206+ &t_TI = " \<Esc> [>1u" .. " \<Esc> [?u"
169207 proto_name = ' kitty'
170208 else
171209 echoerr ' invalid protocol choice'
172210 return
173211 endif
174212
213+ # Append the request for the version response, this is used to check we have
214+ # the results.
215+ &t_TI ..= " \<Esc> [>c"
216+
217+ # Pattern that matches the line with the version response.
218+ const version_pattern = " \<Esc> \\ [>\\ d\\ +;\\ d\\ +;\\ d*c"
219+
220+ # Pattern that matches the line with the status. Currently what terminals
221+ # return for the Kitty keyboard protocol.
222+ const status_pattern = " \<Esc> \\ [?\\ d\\ +u"
223+
224+ ch_logfile (' keylog' , ' w' )
225+
175226 # executing a dummy shell command will output t_TI
176227 ! echo >/dev/ null
177228
229+ # Wait until the log file has the version response.
230+ var startTime = reltime ()
231+ var seenVersion = false
232+ while ! seenVersion
233+ var log = readfile (' keylog' )
234+ if len (log ) > 2
235+ for line in log
236+ if line = ~ ' raw key input'
237+ var code = substitute (line , ' .*raw key input: "\([^"]*\).*' , ' \1' , ' ' )
238+ if code = ~ version_pattern
239+ seenVersion = true
240+ echo ' Found the version response'
241+ break
242+ endif
243+ endif
244+ endfor
245+ endif
246+ if reltime (startTime)- >reltimefloat () > 3
247+ break
248+ endif
249+ endwhile
250+
251+ echo ' seenVersion: ' seenVersion
252+
253+ # Prepare the terminal entry, set protocol and clear status and version .
178254 if ! has_key (keycodes, name)
179255 keycodes[name] = {}
180256 endif
181257 keycodes[name][' protocol' ] = proto_name
258+ keycodes[name][' version' ] = ' '
259+ keycodes[name][' status' ] = ' '
182260
183- echo " When a key press doesn't get to Vim (e.g. when using Alt) press Space"
261+ # Check the log file for a status and the version response
262+ ch_logfile (' ' , ' ' )
263+ var log = readfile (' keylog' )
264+ delete (' keylog' )
265+ for line in log
266+ if line = ~ ' raw key input'
267+ var code = substitute (line , ' .*raw key input: "\([^"]*\).*' , ' \1' , ' ' )
268+ # Check for kitty keyboard protocol status
269+ if code = ~ status_pattern
270+ var status = substitute (code, ' .*\(' .. status_pattern .. ' \).*' , ' \1' , ' ' )
271+ keycodes[name][' status' ] = Literal2hex (status)
272+ endif
273+
274+ if code = ~ version_pattern
275+ var version = substitute (code, ' .*\(' .. version_pattern .. ' \).*' , ' \1' , ' ' )
276+ keycodes[name][' version' ] = Literal2hex (version )
277+ break
278+ endif
279+ endif
280+ endfor
281+
282+ echo " For Alt to work you may need to press the Windows/Super key as well"
283+ echo " When a key press doesn't get to Vim (e.g. when using Alt) press x"
184284
185285 for entry in key_entries
286+ # Consume any typeahead. Wait a bit for any responses to arrive.
287+ sleep 100 m
288+ while getchar (1 )
289+ getchar ()
290+ sleep 100 m
291+ endwhile
292+
186293 ch_logfile (' keylog' , ' w' )
187294 echo $ ' Press the {entry[0]} key (q to quit):'
188295 var r = getcharstr ()
189296 ch_logfile (' ' , ' ' )
190297 if r == ' q'
191298 break
192299 endif
193- var log = readfile (' keylog' )
194- delete (' keylog' )
300+ log = readfile (' keylog' )
301+ if entry[1 ] == ' Tab'
302+ # keep a copy
303+ rename (' keylog' , ' keylog-tab' )
304+ else
305+ delete (' keylog' )
306+ endif
195307 if len (log ) < 2
196308 echoerr ' failed to read result'
197309 return
@@ -201,11 +313,26 @@ def DoTerm(name: string)
201313 if line = ~ ' raw key input'
202314 var code = substitute (line , ' .*raw key input: "\([^"]*\).*' , ' \1' , ' ' )
203315
204- # convert the literal bytes into hex
316+ # Remove any version termresponse
317+ code = substitute (code, version_pattern, ' ' , ' g' )
318+
319+ # Remove any XTGETTCAP replies.
320+ const cappat = " \<Esc> P[01]+\\ k\\ +=\\ x*\<Esc> \\\\ "
321+ code = substitute (code, cappat, ' ' , ' g' )
322+
323+ # Remove any kitty status reply
324+ code = substitute (code, status_pattern, ' ' , ' g' )
325+ if code == ' '
326+ continue
327+ endif
328+
329+ # Convert the literal bytes into hex. If ' x' was pressed then clear
330+ # the entry.
205331 var hex = ' '
206- for i in range (len (code))
207- hex ..= printf (' %02x' , char2nr (code[i ]))
208- endfor
332+ if code != ' x'
333+ hex = Literal2hex (code)
334+ endif
335+
209336 keycodes[name][entry[1 ]] = hex
210337 done = true
211338 break
@@ -241,8 +368,26 @@ def ActionReplace()
241368 echo " \n "
242369 if choice > 0 && choice <= len (terms)
243370 DoTerm (terms[choice - 1 ])
371+ else
372+ echo ' invalid index'
373+ endif
374+ enddef
375+
376+ # Action: Clear key codes for an already known terminal .
377+ def ActionClear ()
378+ var terms = keys (keycodes)
379+ if len (terms) == 0
380+ echo ' No terminal results yet'
381+ return
382+ endif
383+
384+ var choice = inputlist ([' Select:' ] + terms- >copy ()- >map ((idx, arg) = > (idx + 1 ) .. ' : ' .. arg))
385+ echo " \n "
386+ if choice > 0 && choice <= len (terms)
387+ remove (keycodes, terms[choice - 1 ])
388+ else
389+ echo ' invalid index'
244390 endif
245- echo ' invalid index'
246391enddef
247392
248393# Action: Quit, possibly after saving the results first .
@@ -271,7 +416,8 @@ while true
271416 ' 1. List results' ,
272417 ' 2. Add results for a new terminal' ,
273418 ' 3. Replace results' ,
274- ' 4. Quit' ,
419+ ' 4. Clear results' ,
420+ ' 5. Quit' ,
275421 ])
276422 echo " \n "
277423 if action == 1
@@ -281,6 +427,8 @@ while true
281427 elseif action == 3
282428 ActionReplace ()
283429 elseif action == 4
430+ ActionClear ()
431+ elseif action == 5
284432 ActionQuit ()
285433 endif
286434endwhile
0 commit comments