key_windows.go 8.5 KB

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