key_windows.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //go:build windows
  2. // +build windows
  3. package tea
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "github.com/erikgeiser/coninput"
  9. localereader "github.com/mattn/go-localereader"
  10. "golang.org/x/sys/windows"
  11. )
  12. func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
  13. if coninReader, ok := input.(*conInputReader); ok {
  14. return readConInputs(ctx, msgs, coninReader.conin)
  15. }
  16. return readAnsiInputs(ctx, msgs, localereader.NewReader(input))
  17. }
  18. func readConInputs(ctx context.Context, msgsch chan<- Msg, con windows.Handle) error {
  19. var ps coninput.ButtonState // keep track of previous mouse state
  20. var ws coninput.WindowBufferSizeEventRecord // keep track of the last window size event
  21. for {
  22. events, err := coninput.ReadNConsoleInputs(con, 16)
  23. if err != nil {
  24. return fmt.Errorf("read coninput events: %w", err)
  25. }
  26. for _, event := range events {
  27. var msgs []Msg
  28. switch e := event.Unwrap().(type) {
  29. case coninput.KeyEventRecord:
  30. if !e.KeyDown || e.VirtualKeyCode == coninput.VK_SHIFT {
  31. continue
  32. }
  33. for i := 0; i < int(e.RepeatCount); i++ {
  34. eventKeyType := keyType(e)
  35. var runes []rune
  36. // Add the character only if the key type is an actual character and not a control sequence.
  37. // This mimics the behavior in readAnsiInputs where the character is also removed.
  38. // We don't need to handle KeySpace here. See the comment in keyType().
  39. if eventKeyType == KeyRunes {
  40. runes = []rune{e.Char}
  41. }
  42. msgs = append(msgs, KeyMsg{
  43. Type: eventKeyType,
  44. Runes: runes,
  45. Alt: e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED),
  46. })
  47. }
  48. case coninput.WindowBufferSizeEventRecord:
  49. if e != ws {
  50. ws = e
  51. msgs = append(msgs, WindowSizeMsg{
  52. Width: int(e.Size.X),
  53. Height: int(e.Size.Y),
  54. })
  55. }
  56. case coninput.MouseEventRecord:
  57. event := mouseEvent(ps, e)
  58. if event.Type != MouseUnknown {
  59. msgs = append(msgs, event)
  60. }
  61. ps = e.ButtonState
  62. case coninput.FocusEventRecord, coninput.MenuEventRecord:
  63. // ignore
  64. default: // unknown event
  65. continue
  66. }
  67. // Send all messages to the channel
  68. for _, msg := range msgs {
  69. select {
  70. case msgsch <- msg:
  71. case <-ctx.Done():
  72. err := ctx.Err()
  73. if err != nil {
  74. return fmt.Errorf("coninput context error: %w", err)
  75. }
  76. return err
  77. }
  78. }
  79. }
  80. }
  81. }
  82. func mouseEventButton(p, s coninput.ButtonState) (button MouseButton, action MouseAction) {
  83. btn := p ^ s
  84. action = MouseActionPress
  85. if btn&s == 0 {
  86. action = MouseActionRelease
  87. }
  88. if btn == 0 {
  89. switch {
  90. case s&coninput.FROM_LEFT_1ST_BUTTON_PRESSED > 0:
  91. button = MouseButtonLeft
  92. case s&coninput.FROM_LEFT_2ND_BUTTON_PRESSED > 0:
  93. button = MouseButtonMiddle
  94. case s&coninput.RIGHTMOST_BUTTON_PRESSED > 0:
  95. button = MouseButtonRight
  96. case s&coninput.FROM_LEFT_3RD_BUTTON_PRESSED > 0:
  97. button = MouseButtonBackward
  98. case s&coninput.FROM_LEFT_4TH_BUTTON_PRESSED > 0:
  99. button = MouseButtonForward
  100. }
  101. return
  102. }
  103. switch {
  104. case btn == coninput.FROM_LEFT_1ST_BUTTON_PRESSED: // left button
  105. button = MouseButtonLeft
  106. case btn == coninput.RIGHTMOST_BUTTON_PRESSED: // right button
  107. button = MouseButtonRight
  108. case btn == coninput.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button
  109. button = MouseButtonMiddle
  110. case btn == coninput.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward)
  111. button = MouseButtonBackward
  112. case btn == coninput.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward)
  113. button = MouseButtonForward
  114. }
  115. return button, action
  116. }
  117. func mouseEvent(p coninput.ButtonState, e coninput.MouseEventRecord) MouseMsg {
  118. ev := MouseMsg{
  119. X: int(e.MousePositon.X),
  120. Y: int(e.MousePositon.Y),
  121. Alt: e.ControlKeyState.Contains(coninput.LEFT_ALT_PRESSED | coninput.RIGHT_ALT_PRESSED),
  122. Ctrl: e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED),
  123. Shift: e.ControlKeyState.Contains(coninput.SHIFT_PRESSED),
  124. }
  125. switch e.EventFlags {
  126. case coninput.CLICK, coninput.DOUBLE_CLICK:
  127. ev.Button, ev.Action = mouseEventButton(p, e.ButtonState)
  128. if ev.Action == MouseActionRelease {
  129. ev.Type = MouseRelease
  130. }
  131. switch ev.Button {
  132. case MouseButtonLeft:
  133. ev.Type = MouseLeft
  134. case MouseButtonMiddle:
  135. ev.Type = MouseMiddle
  136. case MouseButtonRight:
  137. ev.Type = MouseRight
  138. case MouseButtonBackward:
  139. ev.Type = MouseBackward
  140. case MouseButtonForward:
  141. ev.Type = MouseForward
  142. }
  143. case coninput.MOUSE_WHEELED:
  144. if e.WheelDirection > 0 {
  145. ev.Button = MouseButtonWheelUp
  146. ev.Type = MouseWheelUp
  147. } else {
  148. ev.Button = MouseButtonWheelDown
  149. ev.Type = MouseWheelDown
  150. }
  151. case coninput.MOUSE_HWHEELED:
  152. if e.WheelDirection > 0 {
  153. ev.Button = MouseButtonWheelRight
  154. ev.Type = MouseWheelRight
  155. } else {
  156. ev.Button = MouseButtonWheelLeft
  157. ev.Type = MouseWheelLeft
  158. }
  159. case coninput.MOUSE_MOVED:
  160. ev.Button, _ = mouseEventButton(p, e.ButtonState)
  161. ev.Action = MouseActionMotion
  162. ev.Type = MouseMotion
  163. }
  164. return ev
  165. }
  166. func keyType(e coninput.KeyEventRecord) KeyType {
  167. code := e.VirtualKeyCode
  168. shiftPressed := e.ControlKeyState.Contains(coninput.SHIFT_PRESSED)
  169. ctrlPressed := e.ControlKeyState.Contains(coninput.LEFT_CTRL_PRESSED | coninput.RIGHT_CTRL_PRESSED)
  170. switch code {
  171. case coninput.VK_RETURN:
  172. return KeyEnter
  173. case coninput.VK_BACK:
  174. return KeyBackspace
  175. case coninput.VK_TAB:
  176. if shiftPressed {
  177. return KeyShiftTab
  178. }
  179. return KeyTab
  180. case coninput.VK_SPACE:
  181. return KeyRunes // this could be KeySpace but on unix space also produces KeyRunes
  182. case coninput.VK_ESCAPE:
  183. return KeyEscape
  184. case coninput.VK_UP:
  185. switch {
  186. case shiftPressed && ctrlPressed:
  187. return KeyCtrlShiftUp
  188. case shiftPressed:
  189. return KeyShiftUp
  190. case ctrlPressed:
  191. return KeyCtrlUp
  192. default:
  193. return KeyUp
  194. }
  195. case coninput.VK_DOWN:
  196. switch {
  197. case shiftPressed && ctrlPressed:
  198. return KeyCtrlShiftDown
  199. case shiftPressed:
  200. return KeyShiftDown
  201. case ctrlPressed:
  202. return KeyCtrlDown
  203. default:
  204. return KeyDown
  205. }
  206. case coninput.VK_RIGHT:
  207. switch {
  208. case shiftPressed && ctrlPressed:
  209. return KeyCtrlShiftRight
  210. case shiftPressed:
  211. return KeyShiftRight
  212. case ctrlPressed:
  213. return KeyCtrlRight
  214. default:
  215. return KeyRight
  216. }
  217. case coninput.VK_LEFT:
  218. switch {
  219. case shiftPressed && ctrlPressed:
  220. return KeyCtrlShiftLeft
  221. case shiftPressed:
  222. return KeyShiftLeft
  223. case ctrlPressed:
  224. return KeyCtrlLeft
  225. default:
  226. return KeyLeft
  227. }
  228. case coninput.VK_HOME:
  229. switch {
  230. case shiftPressed && ctrlPressed:
  231. return KeyCtrlShiftHome
  232. case shiftPressed:
  233. return KeyShiftHome
  234. case ctrlPressed:
  235. return KeyCtrlHome
  236. default:
  237. return KeyHome
  238. }
  239. case coninput.VK_END:
  240. switch {
  241. case shiftPressed && ctrlPressed:
  242. return KeyCtrlShiftEnd
  243. case shiftPressed:
  244. return KeyShiftEnd
  245. case ctrlPressed:
  246. return KeyCtrlEnd
  247. default:
  248. return KeyEnd
  249. }
  250. case coninput.VK_PRIOR:
  251. return KeyPgUp
  252. case coninput.VK_NEXT:
  253. return KeyPgDown
  254. case coninput.VK_DELETE:
  255. return KeyDelete
  256. default:
  257. if e.ControlKeyState&(coninput.LEFT_CTRL_PRESSED|coninput.RIGHT_CTRL_PRESSED) == 0 {
  258. return KeyRunes
  259. }
  260. switch e.Char {
  261. case '@':
  262. return KeyCtrlAt
  263. case '\x01':
  264. return KeyCtrlA
  265. case '\x02':
  266. return KeyCtrlB
  267. case '\x03':
  268. return KeyCtrlC
  269. case '\x04':
  270. return KeyCtrlD
  271. case '\x05':
  272. return KeyCtrlE
  273. case '\x06':
  274. return KeyCtrlF
  275. case '\a':
  276. return KeyCtrlG
  277. case '\b':
  278. return KeyCtrlH
  279. case '\t':
  280. return KeyCtrlI
  281. case '\n':
  282. return KeyCtrlJ
  283. case '\v':
  284. return KeyCtrlK
  285. case '\f':
  286. return KeyCtrlL
  287. case '\r':
  288. return KeyCtrlM
  289. case '\x0e':
  290. return KeyCtrlN
  291. case '\x0f':
  292. return KeyCtrlO
  293. case '\x10':
  294. return KeyCtrlP
  295. case '\x11':
  296. return KeyCtrlQ
  297. case '\x12':
  298. return KeyCtrlR
  299. case '\x13':
  300. return KeyCtrlS
  301. case '\x14':
  302. return KeyCtrlT
  303. case '\x15':
  304. return KeyCtrlU
  305. case '\x16':
  306. return KeyCtrlV
  307. case '\x17':
  308. return KeyCtrlW
  309. case '\x18':
  310. return KeyCtrlX
  311. case '\x19':
  312. return KeyCtrlY
  313. case '\x1a':
  314. return KeyCtrlZ
  315. case '\x1b':
  316. return KeyCtrlCloseBracket
  317. case '\x1c':
  318. return KeyCtrlBackslash
  319. case '\x1f':
  320. return KeyCtrlUnderscore
  321. }
  322. switch code {
  323. case coninput.VK_OEM_4:
  324. return KeyCtrlOpenBracket
  325. }
  326. return KeyRunes
  327. }
  328. }