driver_windows_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package input
  2. import (
  3. "encoding/binary"
  4. "image/color"
  5. "reflect"
  6. "testing"
  7. "unicode/utf16"
  8. "github.com/charmbracelet/x/ansi"
  9. xwindows "github.com/charmbracelet/x/windows"
  10. "golang.org/x/sys/windows"
  11. )
  12. func TestWindowsInputEvents(t *testing.T) {
  13. cases := []struct {
  14. name string
  15. events []xwindows.InputRecord
  16. expected []Event
  17. sequence bool // indicates that the input events are ANSI sequence or utf16
  18. }{
  19. {
  20. name: "single key event",
  21. events: []xwindows.InputRecord{
  22. encodeKeyEvent(xwindows.KeyEventRecord{
  23. KeyDown: true,
  24. Char: 'a',
  25. VirtualKeyCode: 'A',
  26. }),
  27. },
  28. expected: []Event{KeyPressEvent{Code: 'a', BaseCode: 'a', Text: "a"}},
  29. },
  30. {
  31. name: "single key event with control key",
  32. events: []xwindows.InputRecord{
  33. encodeKeyEvent(xwindows.KeyEventRecord{
  34. KeyDown: true,
  35. Char: 'a',
  36. VirtualKeyCode: 'A',
  37. ControlKeyState: xwindows.LEFT_CTRL_PRESSED,
  38. }),
  39. },
  40. expected: []Event{KeyPressEvent{Code: 'a', BaseCode: 'a', Mod: ModCtrl}},
  41. },
  42. {
  43. name: "escape alt key event",
  44. events: []xwindows.InputRecord{
  45. encodeKeyEvent(xwindows.KeyEventRecord{
  46. KeyDown: true,
  47. Char: ansi.ESC,
  48. VirtualKeyCode: ansi.ESC,
  49. ControlKeyState: xwindows.LEFT_ALT_PRESSED,
  50. }),
  51. },
  52. expected: []Event{KeyPressEvent{Code: ansi.ESC, BaseCode: ansi.ESC, Mod: ModAlt}},
  53. },
  54. {
  55. name: "single shifted key event",
  56. events: []xwindows.InputRecord{
  57. encodeKeyEvent(xwindows.KeyEventRecord{
  58. KeyDown: true,
  59. Char: 'A',
  60. VirtualKeyCode: 'A',
  61. ControlKeyState: xwindows.SHIFT_PRESSED,
  62. }),
  63. },
  64. expected: []Event{KeyPressEvent{Code: 'A', BaseCode: 'a', Text: "A", Mod: ModShift}},
  65. },
  66. {
  67. name: "utf16 rune",
  68. events: encodeUtf16Rune('😊'), // smiley emoji '😊'
  69. expected: []Event{
  70. KeyPressEvent{Code: '😊', Text: "😊"},
  71. },
  72. sequence: true,
  73. },
  74. {
  75. name: "background color response",
  76. events: encodeSequence("\x1b]11;rgb:ff/ff/ff\x07"),
  77. expected: []Event{BackgroundColorEvent{Color: color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}}},
  78. sequence: true,
  79. },
  80. {
  81. name: "st terminated background color response",
  82. events: encodeSequence("\x1b]11;rgb:ffff/ffff/ffff\x1b\\"),
  83. expected: []Event{BackgroundColorEvent{Color: color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}}},
  84. sequence: true,
  85. },
  86. {
  87. name: "simple mouse event",
  88. events: []xwindows.InputRecord{
  89. encodeMouseEvent(xwindows.MouseEventRecord{
  90. MousePositon: windows.Coord{X: 10, Y: 20},
  91. ButtonState: xwindows.FROM_LEFT_1ST_BUTTON_PRESSED,
  92. EventFlags: 0,
  93. }),
  94. encodeMouseEvent(xwindows.MouseEventRecord{
  95. MousePositon: windows.Coord{X: 10, Y: 20},
  96. EventFlags: 0,
  97. }),
  98. },
  99. expected: []Event{
  100. MouseClickEvent{Button: MouseLeft, X: 10, Y: 20},
  101. MouseReleaseEvent{Button: MouseLeft, X: 10, Y: 20},
  102. },
  103. },
  104. {
  105. name: "focus event",
  106. events: []xwindows.InputRecord{
  107. encodeFocusEvent(xwindows.FocusEventRecord{
  108. SetFocus: true,
  109. }),
  110. encodeFocusEvent(xwindows.FocusEventRecord{
  111. SetFocus: false,
  112. }),
  113. },
  114. expected: []Event{
  115. FocusEvent{},
  116. BlurEvent{},
  117. },
  118. },
  119. {
  120. name: "window size event",
  121. events: []xwindows.InputRecord{
  122. encodeWindowBufferSizeEvent(xwindows.WindowBufferSizeRecord{
  123. Size: windows.Coord{X: 10, Y: 20},
  124. }),
  125. },
  126. expected: []Event{
  127. WindowSizeEvent{Width: 10, Height: 20},
  128. },
  129. },
  130. }
  131. // p is the parser to parse the input events
  132. var p Parser
  133. // keep track of the state of the driver to handle ANSI sequences and utf16
  134. var state win32InputState
  135. for _, tc := range cases {
  136. t.Run(tc.name, func(t *testing.T) {
  137. if tc.sequence {
  138. var Event Event
  139. for _, ev := range tc.events {
  140. if ev.EventType != xwindows.KEY_EVENT {
  141. t.Fatalf("expected key event, got %v", ev.EventType)
  142. }
  143. key := ev.KeyEvent()
  144. Event = p.parseWin32InputKeyEvent(&state, key.VirtualKeyCode, key.VirtualScanCode, key.Char, key.KeyDown, key.ControlKeyState, key.RepeatCount)
  145. }
  146. if len(tc.expected) != 1 {
  147. t.Fatalf("expected 1 event, got %d", len(tc.expected))
  148. }
  149. if !reflect.DeepEqual(Event, tc.expected[0]) {
  150. t.Errorf("expected %v, got %v", tc.expected[0], Event)
  151. }
  152. } else {
  153. if len(tc.events) != len(tc.expected) {
  154. t.Fatalf("expected %d events, got %d", len(tc.expected), len(tc.events))
  155. }
  156. for j, ev := range tc.events {
  157. Event := p.parseConInputEvent(ev, &state)
  158. if !reflect.DeepEqual(Event, tc.expected[j]) {
  159. t.Errorf("expected %#v, got %#v", tc.expected[j], Event)
  160. }
  161. }
  162. }
  163. })
  164. }
  165. }
  166. func boolToUint32(b bool) uint32 {
  167. if b {
  168. return 1
  169. }
  170. return 0
  171. }
  172. func encodeMenuEvent(menu xwindows.MenuEventRecord) xwindows.InputRecord {
  173. var bts [16]byte
  174. binary.LittleEndian.PutUint32(bts[0:4], menu.CommandID)
  175. return xwindows.InputRecord{
  176. EventType: xwindows.MENU_EVENT,
  177. Event: bts,
  178. }
  179. }
  180. func encodeWindowBufferSizeEvent(size xwindows.WindowBufferSizeRecord) xwindows.InputRecord {
  181. var bts [16]byte
  182. binary.LittleEndian.PutUint16(bts[0:2], uint16(size.Size.X))
  183. binary.LittleEndian.PutUint16(bts[2:4], uint16(size.Size.Y))
  184. return xwindows.InputRecord{
  185. EventType: xwindows.WINDOW_BUFFER_SIZE_EVENT,
  186. Event: bts,
  187. }
  188. }
  189. func encodeFocusEvent(focus xwindows.FocusEventRecord) xwindows.InputRecord {
  190. var bts [16]byte
  191. if focus.SetFocus {
  192. bts[0] = 1
  193. }
  194. return xwindows.InputRecord{
  195. EventType: xwindows.FOCUS_EVENT,
  196. Event: bts,
  197. }
  198. }
  199. func encodeMouseEvent(mouse xwindows.MouseEventRecord) xwindows.InputRecord {
  200. var bts [16]byte
  201. binary.LittleEndian.PutUint16(bts[0:2], uint16(mouse.MousePositon.X))
  202. binary.LittleEndian.PutUint16(bts[2:4], uint16(mouse.MousePositon.Y))
  203. binary.LittleEndian.PutUint32(bts[4:8], mouse.ButtonState)
  204. binary.LittleEndian.PutUint32(bts[8:12], mouse.ControlKeyState)
  205. binary.LittleEndian.PutUint32(bts[12:16], mouse.EventFlags)
  206. return xwindows.InputRecord{
  207. EventType: xwindows.MOUSE_EVENT,
  208. Event: bts,
  209. }
  210. }
  211. func encodeKeyEvent(key xwindows.KeyEventRecord) xwindows.InputRecord {
  212. var bts [16]byte
  213. binary.LittleEndian.PutUint32(bts[0:4], boolToUint32(key.KeyDown))
  214. binary.LittleEndian.PutUint16(bts[4:6], key.RepeatCount)
  215. binary.LittleEndian.PutUint16(bts[6:8], key.VirtualKeyCode)
  216. binary.LittleEndian.PutUint16(bts[8:10], key.VirtualScanCode)
  217. binary.LittleEndian.PutUint16(bts[10:12], uint16(key.Char))
  218. binary.LittleEndian.PutUint32(bts[12:16], key.ControlKeyState)
  219. return xwindows.InputRecord{
  220. EventType: xwindows.KEY_EVENT,
  221. Event: bts,
  222. }
  223. }
  224. // encodeSequence encodes a string of ANSI escape sequences into a slice of
  225. // Windows input key records.
  226. func encodeSequence(s string) (evs []xwindows.InputRecord) {
  227. var state byte
  228. for len(s) > 0 {
  229. seq, _, n, newState := ansi.DecodeSequence(s, state, nil)
  230. for i := 0; i < n; i++ {
  231. evs = append(evs, encodeKeyEvent(xwindows.KeyEventRecord{
  232. KeyDown: true,
  233. Char: rune(seq[i]),
  234. }))
  235. }
  236. state = newState
  237. s = s[n:]
  238. }
  239. return
  240. }
  241. func encodeUtf16Rune(r rune) []xwindows.InputRecord {
  242. r1, r2 := utf16.EncodeRune(r)
  243. return encodeUtf16Pair(r1, r2)
  244. }
  245. func encodeUtf16Pair(r1, r2 rune) []xwindows.InputRecord {
  246. return []xwindows.InputRecord{
  247. encodeKeyEvent(xwindows.KeyEventRecord{
  248. KeyDown: true,
  249. Char: r1,
  250. }),
  251. encodeKeyEvent(xwindows.KeyEventRecord{
  252. KeyDown: true,
  253. Char: r2,
  254. }),
  255. }
  256. }