driver_windows.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. //go:build windows
  2. // +build windows
  3. package input
  4. import (
  5. "errors"
  6. "fmt"
  7. "unicode/utf16"
  8. "github.com/charmbracelet/x/ansi"
  9. termwindows "github.com/charmbracelet/x/windows"
  10. "github.com/erikgeiser/coninput"
  11. "golang.org/x/sys/windows"
  12. )
  13. // ReadEvents reads input events from the terminal.
  14. //
  15. // It reads the events available in the input buffer and returns them.
  16. func (d *Driver) ReadEvents() ([]Event, error) {
  17. events, err := d.handleConInput(coninput.ReadConsoleInput)
  18. if errors.Is(err, errNotConInputReader) {
  19. return d.readEvents()
  20. }
  21. return events, err
  22. }
  23. var errNotConInputReader = fmt.Errorf("handleConInput: not a conInputReader")
  24. func (d *Driver) handleConInput(
  25. finput func(windows.Handle, []coninput.InputRecord) (uint32, error),
  26. ) ([]Event, error) {
  27. cc, ok := d.rd.(*conInputReader)
  28. if !ok {
  29. return nil, errNotConInputReader
  30. }
  31. // read up to 256 events, this is to allow for sequences events reported as
  32. // key events.
  33. var events [256]coninput.InputRecord
  34. _, err := finput(cc.conin, events[:])
  35. if err != nil {
  36. return nil, fmt.Errorf("read coninput events: %w", err)
  37. }
  38. var evs []Event
  39. for _, event := range events {
  40. if e := parseConInputEvent(event, &d.prevMouseState, &d.lastWinsizeEvent); e != nil {
  41. evs = append(evs, e)
  42. }
  43. }
  44. return d.detectConInputQuerySequences(evs), nil
  45. }
  46. // Using ConInput API, Windows Terminal responds to sequence query events with
  47. // KEY_EVENT_RECORDs so we need to collect them and parse them as a single
  48. // sequence.
  49. // Is this a hack?
  50. func (d *Driver) detectConInputQuerySequences(events []Event) []Event {
  51. var newEvents []Event
  52. start, end := -1, -1
  53. loop:
  54. for i, e := range events {
  55. switch e := e.(type) {
  56. case KeyPressEvent:
  57. switch e.Rune {
  58. case ansi.ESC, ansi.CSI, ansi.OSC, ansi.DCS, ansi.APC:
  59. // start of a sequence
  60. if start == -1 {
  61. start = i
  62. }
  63. }
  64. default:
  65. break loop
  66. }
  67. end = i
  68. }
  69. if start == -1 || end <= start {
  70. return events
  71. }
  72. var seq []byte
  73. for i := start; i <= end; i++ {
  74. switch e := events[i].(type) {
  75. case KeyPressEvent:
  76. seq = append(seq, byte(e.Rune))
  77. }
  78. }
  79. n, seqevent := ParseSequence(seq)
  80. switch seqevent.(type) {
  81. case UnknownEvent:
  82. // We're not interested in unknown events
  83. default:
  84. if start+n > len(events) {
  85. return events
  86. }
  87. newEvents = events[:start]
  88. newEvents = append(newEvents, seqevent)
  89. newEvents = append(newEvents, events[start+n:]...)
  90. return d.detectConInputQuerySequences(newEvents)
  91. }
  92. return events
  93. }
  94. func parseConInputEvent(event coninput.InputRecord, ps *coninput.ButtonState, ws *coninput.WindowBufferSizeEventRecord) Event {
  95. switch e := event.Unwrap().(type) {
  96. case coninput.KeyEventRecord:
  97. event := parseWin32InputKeyEvent(e.VirtualKeyCode, e.VirtualScanCode,
  98. e.Char, e.KeyDown, e.ControlKeyState, e.RepeatCount)
  99. var key Key
  100. switch event := event.(type) {
  101. case KeyPressEvent:
  102. key = Key(event)
  103. case KeyReleaseEvent:
  104. key = Key(event)
  105. default:
  106. return nil
  107. }
  108. // If the key is not printable, return the event as is
  109. // (e.g. function keys, arrows, etc.)
  110. // Otherwise, try to translate it to a rune based on the active keyboard
  111. // layout.
  112. if key.Rune == 0 {
  113. return event
  114. }
  115. // Always use US layout for translation
  116. // This is to follow the behavior of the Kitty Keyboard base layout
  117. // feature :eye_roll:
  118. // https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values?view=windows-11
  119. const usLayout = 0x409
  120. // Translate key to rune
  121. var keyState [256]byte
  122. var utf16Buf [16]uint16
  123. const dontChangeKernelKeyboardLayout = 0x4
  124. ret := termwindows.ToUnicodeEx(
  125. uint32(e.VirtualKeyCode),
  126. uint32(e.VirtualScanCode),
  127. &keyState[0],
  128. &utf16Buf[0],
  129. int32(len(utf16Buf)),
  130. dontChangeKernelKeyboardLayout,
  131. usLayout,
  132. )
  133. // -1 indicates a dead key
  134. // 0 indicates no translation for this key
  135. if ret < 1 {
  136. return event
  137. }
  138. runes := utf16.Decode(utf16Buf[:ret])
  139. if len(runes) != 1 {
  140. // Key doesn't translate to a single rune
  141. return event
  142. }
  143. key.baseRune = runes[0]
  144. if e.KeyDown {
  145. return KeyPressEvent(key)
  146. }
  147. return KeyReleaseEvent(key)
  148. case coninput.WindowBufferSizeEventRecord:
  149. if e != *ws {
  150. *ws = e
  151. return WindowSizeEvent{
  152. Width: int(e.Size.X),
  153. Height: int(e.Size.Y),
  154. }
  155. }
  156. case coninput.MouseEventRecord:
  157. mevent := mouseEvent(*ps, e)
  158. *ps = e.ButtonState
  159. return mevent
  160. case coninput.FocusEventRecord, coninput.MenuEventRecord:
  161. // ignore
  162. }
  163. return nil
  164. }
  165. func mouseEventButton(p, s coninput.ButtonState) (button MouseButton, isRelease bool) {
  166. btn := p ^ s
  167. if btn&s == 0 {
  168. isRelease = true
  169. }
  170. if btn == 0 {
  171. switch {
  172. case s&coninput.FROM_LEFT_1ST_BUTTON_PRESSED > 0:
  173. button = MouseLeft
  174. case s&coninput.FROM_LEFT_2ND_BUTTON_PRESSED > 0:
  175. button = MouseMiddle
  176. case s&coninput.RIGHTMOST_BUTTON_PRESSED > 0:
  177. button = MouseRight
  178. case s&coninput.FROM_LEFT_3RD_BUTTON_PRESSED > 0:
  179. button = MouseBackward
  180. case s&coninput.FROM_LEFT_4TH_BUTTON_PRESSED > 0:
  181. button = MouseForward
  182. }
  183. return
  184. }
  185. switch btn {
  186. case coninput.FROM_LEFT_1ST_BUTTON_PRESSED: // left button
  187. button = MouseLeft
  188. case coninput.RIGHTMOST_BUTTON_PRESSED: // right button
  189. button = MouseRight
  190. case coninput.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button
  191. button = MouseMiddle
  192. case coninput.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward)
  193. button = MouseBackward
  194. case coninput.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward)
  195. button = MouseForward
  196. }
  197. return
  198. }
  199. func mouseEvent(p coninput.ButtonState, e coninput.MouseEventRecord) (ev Event) {
  200. var mod KeyMod
  201. var isRelease bool
  202. if e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED) {
  203. mod |= ModAlt
  204. }
  205. if e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED) {
  206. mod |= ModCtrl
  207. }
  208. if e.ControlKeyState.Contains(coninput.SHIFT_PRESSED) {
  209. mod |= ModShift
  210. }
  211. m := Mouse{
  212. X: int(e.MousePositon.X),
  213. Y: int(e.MousePositon.Y),
  214. Mod: mod,
  215. }
  216. switch e.EventFlags {
  217. case coninput.CLICK, coninput.DOUBLE_CLICK:
  218. m.Button, isRelease = mouseEventButton(p, e.ButtonState)
  219. case coninput.MOUSE_WHEELED:
  220. if e.WheelDirection > 0 {
  221. m.Button = MouseWheelUp
  222. } else {
  223. m.Button = MouseWheelDown
  224. }
  225. case coninput.MOUSE_HWHEELED:
  226. if e.WheelDirection > 0 {
  227. m.Button = MouseWheelRight
  228. } else {
  229. m.Button = MouseWheelLeft
  230. }
  231. case coninput.MOUSE_MOVED:
  232. m.Button, _ = mouseEventButton(p, e.ButtonState)
  233. return MouseMotionEvent(m)
  234. }
  235. if isWheel(m.Button) {
  236. return MouseWheelEvent(m)
  237. } else if isRelease {
  238. return MouseReleaseEvent(m)
  239. }
  240. return MouseClickEvent(m)
  241. }