table.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package input
  2. import (
  3. "strconv"
  4. "github.com/charmbracelet/x/ansi"
  5. )
  6. func buildKeysTable(flags int, term string) map[string]Key {
  7. nul := Key{Rune: ' ', Sym: KeySpace, Mod: ModCtrl} // ctrl+@ or ctrl+space
  8. if flags&FlagCtrlAt != 0 {
  9. nul = Key{Rune: '@', Mod: ModCtrl}
  10. }
  11. tab := Key{Sym: KeyTab} // ctrl+i or tab
  12. if flags&FlagCtrlI != 0 {
  13. tab = Key{Rune: 'i', Mod: ModCtrl}
  14. }
  15. enter := Key{Sym: KeyEnter} // ctrl+m or enter
  16. if flags&FlagCtrlM != 0 {
  17. enter = Key{Rune: 'm', Mod: ModCtrl}
  18. }
  19. esc := Key{Sym: KeyEscape} // ctrl+[ or escape
  20. if flags&FlagCtrlOpenBracket != 0 {
  21. esc = Key{Rune: '[', Mod: ModCtrl} // ctrl+[ or escape
  22. }
  23. del := Key{Sym: KeyBackspace}
  24. if flags&FlagBackspace != 0 {
  25. del.Sym = KeyDelete
  26. }
  27. find := Key{Sym: KeyHome}
  28. if flags&FlagFind != 0 {
  29. find.Sym = KeyFind
  30. }
  31. sel := Key{Sym: KeyEnd}
  32. if flags&FlagSelect != 0 {
  33. sel.Sym = KeySelect
  34. }
  35. // The following is a table of key sequences and their corresponding key
  36. // events based on the VT100/VT200 terminal specs.
  37. //
  38. // See: https://vt100.net/docs/vt100-ug/chapter3.html#S3.2
  39. // See: https://vt100.net/docs/vt220-rm/chapter3.html
  40. //
  41. // XXX: These keys may be overwritten by other options like XTerm or
  42. // Terminfo.
  43. table := map[string]Key{
  44. // C0 control characters
  45. string(byte(ansi.NUL)): nul,
  46. string(byte(ansi.SOH)): {Rune: 'a', Mod: ModCtrl},
  47. string(byte(ansi.STX)): {Rune: 'b', Mod: ModCtrl},
  48. string(byte(ansi.ETX)): {Rune: 'c', Mod: ModCtrl},
  49. string(byte(ansi.EOT)): {Rune: 'd', Mod: ModCtrl},
  50. string(byte(ansi.ENQ)): {Rune: 'e', Mod: ModCtrl},
  51. string(byte(ansi.ACK)): {Rune: 'f', Mod: ModCtrl},
  52. string(byte(ansi.BEL)): {Rune: 'g', Mod: ModCtrl},
  53. string(byte(ansi.BS)): {Rune: 'h', Mod: ModCtrl},
  54. string(byte(ansi.HT)): tab,
  55. string(byte(ansi.LF)): {Rune: 'j', Mod: ModCtrl},
  56. string(byte(ansi.VT)): {Rune: 'k', Mod: ModCtrl},
  57. string(byte(ansi.FF)): {Rune: 'l', Mod: ModCtrl},
  58. string(byte(ansi.CR)): enter,
  59. string(byte(ansi.SO)): {Rune: 'n', Mod: ModCtrl},
  60. string(byte(ansi.SI)): {Rune: 'o', Mod: ModCtrl},
  61. string(byte(ansi.DLE)): {Rune: 'p', Mod: ModCtrl},
  62. string(byte(ansi.DC1)): {Rune: 'q', Mod: ModCtrl},
  63. string(byte(ansi.DC2)): {Rune: 'r', Mod: ModCtrl},
  64. string(byte(ansi.DC3)): {Rune: 's', Mod: ModCtrl},
  65. string(byte(ansi.DC4)): {Rune: 't', Mod: ModCtrl},
  66. string(byte(ansi.NAK)): {Rune: 'u', Mod: ModCtrl},
  67. string(byte(ansi.SYN)): {Rune: 'v', Mod: ModCtrl},
  68. string(byte(ansi.ETB)): {Rune: 'w', Mod: ModCtrl},
  69. string(byte(ansi.CAN)): {Rune: 'x', Mod: ModCtrl},
  70. string(byte(ansi.EM)): {Rune: 'y', Mod: ModCtrl},
  71. string(byte(ansi.SUB)): {Rune: 'z', Mod: ModCtrl},
  72. string(byte(ansi.ESC)): esc,
  73. string(byte(ansi.FS)): {Rune: '\\', Mod: ModCtrl},
  74. string(byte(ansi.GS)): {Rune: ']', Mod: ModCtrl},
  75. string(byte(ansi.RS)): {Rune: '^', Mod: ModCtrl},
  76. string(byte(ansi.US)): {Rune: '_', Mod: ModCtrl},
  77. // Special keys in G0
  78. string(byte(ansi.SP)): {Sym: KeySpace, Rune: ' '},
  79. string(byte(ansi.DEL)): del,
  80. // Special keys
  81. "\x1b[Z": {Sym: KeyTab, Mod: ModShift},
  82. "\x1b[1~": find,
  83. "\x1b[2~": {Sym: KeyInsert},
  84. "\x1b[3~": {Sym: KeyDelete},
  85. "\x1b[4~": sel,
  86. "\x1b[5~": {Sym: KeyPgUp},
  87. "\x1b[6~": {Sym: KeyPgDown},
  88. "\x1b[7~": {Sym: KeyHome},
  89. "\x1b[8~": {Sym: KeyEnd},
  90. // Normal mode
  91. "\x1b[A": {Sym: KeyUp},
  92. "\x1b[B": {Sym: KeyDown},
  93. "\x1b[C": {Sym: KeyRight},
  94. "\x1b[D": {Sym: KeyLeft},
  95. "\x1b[E": {Sym: KeyBegin},
  96. "\x1b[F": {Sym: KeyEnd},
  97. "\x1b[H": {Sym: KeyHome},
  98. "\x1b[P": {Sym: KeyF1},
  99. "\x1b[Q": {Sym: KeyF2},
  100. "\x1b[R": {Sym: KeyF3},
  101. "\x1b[S": {Sym: KeyF4},
  102. // Application Cursor Key Mode (DECCKM)
  103. "\x1bOA": {Sym: KeyUp},
  104. "\x1bOB": {Sym: KeyDown},
  105. "\x1bOC": {Sym: KeyRight},
  106. "\x1bOD": {Sym: KeyLeft},
  107. "\x1bOE": {Sym: KeyBegin},
  108. "\x1bOF": {Sym: KeyEnd},
  109. "\x1bOH": {Sym: KeyHome},
  110. "\x1bOP": {Sym: KeyF1},
  111. "\x1bOQ": {Sym: KeyF2},
  112. "\x1bOR": {Sym: KeyF3},
  113. "\x1bOS": {Sym: KeyF4},
  114. // Keypad Application Mode (DECKPAM)
  115. "\x1bOM": {Sym: KeyKpEnter},
  116. "\x1bOX": {Sym: KeyKpEqual},
  117. "\x1bOj": {Sym: KeyKpMultiply},
  118. "\x1bOk": {Sym: KeyKpPlus},
  119. "\x1bOl": {Sym: KeyKpComma},
  120. "\x1bOm": {Sym: KeyKpMinus},
  121. "\x1bOn": {Sym: KeyKpDecimal},
  122. "\x1bOo": {Sym: KeyKpDivide},
  123. "\x1bOp": {Sym: KeyKp0},
  124. "\x1bOq": {Sym: KeyKp1},
  125. "\x1bOr": {Sym: KeyKp2},
  126. "\x1bOs": {Sym: KeyKp3},
  127. "\x1bOt": {Sym: KeyKp4},
  128. "\x1bOu": {Sym: KeyKp5},
  129. "\x1bOv": {Sym: KeyKp6},
  130. "\x1bOw": {Sym: KeyKp7},
  131. "\x1bOx": {Sym: KeyKp8},
  132. "\x1bOy": {Sym: KeyKp9},
  133. // Function keys
  134. "\x1b[11~": {Sym: KeyF1},
  135. "\x1b[12~": {Sym: KeyF2},
  136. "\x1b[13~": {Sym: KeyF3},
  137. "\x1b[14~": {Sym: KeyF4},
  138. "\x1b[15~": {Sym: KeyF5},
  139. "\x1b[17~": {Sym: KeyF6},
  140. "\x1b[18~": {Sym: KeyF7},
  141. "\x1b[19~": {Sym: KeyF8},
  142. "\x1b[20~": {Sym: KeyF9},
  143. "\x1b[21~": {Sym: KeyF10},
  144. "\x1b[23~": {Sym: KeyF11},
  145. "\x1b[24~": {Sym: KeyF12},
  146. "\x1b[25~": {Sym: KeyF13},
  147. "\x1b[26~": {Sym: KeyF14},
  148. "\x1b[28~": {Sym: KeyF15},
  149. "\x1b[29~": {Sym: KeyF16},
  150. "\x1b[31~": {Sym: KeyF17},
  151. "\x1b[32~": {Sym: KeyF18},
  152. "\x1b[33~": {Sym: KeyF19},
  153. "\x1b[34~": {Sym: KeyF20},
  154. }
  155. // CSI ~ sequence keys
  156. csiTildeKeys := map[string]Key{
  157. "1": find, "2": {Sym: KeyInsert},
  158. "3": {Sym: KeyDelete}, "4": sel,
  159. "5": {Sym: KeyPgUp}, "6": {Sym: KeyPgDown},
  160. "7": {Sym: KeyHome}, "8": {Sym: KeyEnd},
  161. // There are no 9 and 10 keys
  162. "11": {Sym: KeyF1}, "12": {Sym: KeyF2},
  163. "13": {Sym: KeyF3}, "14": {Sym: KeyF4},
  164. "15": {Sym: KeyF5}, "17": {Sym: KeyF6},
  165. "18": {Sym: KeyF7}, "19": {Sym: KeyF8},
  166. "20": {Sym: KeyF9}, "21": {Sym: KeyF10},
  167. "23": {Sym: KeyF11}, "24": {Sym: KeyF12},
  168. "25": {Sym: KeyF13}, "26": {Sym: KeyF14},
  169. "28": {Sym: KeyF15}, "29": {Sym: KeyF16},
  170. "31": {Sym: KeyF17}, "32": {Sym: KeyF18},
  171. "33": {Sym: KeyF19}, "34": {Sym: KeyF20},
  172. }
  173. // URxvt keys
  174. // See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
  175. table["\x1b[a"] = Key{Sym: KeyUp, Mod: ModShift}
  176. table["\x1b[b"] = Key{Sym: KeyDown, Mod: ModShift}
  177. table["\x1b[c"] = Key{Sym: KeyRight, Mod: ModShift}
  178. table["\x1b[d"] = Key{Sym: KeyLeft, Mod: ModShift}
  179. table["\x1bOa"] = Key{Sym: KeyUp, Mod: ModCtrl}
  180. table["\x1bOb"] = Key{Sym: KeyDown, Mod: ModCtrl}
  181. table["\x1bOc"] = Key{Sym: KeyRight, Mod: ModCtrl}
  182. table["\x1bOd"] = Key{Sym: KeyLeft, Mod: ModCtrl}
  183. // TODO: invistigate if shift-ctrl arrow keys collide with DECCKM keys i.e.
  184. // "\x1bOA", "\x1bOB", "\x1bOC", "\x1bOD"
  185. // URxvt modifier CSI ~ keys
  186. for k, v := range csiTildeKeys {
  187. key := v
  188. // Normal (no modifier) already defined part of VT100/VT200
  189. // Shift modifier
  190. key.Mod = ModShift
  191. table["\x1b["+k+"$"] = key
  192. // Ctrl modifier
  193. key.Mod = ModCtrl
  194. table["\x1b["+k+"^"] = key
  195. // Shift-Ctrl modifier
  196. key.Mod = ModShift | ModCtrl
  197. table["\x1b["+k+"@"] = key
  198. }
  199. // URxvt F keys
  200. // Note: Shift + F1-F10 generates F11-F20.
  201. // This means Shift + F1 and Shift + F2 will generate F11 and F12, the same
  202. // applies to Ctrl + Shift F1 & F2.
  203. //
  204. // P.S. Don't like this? Blame URxvt, configure your terminal to use
  205. // different escapes like XTerm, or switch to a better terminal ¯\_(ツ)_/¯
  206. //
  207. // See https://manpages.ubuntu.com/manpages/trusty/man7/urxvt.7.html#key%20codes
  208. table["\x1b[23$"] = Key{Sym: KeyF11, Mod: ModShift}
  209. table["\x1b[24$"] = Key{Sym: KeyF12, Mod: ModShift}
  210. table["\x1b[25$"] = Key{Sym: KeyF13, Mod: ModShift}
  211. table["\x1b[26$"] = Key{Sym: KeyF14, Mod: ModShift}
  212. table["\x1b[28$"] = Key{Sym: KeyF15, Mod: ModShift}
  213. table["\x1b[29$"] = Key{Sym: KeyF16, Mod: ModShift}
  214. table["\x1b[31$"] = Key{Sym: KeyF17, Mod: ModShift}
  215. table["\x1b[32$"] = Key{Sym: KeyF18, Mod: ModShift}
  216. table["\x1b[33$"] = Key{Sym: KeyF19, Mod: ModShift}
  217. table["\x1b[34$"] = Key{Sym: KeyF20, Mod: ModShift}
  218. table["\x1b[11^"] = Key{Sym: KeyF1, Mod: ModCtrl}
  219. table["\x1b[12^"] = Key{Sym: KeyF2, Mod: ModCtrl}
  220. table["\x1b[13^"] = Key{Sym: KeyF3, Mod: ModCtrl}
  221. table["\x1b[14^"] = Key{Sym: KeyF4, Mod: ModCtrl}
  222. table["\x1b[15^"] = Key{Sym: KeyF5, Mod: ModCtrl}
  223. table["\x1b[17^"] = Key{Sym: KeyF6, Mod: ModCtrl}
  224. table["\x1b[18^"] = Key{Sym: KeyF7, Mod: ModCtrl}
  225. table["\x1b[19^"] = Key{Sym: KeyF8, Mod: ModCtrl}
  226. table["\x1b[20^"] = Key{Sym: KeyF9, Mod: ModCtrl}
  227. table["\x1b[21^"] = Key{Sym: KeyF10, Mod: ModCtrl}
  228. table["\x1b[23^"] = Key{Sym: KeyF11, Mod: ModCtrl}
  229. table["\x1b[24^"] = Key{Sym: KeyF12, Mod: ModCtrl}
  230. table["\x1b[25^"] = Key{Sym: KeyF13, Mod: ModCtrl}
  231. table["\x1b[26^"] = Key{Sym: KeyF14, Mod: ModCtrl}
  232. table["\x1b[28^"] = Key{Sym: KeyF15, Mod: ModCtrl}
  233. table["\x1b[29^"] = Key{Sym: KeyF16, Mod: ModCtrl}
  234. table["\x1b[31^"] = Key{Sym: KeyF17, Mod: ModCtrl}
  235. table["\x1b[32^"] = Key{Sym: KeyF18, Mod: ModCtrl}
  236. table["\x1b[33^"] = Key{Sym: KeyF19, Mod: ModCtrl}
  237. table["\x1b[34^"] = Key{Sym: KeyF20, Mod: ModCtrl}
  238. table["\x1b[23@"] = Key{Sym: KeyF11, Mod: ModShift | ModCtrl}
  239. table["\x1b[24@"] = Key{Sym: KeyF12, Mod: ModShift | ModCtrl}
  240. table["\x1b[25@"] = Key{Sym: KeyF13, Mod: ModShift | ModCtrl}
  241. table["\x1b[26@"] = Key{Sym: KeyF14, Mod: ModShift | ModCtrl}
  242. table["\x1b[28@"] = Key{Sym: KeyF15, Mod: ModShift | ModCtrl}
  243. table["\x1b[29@"] = Key{Sym: KeyF16, Mod: ModShift | ModCtrl}
  244. table["\x1b[31@"] = Key{Sym: KeyF17, Mod: ModShift | ModCtrl}
  245. table["\x1b[32@"] = Key{Sym: KeyF18, Mod: ModShift | ModCtrl}
  246. table["\x1b[33@"] = Key{Sym: KeyF19, Mod: ModShift | ModCtrl}
  247. table["\x1b[34@"] = Key{Sym: KeyF20, Mod: ModShift | ModCtrl}
  248. // Register Alt + <key> combinations
  249. // XXX: this must come after URxvt but before XTerm keys to register URxvt
  250. // keys with alt modifier
  251. tmap := map[string]Key{}
  252. for seq, key := range table {
  253. key := key
  254. key.Mod |= ModAlt
  255. tmap["\x1b"+seq] = key
  256. }
  257. for seq, key := range tmap {
  258. table[seq] = key
  259. }
  260. // XTerm modifiers
  261. // These are offset by 1 to be compatible with our Mod type.
  262. // See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
  263. modifiers := []KeyMod{
  264. ModShift, // 1
  265. ModAlt, // 2
  266. ModShift | ModAlt, // 3
  267. ModCtrl, // 4
  268. ModShift | ModCtrl, // 5
  269. ModAlt | ModCtrl, // 6
  270. ModShift | ModAlt | ModCtrl, // 7
  271. ModMeta, // 8
  272. ModMeta | ModShift, // 9
  273. ModMeta | ModAlt, // 10
  274. ModMeta | ModShift | ModAlt, // 11
  275. ModMeta | ModCtrl, // 12
  276. ModMeta | ModShift | ModCtrl, // 13
  277. ModMeta | ModAlt | ModCtrl, // 14
  278. ModMeta | ModShift | ModAlt | ModCtrl, // 15
  279. }
  280. // SS3 keypad function keys
  281. ss3FuncKeys := map[string]Key{
  282. // These are defined in XTerm
  283. // Taken from Foot keymap.h and XTerm modifyOtherKeys
  284. // https://codeberg.org/dnkl/foot/src/branch/master/keymap.h
  285. "M": {Sym: KeyKpEnter}, "X": {Sym: KeyKpEqual},
  286. "j": {Sym: KeyKpMultiply}, "k": {Sym: KeyKpPlus},
  287. "l": {Sym: KeyKpComma}, "m": {Sym: KeyKpMinus},
  288. "n": {Sym: KeyKpDecimal}, "o": {Sym: KeyKpDivide},
  289. "p": {Sym: KeyKp0}, "q": {Sym: KeyKp1},
  290. "r": {Sym: KeyKp2}, "s": {Sym: KeyKp3},
  291. "t": {Sym: KeyKp4}, "u": {Sym: KeyKp5},
  292. "v": {Sym: KeyKp6}, "w": {Sym: KeyKp7},
  293. "x": {Sym: KeyKp8}, "y": {Sym: KeyKp9},
  294. }
  295. // XTerm keys
  296. csiFuncKeys := map[string]Key{
  297. "A": {Sym: KeyUp}, "B": {Sym: KeyDown},
  298. "C": {Sym: KeyRight}, "D": {Sym: KeyLeft},
  299. "E": {Sym: KeyBegin}, "F": {Sym: KeyEnd},
  300. "H": {Sym: KeyHome}, "P": {Sym: KeyF1},
  301. "Q": {Sym: KeyF2}, "R": {Sym: KeyF3},
  302. "S": {Sym: KeyF4},
  303. }
  304. // CSI 27 ; <modifier> ; <code> ~ keys defined in XTerm modifyOtherKeys
  305. modifyOtherKeys := map[int]Key{
  306. ansi.BS: {Sym: KeyBackspace},
  307. ansi.HT: {Sym: KeyTab},
  308. ansi.CR: {Sym: KeyEnter},
  309. ansi.ESC: {Sym: KeyEscape},
  310. ansi.DEL: {Sym: KeyBackspace},
  311. }
  312. for _, m := range modifiers {
  313. // XTerm modifier offset +1
  314. xtermMod := strconv.Itoa(int(m) + 1)
  315. // CSI 1 ; <modifier> <func>
  316. for k, v := range csiFuncKeys {
  317. // Functions always have a leading 1 param
  318. seq := "\x1b[1;" + xtermMod + k
  319. key := v
  320. key.Mod = m
  321. table[seq] = key
  322. }
  323. // SS3 <modifier> <func>
  324. for k, v := range ss3FuncKeys {
  325. seq := "\x1bO" + xtermMod + k
  326. key := v
  327. key.Mod = m
  328. table[seq] = key
  329. }
  330. // CSI <number> ; <modifier> ~
  331. for k, v := range csiTildeKeys {
  332. seq := "\x1b[" + k + ";" + xtermMod + "~"
  333. key := v
  334. key.Mod = m
  335. table[seq] = key
  336. }
  337. // CSI 27 ; <modifier> ; <code> ~
  338. for k, v := range modifyOtherKeys {
  339. code := strconv.Itoa(k)
  340. seq := "\x1b[27;" + xtermMod + ";" + code + "~"
  341. key := v
  342. key.Mod = m
  343. table[seq] = key
  344. }
  345. }
  346. // Register terminfo keys
  347. // XXX: this might override keys already registered in table
  348. if flags&FlagTerminfo != 0 {
  349. titable := buildTerminfoKeys(flags, term)
  350. for seq, key := range titable {
  351. table[seq] = key
  352. }
  353. }
  354. return table
  355. }