key_table.go

  1package uv
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"github.com/charmbracelet/x/ansi"
  8	"github.com/xo/terminfo"
  9)
 10
 11// buildKeysTable builds a table of key sequences and their corresponding key
 12// events based on the VT100/VT200, XTerm, and Urxvt terminal specs.
 13func buildKeysTable(flags LegacyKeyEncoding, term string, useTerminfo bool) map[string]Key {
 14	nul := Key{Code: KeySpace, Mod: ModCtrl} // ctrl+@ or ctrl+space
 15	if flags&flagCtrlAt != 0 {
 16		nul = Key{Code: '@', Mod: ModCtrl}
 17	}
 18
 19	tab := Key{Code: KeyTab} // ctrl+i or tab
 20	if flags&flagCtrlI != 0 {
 21		tab = Key{Code: 'i', Mod: ModCtrl}
 22	}
 23
 24	enter := Key{Code: KeyEnter} // ctrl+m or enter
 25	if flags&flagCtrlM != 0 {
 26		enter = Key{Code: 'm', Mod: ModCtrl}
 27	}
 28
 29	esc := Key{Code: KeyEscape} // ctrl+[ or escape
 30	if flags&flagCtrlOpenBracket != 0 {
 31		esc = Key{Code: '[', Mod: ModCtrl} // ctrl+[ or escape
 32	}
 33
 34	del := Key{Code: KeyBackspace}
 35	if flags&flagBackspace != 0 {
 36		del.Code = KeyDelete
 37	}
 38
 39	find := Key{Code: KeyHome}
 40	if flags&flagFind != 0 {
 41		find.Code = KeyFind
 42	}
 43
 44	sel := Key{Code: KeyEnd}
 45	if flags&flagSelect != 0 {
 46		sel.Code = KeySelect
 47	}
 48
 49	// The following is a table of key sequences and their corresponding key
 50	// events based on the VT100/VT200 terminal specs.
 51	//
 52	// See: https://vt100.net/docs/vt100-ug/chapter3.html#S3.2
 53	// See: https://vt100.net/docs/vt220-rm/chapter3.html
 54	//
 55	// XXX: These keys may be overwritten by other options like XTerm or
 56	// Terminfo.
 57	table := map[string]Key{
 58		// C0 control characters
 59		string(byte(ansi.NUL)): nul,
 60		string(byte(ansi.SOH)): {Code: 'a', Mod: ModCtrl},
 61		string(byte(ansi.STX)): {Code: 'b', Mod: ModCtrl},
 62		string(byte(ansi.ETX)): {Code: 'c', Mod: ModCtrl},
 63		string(byte(ansi.EOT)): {Code: 'd', Mod: ModCtrl},
 64		string(byte(ansi.ENQ)): {Code: 'e', Mod: ModCtrl},
 65		string(byte(ansi.ACK)): {Code: 'f', Mod: ModCtrl},
 66		string(byte(ansi.BEL)): {Code: 'g', Mod: ModCtrl},
 67		string(byte(ansi.BS)):  {Code: 'h', Mod: ModCtrl},
 68		string(byte(ansi.HT)):  tab,
 69		string(byte(ansi.LF)):  {Code: 'j', Mod: ModCtrl},
 70		string(byte(ansi.VT)):  {Code: 'k', Mod: ModCtrl},
 71		string(byte(ansi.FF)):  {Code: 'l', Mod: ModCtrl},
 72		string(byte(ansi.CR)):  enter,
 73		string(byte(ansi.SO)):  {Code: 'n', Mod: ModCtrl},
 74		string(byte(ansi.SI)):  {Code: 'o', Mod: ModCtrl},
 75		string(byte(ansi.DLE)): {Code: 'p', Mod: ModCtrl},
 76		string(byte(ansi.DC1)): {Code: 'q', Mod: ModCtrl},
 77		string(byte(ansi.DC2)): {Code: 'r', Mod: ModCtrl},
 78		string(byte(ansi.DC3)): {Code: 's', Mod: ModCtrl},
 79		string(byte(ansi.DC4)): {Code: 't', Mod: ModCtrl},
 80		string(byte(ansi.NAK)): {Code: 'u', Mod: ModCtrl},
 81		string(byte(ansi.SYN)): {Code: 'v', Mod: ModCtrl},
 82		string(byte(ansi.ETB)): {Code: 'w', Mod: ModCtrl},
 83		string(byte(ansi.CAN)): {Code: 'x', Mod: ModCtrl},
 84		string(byte(ansi.EM)):  {Code: 'y', Mod: ModCtrl},
 85		string(byte(ansi.SUB)): {Code: 'z', Mod: ModCtrl},
 86		string(byte(ansi.ESC)): esc,
 87		string(byte(ansi.FS)):  {Code: '\\', Mod: ModCtrl},
 88		string(byte(ansi.GS)):  {Code: ']', Mod: ModCtrl},
 89		string(byte(ansi.RS)):  {Code: '^', Mod: ModCtrl},
 90		string(byte(ansi.US)):  {Code: '_', Mod: ModCtrl},
 91
 92		// Special keys in G0
 93		string(byte(ansi.SP)):  {Code: KeySpace, Text: " "},
 94		string(byte(ansi.DEL)): del,
 95
 96		// Special keys
 97
 98		"\x1b[Z": {Code: KeyTab, Mod: ModShift},
 99
100		"\x1b[1~": find,
101		"\x1b[2~": {Code: KeyInsert},
102		"\x1b[3~": {Code: KeyDelete},
103		"\x1b[4~": sel,
104		"\x1b[5~": {Code: KeyPgUp},
105		"\x1b[6~": {Code: KeyPgDown},
106		"\x1b[7~": {Code: KeyHome},
107		"\x1b[8~": {Code: KeyEnd},
108
109		// Normal mode
110		"\x1b[A": {Code: KeyUp},
111		"\x1b[B": {Code: KeyDown},
112		"\x1b[C": {Code: KeyRight},
113		"\x1b[D": {Code: KeyLeft},
114		"\x1b[E": {Code: KeyBegin},
115		"\x1b[F": {Code: KeyEnd},
116		"\x1b[H": {Code: KeyHome},
117		"\x1b[P": {Code: KeyF1},
118		"\x1b[Q": {Code: KeyF2},
119		"\x1b[R": {Code: KeyF3},
120		"\x1b[S": {Code: KeyF4},
121
122		// Application Cursor Key Mode (DECCKM)
123		"\x1bOA": {Code: KeyUp},
124		"\x1bOB": {Code: KeyDown},
125		"\x1bOC": {Code: KeyRight},
126		"\x1bOD": {Code: KeyLeft},
127		"\x1bOE": {Code: KeyBegin},
128		"\x1bOF": {Code: KeyEnd},
129		"\x1bOH": {Code: KeyHome},
130		"\x1bOP": {Code: KeyF1},
131		"\x1bOQ": {Code: KeyF2},
132		"\x1bOR": {Code: KeyF3},
133		"\x1bOS": {Code: KeyF4},
134
135		// Keypad Application Mode (DECKPAM)
136
137		"\x1bOM": {Code: KeyKpEnter},
138		"\x1bOX": {Code: KeyKpEqual},
139		"\x1bOj": {Code: KeyKpMultiply},
140		"\x1bOk": {Code: KeyKpPlus},
141		"\x1bOl": {Code: KeyKpComma},
142		"\x1bOm": {Code: KeyKpMinus},
143		"\x1bOn": {Code: KeyKpDecimal},
144		"\x1bOo": {Code: KeyKpDivide},
145		"\x1bOp": {Code: KeyKp0},
146		"\x1bOq": {Code: KeyKp1},
147		"\x1bOr": {Code: KeyKp2},
148		"\x1bOs": {Code: KeyKp3},
149		"\x1bOt": {Code: KeyKp4},
150		"\x1bOu": {Code: KeyKp5},
151		"\x1bOv": {Code: KeyKp6},
152		"\x1bOw": {Code: KeyKp7},
153		"\x1bOx": {Code: KeyKp8},
154		"\x1bOy": {Code: KeyKp9},
155
156		// Function keys
157
158		"\x1b[11~": {Code: KeyF1},
159		"\x1b[12~": {Code: KeyF2},
160		"\x1b[13~": {Code: KeyF3},
161		"\x1b[14~": {Code: KeyF4},
162		"\x1b[15~": {Code: KeyF5},
163		"\x1b[17~": {Code: KeyF6},
164		"\x1b[18~": {Code: KeyF7},
165		"\x1b[19~": {Code: KeyF8},
166		"\x1b[20~": {Code: KeyF9},
167		"\x1b[21~": {Code: KeyF10},
168		"\x1b[23~": {Code: KeyF11},
169		"\x1b[24~": {Code: KeyF12},
170		"\x1b[25~": {Code: KeyF13},
171		"\x1b[26~": {Code: KeyF14},
172		"\x1b[28~": {Code: KeyF15},
173		"\x1b[29~": {Code: KeyF16},
174		"\x1b[31~": {Code: KeyF17},
175		"\x1b[32~": {Code: KeyF18},
176		"\x1b[33~": {Code: KeyF19},
177		"\x1b[34~": {Code: KeyF20},
178	}
179
180	// CSI ~ sequence keys
181	csiTildeKeys := map[string]Key{
182		"1": find, "2": {Code: KeyInsert},
183		"3": {Code: KeyDelete}, "4": sel,
184		"5": {Code: KeyPgUp}, "6": {Code: KeyPgDown},
185		"7": {Code: KeyHome}, "8": {Code: KeyEnd},
186		// There are no 9 and 10 keys
187		"11": {Code: KeyF1}, "12": {Code: KeyF2},
188		"13": {Code: KeyF3}, "14": {Code: KeyF4},
189		"15": {Code: KeyF5}, "17": {Code: KeyF6},
190		"18": {Code: KeyF7}, "19": {Code: KeyF8},
191		"20": {Code: KeyF9}, "21": {Code: KeyF10},
192		"23": {Code: KeyF11}, "24": {Code: KeyF12},
193		"25": {Code: KeyF13}, "26": {Code: KeyF14},
194		"28": {Code: KeyF15}, "29": {Code: KeyF16},
195		"31": {Code: KeyF17}, "32": {Code: KeyF18},
196		"33": {Code: KeyF19}, "34": {Code: KeyF20},
197	}
198
199	// URxvt keys
200	// See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
201	table["\x1b[a"] = Key{Code: KeyUp, Mod: ModShift}
202	table["\x1b[b"] = Key{Code: KeyDown, Mod: ModShift}
203	table["\x1b[c"] = Key{Code: KeyRight, Mod: ModShift}
204	table["\x1b[d"] = Key{Code: KeyLeft, Mod: ModShift}
205	table["\x1bOa"] = Key{Code: KeyUp, Mod: ModCtrl}
206	table["\x1bOb"] = Key{Code: KeyDown, Mod: ModCtrl}
207	table["\x1bOc"] = Key{Code: KeyRight, Mod: ModCtrl}
208	table["\x1bOd"] = Key{Code: KeyLeft, Mod: ModCtrl}
209	//nolint:godox
210	// TODO: invistigate if shift-ctrl arrow keys collide with DECCKM keys i.e.
211	// "\x1bOA", "\x1bOB", "\x1bOC", "\x1bOD"
212
213	// URxvt modifier CSI ~ keys
214	for k, v := range csiTildeKeys {
215		key := v
216		// Normal (no modifier) already defined part of VT100/VT200
217		// Shift modifier
218		key.Mod = ModShift
219		table["\x1b["+k+"$"] = key
220		// Ctrl modifier
221		key.Mod = ModCtrl
222		table["\x1b["+k+"^"] = key
223		// Shift-Ctrl modifier
224		key.Mod = ModShift | ModCtrl
225		table["\x1b["+k+"@"] = key
226	}
227
228	// URxvt F keys
229	// Note: Shift + F1-F10 generates F11-F20.
230	// This means Shift + F1 and Shift + F2 will generate F11 and F12, the same
231	// applies to Ctrl + Shift F1 & F2.
232	//
233	// P.S. Don't like this? Blame URxvt, configure your terminal to use
234	// different escapes like XTerm, or switch to a better terminal ¯\_(ツ)_/¯
235	//
236	// See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
237	table["\x1b[23$"] = Key{Code: KeyF11, Mod: ModShift}
238	table["\x1b[24$"] = Key{Code: KeyF12, Mod: ModShift}
239	table["\x1b[25$"] = Key{Code: KeyF13, Mod: ModShift}
240	table["\x1b[26$"] = Key{Code: KeyF14, Mod: ModShift}
241	table["\x1b[28$"] = Key{Code: KeyF15, Mod: ModShift}
242	table["\x1b[29$"] = Key{Code: KeyF16, Mod: ModShift}
243	table["\x1b[31$"] = Key{Code: KeyF17, Mod: ModShift}
244	table["\x1b[32$"] = Key{Code: KeyF18, Mod: ModShift}
245	table["\x1b[33$"] = Key{Code: KeyF19, Mod: ModShift}
246	table["\x1b[34$"] = Key{Code: KeyF20, Mod: ModShift}
247	table["\x1b[11^"] = Key{Code: KeyF1, Mod: ModCtrl}
248	table["\x1b[12^"] = Key{Code: KeyF2, Mod: ModCtrl}
249	table["\x1b[13^"] = Key{Code: KeyF3, Mod: ModCtrl}
250	table["\x1b[14^"] = Key{Code: KeyF4, Mod: ModCtrl}
251	table["\x1b[15^"] = Key{Code: KeyF5, Mod: ModCtrl}
252	table["\x1b[17^"] = Key{Code: KeyF6, Mod: ModCtrl}
253	table["\x1b[18^"] = Key{Code: KeyF7, Mod: ModCtrl}
254	table["\x1b[19^"] = Key{Code: KeyF8, Mod: ModCtrl}
255	table["\x1b[20^"] = Key{Code: KeyF9, Mod: ModCtrl}
256	table["\x1b[21^"] = Key{Code: KeyF10, Mod: ModCtrl}
257	table["\x1b[23^"] = Key{Code: KeyF11, Mod: ModCtrl}
258	table["\x1b[24^"] = Key{Code: KeyF12, Mod: ModCtrl}
259	table["\x1b[25^"] = Key{Code: KeyF13, Mod: ModCtrl}
260	table["\x1b[26^"] = Key{Code: KeyF14, Mod: ModCtrl}
261	table["\x1b[28^"] = Key{Code: KeyF15, Mod: ModCtrl}
262	table["\x1b[29^"] = Key{Code: KeyF16, Mod: ModCtrl}
263	table["\x1b[31^"] = Key{Code: KeyF17, Mod: ModCtrl}
264	table["\x1b[32^"] = Key{Code: KeyF18, Mod: ModCtrl}
265	table["\x1b[33^"] = Key{Code: KeyF19, Mod: ModCtrl}
266	table["\x1b[34^"] = Key{Code: KeyF20, Mod: ModCtrl}
267	table["\x1b[23@"] = Key{Code: KeyF11, Mod: ModShift | ModCtrl}
268	table["\x1b[24@"] = Key{Code: KeyF12, Mod: ModShift | ModCtrl}
269	table["\x1b[25@"] = Key{Code: KeyF13, Mod: ModShift | ModCtrl}
270	table["\x1b[26@"] = Key{Code: KeyF14, Mod: ModShift | ModCtrl}
271	table["\x1b[28@"] = Key{Code: KeyF15, Mod: ModShift | ModCtrl}
272	table["\x1b[29@"] = Key{Code: KeyF16, Mod: ModShift | ModCtrl}
273	table["\x1b[31@"] = Key{Code: KeyF17, Mod: ModShift | ModCtrl}
274	table["\x1b[32@"] = Key{Code: KeyF18, Mod: ModShift | ModCtrl}
275	table["\x1b[33@"] = Key{Code: KeyF19, Mod: ModShift | ModCtrl}
276	table["\x1b[34@"] = Key{Code: KeyF20, Mod: ModShift | ModCtrl}
277
278	// Register Alt + <key> combinations
279	// XXX: this must come after URxvt but before XTerm keys to register URxvt
280	// keys with alt modifier
281	tmap := map[string]Key{}
282	for seq, key := range table {
283		key := key
284		key.Mod |= ModAlt
285		key.Text = "" // Clear runes
286		tmap["\x1b"+seq] = key
287	}
288	for seq, key := range tmap {
289		table[seq] = key
290	}
291
292	// XTerm modifiers
293	// These are offset by 1 to be compatible with our Mod type.
294	// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
295	modifiers := []KeyMod{
296		ModShift,                              // 1
297		ModAlt,                                // 2
298		ModShift | ModAlt,                     // 3
299		ModCtrl,                               // 4
300		ModShift | ModCtrl,                    // 5
301		ModAlt | ModCtrl,                      // 6
302		ModShift | ModAlt | ModCtrl,           // 7
303		ModMeta,                               // 8
304		ModMeta | ModShift,                    // 9
305		ModMeta | ModAlt,                      // 10
306		ModMeta | ModShift | ModAlt,           // 11
307		ModMeta | ModCtrl,                     // 12
308		ModMeta | ModShift | ModCtrl,          // 13
309		ModMeta | ModAlt | ModCtrl,            // 14
310		ModMeta | ModShift | ModAlt | ModCtrl, // 15
311	}
312
313	// SS3 keypad function keys
314	ss3FuncKeys := map[string]Key{
315		// These are defined in XTerm
316		// Taken from Foot keymap.h and XTerm modifyOtherKeys
317		// https://codeberg.org/dnkl/foot/src/branch/master/keymap.h
318		"M": {Code: KeyKpEnter}, "X": {Code: KeyKpEqual},
319		"j": {Code: KeyKpMultiply}, "k": {Code: KeyKpPlus},
320		"l": {Code: KeyKpComma}, "m": {Code: KeyKpMinus},
321		"n": {Code: KeyKpDecimal}, "o": {Code: KeyKpDivide},
322		"p": {Code: KeyKp0}, "q": {Code: KeyKp1},
323		"r": {Code: KeyKp2}, "s": {Code: KeyKp3},
324		"t": {Code: KeyKp4}, "u": {Code: KeyKp5},
325		"v": {Code: KeyKp6}, "w": {Code: KeyKp7},
326		"x": {Code: KeyKp8}, "y": {Code: KeyKp9},
327	}
328
329	// XTerm keys
330	csiFuncKeys := map[string]Key{
331		"A": {Code: KeyUp}, "B": {Code: KeyDown},
332		"C": {Code: KeyRight}, "D": {Code: KeyLeft},
333		"E": {Code: KeyBegin}, "F": {Code: KeyEnd},
334		"H": {Code: KeyHome}, "P": {Code: KeyF1},
335		"Q": {Code: KeyF2}, "R": {Code: KeyF3},
336		"S": {Code: KeyF4},
337	}
338
339	// CSI 27 ; <modifier> ; <code> ~ keys defined in XTerm modifyOtherKeys
340	modifyOtherKeys := map[int]Key{
341		ansi.BS:  {Code: KeyBackspace},
342		ansi.HT:  {Code: KeyTab},
343		ansi.CR:  {Code: KeyEnter},
344		ansi.ESC: {Code: KeyEscape},
345		ansi.DEL: {Code: KeyBackspace},
346	}
347
348	for _, m := range modifiers {
349		// XTerm modifier offset +1
350		xtermMod := strconv.Itoa(int(m) + 1)
351
352		//  CSI 1 ; <modifier> <func>
353		for k, v := range csiFuncKeys {
354			// Functions always have a leading 1 param
355			seq := "\x1b[1;" + xtermMod + k
356			key := v
357			key.Mod = m
358			table[seq] = key
359		}
360		// SS3 <modifier> <func>
361		for k, v := range ss3FuncKeys {
362			seq := "\x1bO" + xtermMod + k
363			key := v
364			key.Mod = m
365			table[seq] = key
366		}
367		//  CSI <number> ; <modifier> ~
368		for k, v := range csiTildeKeys {
369			seq := "\x1b[" + k + ";" + xtermMod + "~"
370			key := v
371			key.Mod = m
372			table[seq] = key
373		}
374		// CSI 27 ; <modifier> ; <code> ~
375		for k, v := range modifyOtherKeys {
376			code := strconv.Itoa(k)
377			seq := "\x1b[27;" + xtermMod + ";" + code + "~"
378			key := v
379			key.Mod = m
380			table[seq] = key
381		}
382	}
383
384	// Register terminfo keys
385	// XXX: this might override keys already registered in table
386	if useTerminfo {
387		titable := buildTerminfoKeys(flags, term)
388		for seq, key := range titable {
389			table[seq] = key
390		}
391	}
392
393	return table
394}
395
396func buildTerminfoKeys(flags LegacyKeyEncoding, term string) map[string]Key {
397	table := make(map[string]Key)
398	ti, _ := terminfo.Load(term)
399	if ti == nil {
400		return table
401	}
402
403	tiTable := defaultTerminfoKeys(flags)
404
405	// Default keys
406	for name, seq := range ti.StringCapsShort() {
407		if !strings.HasPrefix(name, "k") || len(seq) == 0 {
408			continue
409		}
410
411		if k, ok := tiTable[name]; ok {
412			table[string(seq)] = k
413		}
414	}
415
416	// Extended keys
417	for name, seq := range ti.ExtStringCapsShort() {
418		if !strings.HasPrefix(name, "k") || len(seq) == 0 {
419			continue
420		}
421
422		if k, ok := tiTable[name]; ok {
423			table[string(seq)] = k
424		}
425	}
426
427	return table
428}
429
430// This returns a map of terminfo keys to key events. It's a mix of ncurses
431// terminfo default and user-defined key capabilities.
432// Upper-case caps that are defined in the default terminfo database are
433//   - kNXT
434//   - kPRV
435//   - kHOM
436//   - kEND
437//   - kDC
438//   - kIC
439//   - kLFT
440//   - kRIT
441//
442// See https://man7.org/linux/man-pages/man5/terminfo.5.html
443// See https://github.com/mirror/ncurses/blob/master/include/Caps-ncurses
444func defaultTerminfoKeys(flags LegacyKeyEncoding) map[string]Key {
445	keys := map[string]Key{
446		"kcuu1": {Code: KeyUp},
447		"kUP":   {Code: KeyUp, Mod: ModShift},
448		"kUP3":  {Code: KeyUp, Mod: ModAlt},
449		"kUP4":  {Code: KeyUp, Mod: ModShift | ModAlt},
450		"kUP5":  {Code: KeyUp, Mod: ModCtrl},
451		"kUP6":  {Code: KeyUp, Mod: ModShift | ModCtrl},
452		"kUP7":  {Code: KeyUp, Mod: ModAlt | ModCtrl},
453		"kUP8":  {Code: KeyUp, Mod: ModShift | ModAlt | ModCtrl},
454		"kcud1": {Code: KeyDown},
455		"kDN":   {Code: KeyDown, Mod: ModShift},
456		"kDN3":  {Code: KeyDown, Mod: ModAlt},
457		"kDN4":  {Code: KeyDown, Mod: ModShift | ModAlt},
458		"kDN5":  {Code: KeyDown, Mod: ModCtrl},
459		"kDN7":  {Code: KeyDown, Mod: ModAlt | ModCtrl},
460		"kDN6":  {Code: KeyDown, Mod: ModShift | ModCtrl},
461		"kDN8":  {Code: KeyDown, Mod: ModShift | ModAlt | ModCtrl},
462		"kcub1": {Code: KeyLeft},
463		"kLFT":  {Code: KeyLeft, Mod: ModShift},
464		"kLFT3": {Code: KeyLeft, Mod: ModAlt},
465		"kLFT4": {Code: KeyLeft, Mod: ModShift | ModAlt},
466		"kLFT5": {Code: KeyLeft, Mod: ModCtrl},
467		"kLFT6": {Code: KeyLeft, Mod: ModShift | ModCtrl},
468		"kLFT7": {Code: KeyLeft, Mod: ModAlt | ModCtrl},
469		"kLFT8": {Code: KeyLeft, Mod: ModShift | ModAlt | ModCtrl},
470		"kcuf1": {Code: KeyRight},
471		"kRIT":  {Code: KeyRight, Mod: ModShift},
472		"kRIT3": {Code: KeyRight, Mod: ModAlt},
473		"kRIT4": {Code: KeyRight, Mod: ModShift | ModAlt},
474		"kRIT5": {Code: KeyRight, Mod: ModCtrl},
475		"kRIT6": {Code: KeyRight, Mod: ModShift | ModCtrl},
476		"kRIT7": {Code: KeyRight, Mod: ModAlt | ModCtrl},
477		"kRIT8": {Code: KeyRight, Mod: ModShift | ModAlt | ModCtrl},
478		"kich1": {Code: KeyInsert},
479		"kIC":   {Code: KeyInsert, Mod: ModShift},
480		"kIC3":  {Code: KeyInsert, Mod: ModAlt},
481		"kIC4":  {Code: KeyInsert, Mod: ModShift | ModAlt},
482		"kIC5":  {Code: KeyInsert, Mod: ModCtrl},
483		"kIC6":  {Code: KeyInsert, Mod: ModShift | ModCtrl},
484		"kIC7":  {Code: KeyInsert, Mod: ModAlt | ModCtrl},
485		"kIC8":  {Code: KeyInsert, Mod: ModShift | ModAlt | ModCtrl},
486		"kdch1": {Code: KeyDelete},
487		"kDC":   {Code: KeyDelete, Mod: ModShift},
488		"kDC3":  {Code: KeyDelete, Mod: ModAlt},
489		"kDC4":  {Code: KeyDelete, Mod: ModShift | ModAlt},
490		"kDC5":  {Code: KeyDelete, Mod: ModCtrl},
491		"kDC6":  {Code: KeyDelete, Mod: ModShift | ModCtrl},
492		"kDC7":  {Code: KeyDelete, Mod: ModAlt | ModCtrl},
493		"kDC8":  {Code: KeyDelete, Mod: ModShift | ModAlt | ModCtrl},
494		"khome": {Code: KeyHome},
495		"kHOM":  {Code: KeyHome, Mod: ModShift},
496		"kHOM3": {Code: KeyHome, Mod: ModAlt},
497		"kHOM4": {Code: KeyHome, Mod: ModShift | ModAlt},
498		"kHOM5": {Code: KeyHome, Mod: ModCtrl},
499		"kHOM6": {Code: KeyHome, Mod: ModShift | ModCtrl},
500		"kHOM7": {Code: KeyHome, Mod: ModAlt | ModCtrl},
501		"kHOM8": {Code: KeyHome, Mod: ModShift | ModAlt | ModCtrl},
502		"kend":  {Code: KeyEnd},
503		"kEND":  {Code: KeyEnd, Mod: ModShift},
504		"kEND3": {Code: KeyEnd, Mod: ModAlt},
505		"kEND4": {Code: KeyEnd, Mod: ModShift | ModAlt},
506		"kEND5": {Code: KeyEnd, Mod: ModCtrl},
507		"kEND6": {Code: KeyEnd, Mod: ModShift | ModCtrl},
508		"kEND7": {Code: KeyEnd, Mod: ModAlt | ModCtrl},
509		"kEND8": {Code: KeyEnd, Mod: ModShift | ModAlt | ModCtrl},
510		"kpp":   {Code: KeyPgUp},
511		"kprv":  {Code: KeyPgUp},
512		"kPRV":  {Code: KeyPgUp, Mod: ModShift},
513		"kPRV3": {Code: KeyPgUp, Mod: ModAlt},
514		"kPRV4": {Code: KeyPgUp, Mod: ModShift | ModAlt},
515		"kPRV5": {Code: KeyPgUp, Mod: ModCtrl},
516		"kPRV6": {Code: KeyPgUp, Mod: ModShift | ModCtrl},
517		"kPRV7": {Code: KeyPgUp, Mod: ModAlt | ModCtrl},
518		"kPRV8": {Code: KeyPgUp, Mod: ModShift | ModAlt | ModCtrl},
519		"knp":   {Code: KeyPgDown},
520		"knxt":  {Code: KeyPgDown},
521		"kNXT":  {Code: KeyPgDown, Mod: ModShift},
522		"kNXT3": {Code: KeyPgDown, Mod: ModAlt},
523		"kNXT4": {Code: KeyPgDown, Mod: ModShift | ModAlt},
524		"kNXT5": {Code: KeyPgDown, Mod: ModCtrl},
525		"kNXT6": {Code: KeyPgDown, Mod: ModShift | ModCtrl},
526		"kNXT7": {Code: KeyPgDown, Mod: ModAlt | ModCtrl},
527		"kNXT8": {Code: KeyPgDown, Mod: ModShift | ModAlt | ModCtrl},
528
529		"kbs":  {Code: KeyBackspace},
530		"kcbt": {Code: KeyTab, Mod: ModShift},
531
532		// Function keys
533		// This only includes the first 12 function keys. The rest are treated
534		// as modifiers of the first 12.
535		// Take a look at XTerm modifyFunctionKeys
536		//
537		// XXX: To use unambiguous function keys, use fixterms or kitty clipboard.
538		//
539		// See https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyFunctionKeys
540		// See https://invisible-island.net/xterm/terminfo.html
541
542		"kf1":  {Code: KeyF1},
543		"kf2":  {Code: KeyF2},
544		"kf3":  {Code: KeyF3},
545		"kf4":  {Code: KeyF4},
546		"kf5":  {Code: KeyF5},
547		"kf6":  {Code: KeyF6},
548		"kf7":  {Code: KeyF7},
549		"kf8":  {Code: KeyF8},
550		"kf9":  {Code: KeyF9},
551		"kf10": {Code: KeyF10},
552		"kf11": {Code: KeyF11},
553		"kf12": {Code: KeyF12},
554		"kf13": {Code: KeyF1, Mod: ModShift},
555		"kf14": {Code: KeyF2, Mod: ModShift},
556		"kf15": {Code: KeyF3, Mod: ModShift},
557		"kf16": {Code: KeyF4, Mod: ModShift},
558		"kf17": {Code: KeyF5, Mod: ModShift},
559		"kf18": {Code: KeyF6, Mod: ModShift},
560		"kf19": {Code: KeyF7, Mod: ModShift},
561		"kf20": {Code: KeyF8, Mod: ModShift},
562		"kf21": {Code: KeyF9, Mod: ModShift},
563		"kf22": {Code: KeyF10, Mod: ModShift},
564		"kf23": {Code: KeyF11, Mod: ModShift},
565		"kf24": {Code: KeyF12, Mod: ModShift},
566		"kf25": {Code: KeyF1, Mod: ModCtrl},
567		"kf26": {Code: KeyF2, Mod: ModCtrl},
568		"kf27": {Code: KeyF3, Mod: ModCtrl},
569		"kf28": {Code: KeyF4, Mod: ModCtrl},
570		"kf29": {Code: KeyF5, Mod: ModCtrl},
571		"kf30": {Code: KeyF6, Mod: ModCtrl},
572		"kf31": {Code: KeyF7, Mod: ModCtrl},
573		"kf32": {Code: KeyF8, Mod: ModCtrl},
574		"kf33": {Code: KeyF9, Mod: ModCtrl},
575		"kf34": {Code: KeyF10, Mod: ModCtrl},
576		"kf35": {Code: KeyF11, Mod: ModCtrl},
577		"kf36": {Code: KeyF12, Mod: ModCtrl},
578		"kf37": {Code: KeyF1, Mod: ModShift | ModCtrl},
579		"kf38": {Code: KeyF2, Mod: ModShift | ModCtrl},
580		"kf39": {Code: KeyF3, Mod: ModShift | ModCtrl},
581		"kf40": {Code: KeyF4, Mod: ModShift | ModCtrl},
582		"kf41": {Code: KeyF5, Mod: ModShift | ModCtrl},
583		"kf42": {Code: KeyF6, Mod: ModShift | ModCtrl},
584		"kf43": {Code: KeyF7, Mod: ModShift | ModCtrl},
585		"kf44": {Code: KeyF8, Mod: ModShift | ModCtrl},
586		"kf45": {Code: KeyF9, Mod: ModShift | ModCtrl},
587		"kf46": {Code: KeyF10, Mod: ModShift | ModCtrl},
588		"kf47": {Code: KeyF11, Mod: ModShift | ModCtrl},
589		"kf48": {Code: KeyF12, Mod: ModShift | ModCtrl},
590		"kf49": {Code: KeyF1, Mod: ModAlt},
591		"kf50": {Code: KeyF2, Mod: ModAlt},
592		"kf51": {Code: KeyF3, Mod: ModAlt},
593		"kf52": {Code: KeyF4, Mod: ModAlt},
594		"kf53": {Code: KeyF5, Mod: ModAlt},
595		"kf54": {Code: KeyF6, Mod: ModAlt},
596		"kf55": {Code: KeyF7, Mod: ModAlt},
597		"kf56": {Code: KeyF8, Mod: ModAlt},
598		"kf57": {Code: KeyF9, Mod: ModAlt},
599		"kf58": {Code: KeyF10, Mod: ModAlt},
600		"kf59": {Code: KeyF11, Mod: ModAlt},
601		"kf60": {Code: KeyF12, Mod: ModAlt},
602		"kf61": {Code: KeyF1, Mod: ModShift | ModAlt},
603		"kf62": {Code: KeyF2, Mod: ModShift | ModAlt},
604		"kf63": {Code: KeyF3, Mod: ModShift | ModAlt},
605	}
606
607	// Preserve F keys from F13 to F63 instead of using them for F-keys
608	// modifiers.
609	if flags&flagFKeys != 0 {
610		keys["kf13"] = Key{Code: KeyF13}
611		keys["kf14"] = Key{Code: KeyF14}
612		keys["kf15"] = Key{Code: KeyF15}
613		keys["kf16"] = Key{Code: KeyF16}
614		keys["kf17"] = Key{Code: KeyF17}
615		keys["kf18"] = Key{Code: KeyF18}
616		keys["kf19"] = Key{Code: KeyF19}
617		keys["kf20"] = Key{Code: KeyF20}
618		keys["kf21"] = Key{Code: KeyF21}
619		keys["kf22"] = Key{Code: KeyF22}
620		keys["kf23"] = Key{Code: KeyF23}
621		keys["kf24"] = Key{Code: KeyF24}
622		keys["kf25"] = Key{Code: KeyF25}
623		keys["kf26"] = Key{Code: KeyF26}
624		keys["kf27"] = Key{Code: KeyF27}
625		keys["kf28"] = Key{Code: KeyF28}
626		keys["kf29"] = Key{Code: KeyF29}
627		keys["kf30"] = Key{Code: KeyF30}
628		keys["kf31"] = Key{Code: KeyF31}
629		keys["kf32"] = Key{Code: KeyF32}
630		keys["kf33"] = Key{Code: KeyF33}
631		keys["kf34"] = Key{Code: KeyF34}
632		keys["kf35"] = Key{Code: KeyF35}
633		keys["kf36"] = Key{Code: KeyF36}
634		keys["kf37"] = Key{Code: KeyF37}
635		keys["kf38"] = Key{Code: KeyF38}
636		keys["kf39"] = Key{Code: KeyF39}
637		keys["kf40"] = Key{Code: KeyF40}
638		keys["kf41"] = Key{Code: KeyF41}
639		keys["kf42"] = Key{Code: KeyF42}
640		keys["kf43"] = Key{Code: KeyF43}
641		keys["kf44"] = Key{Code: KeyF44}
642		keys["kf45"] = Key{Code: KeyF45}
643		keys["kf46"] = Key{Code: KeyF46}
644		keys["kf47"] = Key{Code: KeyF47}
645		keys["kf48"] = Key{Code: KeyF48}
646		keys["kf49"] = Key{Code: KeyF49}
647		keys["kf50"] = Key{Code: KeyF50}
648		keys["kf51"] = Key{Code: KeyF51}
649		keys["kf52"] = Key{Code: KeyF52}
650		keys["kf53"] = Key{Code: KeyF53}
651		keys["kf54"] = Key{Code: KeyF54}
652		keys["kf55"] = Key{Code: KeyF55}
653		keys["kf56"] = Key{Code: KeyF56}
654		keys["kf57"] = Key{Code: KeyF57}
655		keys["kf58"] = Key{Code: KeyF58}
656		keys["kf59"] = Key{Code: KeyF59}
657		keys["kf60"] = Key{Code: KeyF60}
658		keys["kf61"] = Key{Code: KeyF61}
659		keys["kf62"] = Key{Code: KeyF62}
660		keys["kf63"] = Key{Code: KeyF63}
661	}
662
663	return keys
664}