kitty.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. package input
  2. import (
  3. "unicode"
  4. "unicode/utf8"
  5. "github.com/charmbracelet/x/ansi"
  6. )
  7. // KittyKeyboardEvent represents Kitty keyboard progressive enhancement flags.
  8. type KittyKeyboardEvent int
  9. // IsDisambiguateEscapeCodes returns true if the DisambiguateEscapeCodes flag is set.
  10. func (e KittyKeyboardEvent) IsDisambiguateEscapeCodes() bool {
  11. return e&ansi.KittyDisambiguateEscapeCodes != 0
  12. }
  13. // IsReportEventTypes returns true if the ReportEventTypes flag is set.
  14. func (e KittyKeyboardEvent) IsReportEventTypes() bool {
  15. return e&ansi.KittyReportEventTypes != 0
  16. }
  17. // IsReportAlternateKeys returns true if the ReportAlternateKeys flag is set.
  18. func (e KittyKeyboardEvent) IsReportAlternateKeys() bool {
  19. return e&ansi.KittyReportAlternateKeys != 0
  20. }
  21. // IsReportAllKeys returns true if the ReportAllKeys flag is set.
  22. func (e KittyKeyboardEvent) IsReportAllKeys() bool {
  23. return e&ansi.KittyReportAllKeys != 0
  24. }
  25. // IsReportAssociatedKeys returns true if the ReportAssociatedKeys flag is set.
  26. func (e KittyKeyboardEvent) IsReportAssociatedKeys() bool {
  27. return e&ansi.KittyReportAssociatedKeys != 0
  28. }
  29. // Kitty Clipboard Control Sequences
  30. var kittyKeyMap = map[int]KeySym{
  31. ansi.BS: KeyBackspace,
  32. ansi.HT: KeyTab,
  33. ansi.CR: KeyEnter,
  34. ansi.ESC: KeyEscape,
  35. ansi.DEL: KeyBackspace,
  36. 57344: KeyEscape,
  37. 57345: KeyEnter,
  38. 57346: KeyTab,
  39. 57347: KeyBackspace,
  40. 57348: KeyInsert,
  41. 57349: KeyDelete,
  42. 57350: KeyLeft,
  43. 57351: KeyRight,
  44. 57352: KeyUp,
  45. 57353: KeyDown,
  46. 57354: KeyPgUp,
  47. 57355: KeyPgDown,
  48. 57356: KeyHome,
  49. 57357: KeyEnd,
  50. 57358: KeyCapsLock,
  51. 57359: KeyScrollLock,
  52. 57360: KeyNumLock,
  53. 57361: KeyPrintScreen,
  54. 57362: KeyPause,
  55. 57363: KeyMenu,
  56. 57364: KeyF1,
  57. 57365: KeyF2,
  58. 57366: KeyF3,
  59. 57367: KeyF4,
  60. 57368: KeyF5,
  61. 57369: KeyF6,
  62. 57370: KeyF7,
  63. 57371: KeyF8,
  64. 57372: KeyF9,
  65. 57373: KeyF10,
  66. 57374: KeyF11,
  67. 57375: KeyF12,
  68. 57376: KeyF13,
  69. 57377: KeyF14,
  70. 57378: KeyF15,
  71. 57379: KeyF16,
  72. 57380: KeyF17,
  73. 57381: KeyF18,
  74. 57382: KeyF19,
  75. 57383: KeyF20,
  76. 57384: KeyF21,
  77. 57385: KeyF22,
  78. 57386: KeyF23,
  79. 57387: KeyF24,
  80. 57388: KeyF25,
  81. 57389: KeyF26,
  82. 57390: KeyF27,
  83. 57391: KeyF28,
  84. 57392: KeyF29,
  85. 57393: KeyF30,
  86. 57394: KeyF31,
  87. 57395: KeyF32,
  88. 57396: KeyF33,
  89. 57397: KeyF34,
  90. 57398: KeyF35,
  91. 57399: KeyKp0,
  92. 57400: KeyKp1,
  93. 57401: KeyKp2,
  94. 57402: KeyKp3,
  95. 57403: KeyKp4,
  96. 57404: KeyKp5,
  97. 57405: KeyKp6,
  98. 57406: KeyKp7,
  99. 57407: KeyKp8,
  100. 57408: KeyKp9,
  101. 57409: KeyKpDecimal,
  102. 57410: KeyKpDivide,
  103. 57411: KeyKpMultiply,
  104. 57412: KeyKpMinus,
  105. 57413: KeyKpPlus,
  106. 57414: KeyKpEnter,
  107. 57415: KeyKpEqual,
  108. 57416: KeyKpSep,
  109. 57417: KeyKpLeft,
  110. 57418: KeyKpRight,
  111. 57419: KeyKpUp,
  112. 57420: KeyKpDown,
  113. 57421: KeyKpPgUp,
  114. 57422: KeyKpPgDown,
  115. 57423: KeyKpHome,
  116. 57424: KeyKpEnd,
  117. 57425: KeyKpInsert,
  118. 57426: KeyKpDelete,
  119. 57427: KeyKpBegin,
  120. 57428: KeyMediaPlay,
  121. 57429: KeyMediaPause,
  122. 57430: KeyMediaPlayPause,
  123. 57431: KeyMediaReverse,
  124. 57432: KeyMediaStop,
  125. 57433: KeyMediaFastForward,
  126. 57434: KeyMediaRewind,
  127. 57435: KeyMediaNext,
  128. 57436: KeyMediaPrev,
  129. 57437: KeyMediaRecord,
  130. 57438: KeyLowerVol,
  131. 57439: KeyRaiseVol,
  132. 57440: KeyMute,
  133. 57441: KeyLeftShift,
  134. 57442: KeyLeftCtrl,
  135. 57443: KeyLeftAlt,
  136. 57444: KeyLeftSuper,
  137. 57445: KeyLeftHyper,
  138. 57446: KeyLeftMeta,
  139. 57447: KeyRightShift,
  140. 57448: KeyRightCtrl,
  141. 57449: KeyRightAlt,
  142. 57450: KeyRightSuper,
  143. 57451: KeyRightHyper,
  144. 57452: KeyRightMeta,
  145. 57453: KeyIsoLevel3Shift,
  146. 57454: KeyIsoLevel5Shift,
  147. }
  148. const (
  149. kittyShift = 1 << iota
  150. kittyAlt
  151. kittyCtrl
  152. kittySuper
  153. kittyHyper
  154. kittyMeta
  155. kittyCapsLock
  156. kittyNumLock
  157. )
  158. func fromKittyMod(mod int) KeyMod {
  159. var m KeyMod
  160. if mod&kittyShift != 0 {
  161. m |= ModShift
  162. }
  163. if mod&kittyAlt != 0 {
  164. m |= ModAlt
  165. }
  166. if mod&kittyCtrl != 0 {
  167. m |= ModCtrl
  168. }
  169. if mod&kittySuper != 0 {
  170. m |= ModSuper
  171. }
  172. if mod&kittyHyper != 0 {
  173. m |= ModHyper
  174. }
  175. if mod&kittyMeta != 0 {
  176. m |= ModMeta
  177. }
  178. if mod&kittyCapsLock != 0 {
  179. m |= ModCapsLock
  180. }
  181. if mod&kittyNumLock != 0 {
  182. m |= ModNumLock
  183. }
  184. return m
  185. }
  186. // parseKittyKeyboard parses a Kitty Keyboard Protocol sequence.
  187. //
  188. // In `CSI u`, this is parsed as:
  189. //
  190. // CSI codepoint ; modifiers u
  191. // codepoint: ASCII Dec value
  192. //
  193. // The Kitty Keyboard Protocol extends this with optional components that can be
  194. // enabled progressively. The full sequence is parsed as:
  195. //
  196. // CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u
  197. //
  198. // See https://sw.kovidgoyal.net/kitty/keyboard-protocol/
  199. func parseKittyKeyboard(csi *ansi.CsiSequence) Event {
  200. var isRelease bool
  201. key := Key{}
  202. if params := csi.Subparams(0); len(params) > 0 {
  203. code := params[0]
  204. if sym, ok := kittyKeyMap[code]; ok {
  205. key.Sym = sym
  206. } else {
  207. r := rune(code)
  208. if !utf8.ValidRune(r) {
  209. r = utf8.RuneError
  210. }
  211. key.Rune = r
  212. // alternate key reporting
  213. switch len(params) {
  214. case 3:
  215. // shifted key + base key
  216. if b := rune(params[2]); unicode.IsPrint(b) {
  217. // XXX: When alternate key reporting is enabled, the protocol
  218. // can return 3 things, the unicode codepoint of the key,
  219. // the shifted codepoint of the key, and the standard
  220. // PC-101 key layout codepoint.
  221. // This is useful to create an unambiguous mapping of keys
  222. // when using a different language layout.
  223. key.baseRune = b
  224. }
  225. fallthrough
  226. case 2:
  227. // shifted key
  228. if s := rune(params[1]); unicode.IsPrint(s) {
  229. // XXX: We swap keys here because we want the shifted key
  230. // to be the Rune that is returned by the event.
  231. // For example, shift+a should produce "A" not "a".
  232. // In such a case, we set AltRune to the original key "a"
  233. // and Rune to "A".
  234. key.AltRune = key.Rune
  235. key.Rune = s
  236. }
  237. }
  238. }
  239. }
  240. if params := csi.Subparams(1); len(params) > 0 {
  241. mod := params[0]
  242. if mod > 1 {
  243. key.Mod = fromKittyMod(mod - 1)
  244. }
  245. if len(params) > 1 {
  246. switch params[1] {
  247. case 2:
  248. key.IsRepeat = true
  249. case 3:
  250. isRelease = true
  251. }
  252. }
  253. }
  254. // TODO: Associated keys are not support yet.
  255. // if params := csi.Subparams(2); len(params) > 0 {
  256. // r := rune(params[0])
  257. // if unicode.IsPrint(r) {
  258. // key.AltRune = r
  259. // }
  260. // }
  261. if isRelease {
  262. return KeyReleaseEvent(key)
  263. }
  264. return KeyPressEvent(key)
  265. }