driver_windows.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. //go:build windows
  2. // +build windows
  3. package input
  4. import (
  5. "errors"
  6. "fmt"
  7. "strings"
  8. "time"
  9. "unicode"
  10. "unicode/utf16"
  11. "unicode/utf8"
  12. "github.com/charmbracelet/x/ansi"
  13. xwindows "github.com/charmbracelet/x/windows"
  14. "github.com/muesli/cancelreader"
  15. "golang.org/x/sys/windows"
  16. )
  17. // ReadEvents reads input events from the terminal.
  18. //
  19. // It reads the events available in the input buffer and returns them.
  20. func (d *Reader) ReadEvents() ([]Event, error) {
  21. events, err := d.handleConInput()
  22. if errors.Is(err, errNotConInputReader) {
  23. return d.readEvents()
  24. }
  25. return events, err
  26. }
  27. var errNotConInputReader = fmt.Errorf("handleConInput: not a conInputReader")
  28. func (d *Reader) handleConInput() ([]Event, error) {
  29. cc, ok := d.rd.(*conInputReader)
  30. if !ok {
  31. return nil, errNotConInputReader
  32. }
  33. var (
  34. events []xwindows.InputRecord
  35. err error
  36. )
  37. for {
  38. // Peek up to 256 events, this is to allow for sequences events reported as
  39. // key events.
  40. events, err = peekNConsoleInputs(cc.conin, 256)
  41. if cc.isCanceled() {
  42. return nil, cancelreader.ErrCanceled
  43. }
  44. if err != nil {
  45. return nil, fmt.Errorf("peek coninput events: %w", err)
  46. }
  47. if len(events) > 0 {
  48. break
  49. }
  50. // Sleep for a bit to avoid busy waiting.
  51. time.Sleep(10 * time.Millisecond)
  52. }
  53. events, err = readNConsoleInputs(cc.conin, uint32(len(events)))
  54. if cc.isCanceled() {
  55. return nil, cancelreader.ErrCanceled
  56. }
  57. if err != nil {
  58. return nil, fmt.Errorf("read coninput events: %w", err)
  59. }
  60. var evs []Event
  61. for _, event := range events {
  62. if e := d.parser.parseConInputEvent(event, &d.keyState); e != nil {
  63. if multi, ok := e.(MultiEvent); ok {
  64. evs = append(evs, multi...)
  65. } else {
  66. evs = append(evs, e)
  67. }
  68. }
  69. }
  70. return evs, nil
  71. }
  72. func (p *Parser) parseConInputEvent(event xwindows.InputRecord, keyState *win32InputState) Event {
  73. switch event.EventType {
  74. case xwindows.KEY_EVENT:
  75. kevent := event.KeyEvent()
  76. return p.parseWin32InputKeyEvent(keyState, kevent.VirtualKeyCode, kevent.VirtualScanCode,
  77. kevent.Char, kevent.KeyDown, kevent.ControlKeyState, kevent.RepeatCount)
  78. case xwindows.WINDOW_BUFFER_SIZE_EVENT:
  79. wevent := event.WindowBufferSizeEvent()
  80. if wevent.Size.X != keyState.lastWinsizeX || wevent.Size.Y != keyState.lastWinsizeY {
  81. keyState.lastWinsizeX, keyState.lastWinsizeY = wevent.Size.X, wevent.Size.Y
  82. return WindowSizeEvent{
  83. Width: int(wevent.Size.X),
  84. Height: int(wevent.Size.Y),
  85. }
  86. }
  87. case xwindows.MOUSE_EVENT:
  88. mevent := event.MouseEvent()
  89. Event := mouseEvent(keyState.lastMouseBtns, mevent)
  90. keyState.lastMouseBtns = mevent.ButtonState
  91. return Event
  92. case xwindows.FOCUS_EVENT:
  93. fevent := event.FocusEvent()
  94. if fevent.SetFocus {
  95. return FocusEvent{}
  96. }
  97. return BlurEvent{}
  98. case xwindows.MENU_EVENT:
  99. // ignore
  100. }
  101. return nil
  102. }
  103. func mouseEventButton(p, s uint32) (MouseButton, bool) {
  104. var isRelease bool
  105. button := MouseNone
  106. btn := p ^ s
  107. if btn&s == 0 {
  108. isRelease = true
  109. }
  110. if btn == 0 {
  111. switch {
  112. case s&xwindows.FROM_LEFT_1ST_BUTTON_PRESSED > 0:
  113. button = MouseLeft
  114. case s&xwindows.FROM_LEFT_2ND_BUTTON_PRESSED > 0:
  115. button = MouseMiddle
  116. case s&xwindows.RIGHTMOST_BUTTON_PRESSED > 0:
  117. button = MouseRight
  118. case s&xwindows.FROM_LEFT_3RD_BUTTON_PRESSED > 0:
  119. button = MouseBackward
  120. case s&xwindows.FROM_LEFT_4TH_BUTTON_PRESSED > 0:
  121. button = MouseForward
  122. }
  123. return button, isRelease
  124. }
  125. switch btn {
  126. case xwindows.FROM_LEFT_1ST_BUTTON_PRESSED: // left button
  127. button = MouseLeft
  128. case xwindows.RIGHTMOST_BUTTON_PRESSED: // right button
  129. button = MouseRight
  130. case xwindows.FROM_LEFT_2ND_BUTTON_PRESSED: // middle button
  131. button = MouseMiddle
  132. case xwindows.FROM_LEFT_3RD_BUTTON_PRESSED: // unknown (possibly mouse backward)
  133. button = MouseBackward
  134. case xwindows.FROM_LEFT_4TH_BUTTON_PRESSED: // unknown (possibly mouse forward)
  135. button = MouseForward
  136. }
  137. return button, isRelease
  138. }
  139. func mouseEvent(p uint32, e xwindows.MouseEventRecord) (ev Event) {
  140. var mod KeyMod
  141. var isRelease bool
  142. if e.ControlKeyState&(xwindows.LEFT_ALT_PRESSED|xwindows.RIGHT_ALT_PRESSED) != 0 {
  143. mod |= ModAlt
  144. }
  145. if e.ControlKeyState&(xwindows.LEFT_CTRL_PRESSED|xwindows.RIGHT_CTRL_PRESSED) != 0 {
  146. mod |= ModCtrl
  147. }
  148. if e.ControlKeyState&(xwindows.SHIFT_PRESSED) != 0 {
  149. mod |= ModShift
  150. }
  151. m := Mouse{
  152. X: int(e.MousePositon.X),
  153. Y: int(e.MousePositon.Y),
  154. Mod: mod,
  155. }
  156. wheelDirection := int16(highWord(e.ButtonState)) //nolint:gosec
  157. switch e.EventFlags {
  158. case 0, xwindows.DOUBLE_CLICK:
  159. m.Button, isRelease = mouseEventButton(p, e.ButtonState)
  160. case xwindows.MOUSE_WHEELED:
  161. if wheelDirection > 0 {
  162. m.Button = MouseWheelUp
  163. } else {
  164. m.Button = MouseWheelDown
  165. }
  166. case xwindows.MOUSE_HWHEELED:
  167. if wheelDirection > 0 {
  168. m.Button = MouseWheelRight
  169. } else {
  170. m.Button = MouseWheelLeft
  171. }
  172. case xwindows.MOUSE_MOVED:
  173. m.Button, _ = mouseEventButton(p, e.ButtonState)
  174. return MouseMotionEvent(m)
  175. }
  176. if isWheel(m.Button) {
  177. return MouseWheelEvent(m)
  178. } else if isRelease {
  179. return MouseReleaseEvent(m)
  180. }
  181. return MouseClickEvent(m)
  182. }
  183. func highWord(data uint32) uint16 {
  184. return uint16((data & 0xFFFF0000) >> 16) //nolint:gosec
  185. }
  186. func readNConsoleInputs(console windows.Handle, maxEvents uint32) ([]xwindows.InputRecord, error) {
  187. if maxEvents == 0 {
  188. return nil, fmt.Errorf("maxEvents cannot be zero")
  189. }
  190. records := make([]xwindows.InputRecord, maxEvents)
  191. n, err := readConsoleInput(console, records)
  192. return records[:n], err
  193. }
  194. func readConsoleInput(console windows.Handle, inputRecords []xwindows.InputRecord) (uint32, error) {
  195. if len(inputRecords) == 0 {
  196. return 0, fmt.Errorf("size of input record buffer cannot be zero")
  197. }
  198. var read uint32
  199. err := xwindows.ReadConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read) //nolint:gosec
  200. return read, err //nolint:wrapcheck
  201. }
  202. func peekConsoleInput(console windows.Handle, inputRecords []xwindows.InputRecord) (uint32, error) {
  203. if len(inputRecords) == 0 {
  204. return 0, fmt.Errorf("size of input record buffer cannot be zero")
  205. }
  206. var read uint32
  207. err := xwindows.PeekConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read) //nolint:gosec
  208. return read, err //nolint:wrapcheck
  209. }
  210. func peekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]xwindows.InputRecord, error) {
  211. if maxEvents == 0 {
  212. return nil, fmt.Errorf("maxEvents cannot be zero")
  213. }
  214. records := make([]xwindows.InputRecord, maxEvents)
  215. n, err := peekConsoleInput(console, records)
  216. return records[:n], err
  217. }
  218. // parseWin32InputKeyEvent parses a single key event from either the Windows
  219. // Console API or win32-input-mode events. When state is nil, it means this is
  220. // an event from win32-input-mode. Otherwise, it's a key event from the Windows
  221. // Console API and needs a state to decode ANSI escape sequences and utf16
  222. // runes.
  223. func (p *Parser) parseWin32InputKeyEvent(state *win32InputState, vkc uint16, _ uint16, r rune, keyDown bool, cks uint32, repeatCount uint16) (event Event) {
  224. defer func() {
  225. // Respect the repeat count.
  226. if repeatCount > 1 {
  227. var multi MultiEvent
  228. for i := 0; i < int(repeatCount); i++ {
  229. multi = append(multi, event)
  230. }
  231. event = multi
  232. }
  233. }()
  234. if state != nil {
  235. defer func() {
  236. state.lastCks = cks
  237. }()
  238. }
  239. var utf8Buf [utf8.UTFMax]byte
  240. var key Key
  241. if state != nil && state.utf16Half {
  242. state.utf16Half = false
  243. state.utf16Buf[1] = r
  244. codepoint := utf16.DecodeRune(state.utf16Buf[0], state.utf16Buf[1])
  245. rw := utf8.EncodeRune(utf8Buf[:], codepoint)
  246. r, _ = utf8.DecodeRune(utf8Buf[:rw])
  247. key.Code = r
  248. key.Text = string(r)
  249. key.Mod = translateControlKeyState(cks)
  250. key = ensureKeyCase(key, cks)
  251. if keyDown {
  252. return KeyPressEvent(key)
  253. }
  254. return KeyReleaseEvent(key)
  255. }
  256. var baseCode rune
  257. switch {
  258. case vkc == 0:
  259. // Zero means this event is either an escape code or a unicode
  260. // codepoint.
  261. if state != nil && state.ansiIdx == 0 && r != ansi.ESC {
  262. // This is a unicode codepoint.
  263. baseCode = r
  264. break
  265. }
  266. if state != nil {
  267. // Collect ANSI escape code.
  268. state.ansiBuf[state.ansiIdx] = byte(r)
  269. state.ansiIdx++
  270. if state.ansiIdx <= 2 {
  271. // We haven't received enough bytes to determine if this is an
  272. // ANSI escape code.
  273. return nil
  274. }
  275. if r == ansi.ESC {
  276. // We're expecting a closing String Terminator [ansi.ST].
  277. return nil
  278. }
  279. n, event := p.parseSequence(state.ansiBuf[:state.ansiIdx])
  280. if n == 0 {
  281. return nil
  282. }
  283. if _, ok := event.(UnknownEvent); ok {
  284. return nil
  285. }
  286. state.ansiIdx = 0
  287. return event
  288. }
  289. case vkc == xwindows.VK_BACK:
  290. baseCode = KeyBackspace
  291. case vkc == xwindows.VK_TAB:
  292. baseCode = KeyTab
  293. case vkc == xwindows.VK_RETURN:
  294. baseCode = KeyEnter
  295. case vkc == xwindows.VK_SHIFT:
  296. //nolint:nestif
  297. if cks&xwindows.SHIFT_PRESSED != 0 {
  298. if cks&xwindows.ENHANCED_KEY != 0 {
  299. baseCode = KeyRightShift
  300. } else {
  301. baseCode = KeyLeftShift
  302. }
  303. } else if state != nil {
  304. if state.lastCks&xwindows.SHIFT_PRESSED != 0 {
  305. if state.lastCks&xwindows.ENHANCED_KEY != 0 {
  306. baseCode = KeyRightShift
  307. } else {
  308. baseCode = KeyLeftShift
  309. }
  310. }
  311. }
  312. case vkc == xwindows.VK_CONTROL:
  313. if cks&xwindows.LEFT_CTRL_PRESSED != 0 {
  314. baseCode = KeyLeftCtrl
  315. } else if cks&xwindows.RIGHT_CTRL_PRESSED != 0 {
  316. baseCode = KeyRightCtrl
  317. } else if state != nil {
  318. if state.lastCks&xwindows.LEFT_CTRL_PRESSED != 0 {
  319. baseCode = KeyLeftCtrl
  320. } else if state.lastCks&xwindows.RIGHT_CTRL_PRESSED != 0 {
  321. baseCode = KeyRightCtrl
  322. }
  323. }
  324. case vkc == xwindows.VK_MENU:
  325. if cks&xwindows.LEFT_ALT_PRESSED != 0 {
  326. baseCode = KeyLeftAlt
  327. } else if cks&xwindows.RIGHT_ALT_PRESSED != 0 {
  328. baseCode = KeyRightAlt
  329. } else if state != nil {
  330. if state.lastCks&xwindows.LEFT_ALT_PRESSED != 0 {
  331. baseCode = KeyLeftAlt
  332. } else if state.lastCks&xwindows.RIGHT_ALT_PRESSED != 0 {
  333. baseCode = KeyRightAlt
  334. }
  335. }
  336. case vkc == xwindows.VK_PAUSE:
  337. baseCode = KeyPause
  338. case vkc == xwindows.VK_CAPITAL:
  339. baseCode = KeyCapsLock
  340. case vkc == xwindows.VK_ESCAPE:
  341. baseCode = KeyEscape
  342. case vkc == xwindows.VK_SPACE:
  343. baseCode = KeySpace
  344. case vkc == xwindows.VK_PRIOR:
  345. baseCode = KeyPgUp
  346. case vkc == xwindows.VK_NEXT:
  347. baseCode = KeyPgDown
  348. case vkc == xwindows.VK_END:
  349. baseCode = KeyEnd
  350. case vkc == xwindows.VK_HOME:
  351. baseCode = KeyHome
  352. case vkc == xwindows.VK_LEFT:
  353. baseCode = KeyLeft
  354. case vkc == xwindows.VK_UP:
  355. baseCode = KeyUp
  356. case vkc == xwindows.VK_RIGHT:
  357. baseCode = KeyRight
  358. case vkc == xwindows.VK_DOWN:
  359. baseCode = KeyDown
  360. case vkc == xwindows.VK_SELECT:
  361. baseCode = KeySelect
  362. case vkc == xwindows.VK_SNAPSHOT:
  363. baseCode = KeyPrintScreen
  364. case vkc == xwindows.VK_INSERT:
  365. baseCode = KeyInsert
  366. case vkc == xwindows.VK_DELETE:
  367. baseCode = KeyDelete
  368. case vkc >= '0' && vkc <= '9':
  369. baseCode = rune(vkc)
  370. case vkc >= 'A' && vkc <= 'Z':
  371. // Convert to lowercase.
  372. baseCode = rune(vkc) + 32
  373. case vkc == xwindows.VK_LWIN:
  374. baseCode = KeyLeftSuper
  375. case vkc == xwindows.VK_RWIN:
  376. baseCode = KeyRightSuper
  377. case vkc == xwindows.VK_APPS:
  378. baseCode = KeyMenu
  379. case vkc >= xwindows.VK_NUMPAD0 && vkc <= xwindows.VK_NUMPAD9:
  380. baseCode = rune(vkc-xwindows.VK_NUMPAD0) + KeyKp0
  381. case vkc == xwindows.VK_MULTIPLY:
  382. baseCode = KeyKpMultiply
  383. case vkc == xwindows.VK_ADD:
  384. baseCode = KeyKpPlus
  385. case vkc == xwindows.VK_SEPARATOR:
  386. baseCode = KeyKpComma
  387. case vkc == xwindows.VK_SUBTRACT:
  388. baseCode = KeyKpMinus
  389. case vkc == xwindows.VK_DECIMAL:
  390. baseCode = KeyKpDecimal
  391. case vkc == xwindows.VK_DIVIDE:
  392. baseCode = KeyKpDivide
  393. case vkc >= xwindows.VK_F1 && vkc <= xwindows.VK_F24:
  394. baseCode = rune(vkc-xwindows.VK_F1) + KeyF1
  395. case vkc == xwindows.VK_NUMLOCK:
  396. baseCode = KeyNumLock
  397. case vkc == xwindows.VK_SCROLL:
  398. baseCode = KeyScrollLock
  399. case vkc == xwindows.VK_LSHIFT:
  400. baseCode = KeyLeftShift
  401. case vkc == xwindows.VK_RSHIFT:
  402. baseCode = KeyRightShift
  403. case vkc == xwindows.VK_LCONTROL:
  404. baseCode = KeyLeftCtrl
  405. case vkc == xwindows.VK_RCONTROL:
  406. baseCode = KeyRightCtrl
  407. case vkc == xwindows.VK_LMENU:
  408. baseCode = KeyLeftAlt
  409. case vkc == xwindows.VK_RMENU:
  410. baseCode = KeyRightAlt
  411. case vkc == xwindows.VK_VOLUME_MUTE:
  412. baseCode = KeyMute
  413. case vkc == xwindows.VK_VOLUME_DOWN:
  414. baseCode = KeyLowerVol
  415. case vkc == xwindows.VK_VOLUME_UP:
  416. baseCode = KeyRaiseVol
  417. case vkc == xwindows.VK_MEDIA_NEXT_TRACK:
  418. baseCode = KeyMediaNext
  419. case vkc == xwindows.VK_MEDIA_PREV_TRACK:
  420. baseCode = KeyMediaPrev
  421. case vkc == xwindows.VK_MEDIA_STOP:
  422. baseCode = KeyMediaStop
  423. case vkc == xwindows.VK_MEDIA_PLAY_PAUSE:
  424. baseCode = KeyMediaPlayPause
  425. case vkc == xwindows.VK_OEM_1, vkc == xwindows.VK_OEM_PLUS, vkc == xwindows.VK_OEM_COMMA,
  426. vkc == xwindows.VK_OEM_MINUS, vkc == xwindows.VK_OEM_PERIOD, vkc == xwindows.VK_OEM_2,
  427. vkc == xwindows.VK_OEM_3, vkc == xwindows.VK_OEM_4, vkc == xwindows.VK_OEM_5,
  428. vkc == xwindows.VK_OEM_6, vkc == xwindows.VK_OEM_7:
  429. // Use the actual character provided by Windows for current keyboard layout
  430. // instead of hardcoded US layout mappings
  431. if !unicode.IsControl(r) && unicode.IsPrint(r) {
  432. baseCode = r
  433. } else {
  434. // Fallback to original hardcoded mappings for non-printable cases
  435. switch vkc {
  436. case xwindows.VK_OEM_1:
  437. baseCode = ';'
  438. case xwindows.VK_OEM_PLUS:
  439. baseCode = '+'
  440. case xwindows.VK_OEM_COMMA:
  441. baseCode = ','
  442. case xwindows.VK_OEM_MINUS:
  443. baseCode = '-'
  444. case xwindows.VK_OEM_PERIOD:
  445. baseCode = '.'
  446. case xwindows.VK_OEM_2:
  447. baseCode = '/'
  448. case xwindows.VK_OEM_3:
  449. baseCode = '`'
  450. case xwindows.VK_OEM_4:
  451. baseCode = '['
  452. case xwindows.VK_OEM_5:
  453. baseCode = '\\'
  454. case xwindows.VK_OEM_6:
  455. baseCode = ']'
  456. case xwindows.VK_OEM_7:
  457. baseCode = '\''
  458. }
  459. }
  460. }
  461. if utf16.IsSurrogate(r) {
  462. if state != nil {
  463. state.utf16Buf[0] = r
  464. state.utf16Half = true
  465. }
  466. return nil
  467. }
  468. // AltGr is left ctrl + right alt. On non-US keyboards, this is used to type
  469. // special characters and produce printable events.
  470. // XXX: Should this be a KeyMod?
  471. altGr := cks&(xwindows.LEFT_CTRL_PRESSED|xwindows.RIGHT_ALT_PRESSED) == xwindows.LEFT_CTRL_PRESSED|xwindows.RIGHT_ALT_PRESSED
  472. // FIXED: Remove numlock and scroll lock states when checking for printable text
  473. // These lock states shouldn't affect normal typing
  474. cksForTextCheck := cks &^ (xwindows.NUMLOCK_ON | xwindows.SCROLLLOCK_ON)
  475. var text string
  476. keyCode := baseCode
  477. if !unicode.IsControl(r) {
  478. rw := utf8.EncodeRune(utf8Buf[:], r)
  479. keyCode, _ = utf8.DecodeRune(utf8Buf[:rw])
  480. if unicode.IsPrint(keyCode) && (cksForTextCheck == 0 ||
  481. cksForTextCheck == xwindows.SHIFT_PRESSED ||
  482. cksForTextCheck == xwindows.CAPSLOCK_ON ||
  483. altGr) {
  484. // If the control key state is 0, shift is pressed, or caps lock
  485. // then the key event is a printable event i.e. [text] is not empty.
  486. text = string(keyCode)
  487. }
  488. }
  489. // Special case: numeric keypad divide should produce "/" text on all layouts (fix french keyboard layout)
  490. if baseCode == KeyKpDivide {
  491. text = "/"
  492. }
  493. key.Code = keyCode
  494. key.Text = text
  495. key.Mod = translateControlKeyState(cks)
  496. key.BaseCode = baseCode
  497. key = ensureKeyCase(key, cks)
  498. if keyDown {
  499. return KeyPressEvent(key)
  500. }
  501. return KeyReleaseEvent(key)
  502. }
  503. // ensureKeyCase ensures that the key's text is in the correct case based on the
  504. // control key state.
  505. func ensureKeyCase(key Key, cks uint32) Key {
  506. if len(key.Text) == 0 {
  507. return key
  508. }
  509. hasShift := cks&xwindows.SHIFT_PRESSED != 0
  510. hasCaps := cks&xwindows.CAPSLOCK_ON != 0
  511. if hasShift || hasCaps {
  512. if unicode.IsLower(key.Code) {
  513. key.ShiftedCode = unicode.ToUpper(key.Code)
  514. key.Text = string(key.ShiftedCode)
  515. }
  516. } else {
  517. if unicode.IsUpper(key.Code) {
  518. key.ShiftedCode = unicode.ToLower(key.Code)
  519. key.Text = string(key.ShiftedCode)
  520. }
  521. }
  522. return key
  523. }
  524. // translateControlKeyState translates the control key state from the Windows
  525. // Console API into a Mod bitmask.
  526. func translateControlKeyState(cks uint32) (m KeyMod) {
  527. if cks&xwindows.LEFT_CTRL_PRESSED != 0 || cks&xwindows.RIGHT_CTRL_PRESSED != 0 {
  528. m |= ModCtrl
  529. }
  530. if cks&xwindows.LEFT_ALT_PRESSED != 0 || cks&xwindows.RIGHT_ALT_PRESSED != 0 {
  531. m |= ModAlt
  532. }
  533. if cks&xwindows.SHIFT_PRESSED != 0 {
  534. m |= ModShift
  535. }
  536. if cks&xwindows.CAPSLOCK_ON != 0 {
  537. m |= ModCapsLock
  538. }
  539. if cks&xwindows.NUMLOCK_ON != 0 {
  540. m |= ModNumLock
  541. }
  542. if cks&xwindows.SCROLLLOCK_ON != 0 {
  543. m |= ModScrollLock
  544. }
  545. return
  546. }
  547. //nolint:unused
  548. func keyEventString(vkc, sc uint16, r rune, keyDown bool, cks uint32, repeatCount uint16) string {
  549. var s strings.Builder
  550. s.WriteString("vkc: ")
  551. s.WriteString(fmt.Sprintf("%d, 0x%02x", vkc, vkc))
  552. s.WriteString(", sc: ")
  553. s.WriteString(fmt.Sprintf("%d, 0x%02x", sc, sc))
  554. s.WriteString(", r: ")
  555. s.WriteString(fmt.Sprintf("%q", r))
  556. s.WriteString(", down: ")
  557. s.WriteString(fmt.Sprintf("%v", keyDown))
  558. s.WriteString(", cks: [")
  559. if cks&xwindows.LEFT_ALT_PRESSED != 0 {
  560. s.WriteString("left alt, ")
  561. }
  562. if cks&xwindows.RIGHT_ALT_PRESSED != 0 {
  563. s.WriteString("right alt, ")
  564. }
  565. if cks&xwindows.LEFT_CTRL_PRESSED != 0 {
  566. s.WriteString("left ctrl, ")
  567. }
  568. if cks&xwindows.RIGHT_CTRL_PRESSED != 0 {
  569. s.WriteString("right ctrl, ")
  570. }
  571. if cks&xwindows.SHIFT_PRESSED != 0 {
  572. s.WriteString("shift, ")
  573. }
  574. if cks&xwindows.CAPSLOCK_ON != 0 {
  575. s.WriteString("caps lock, ")
  576. }
  577. if cks&xwindows.NUMLOCK_ON != 0 {
  578. s.WriteString("num lock, ")
  579. }
  580. if cks&xwindows.SCROLLLOCK_ON != 0 {
  581. s.WriteString("scroll lock, ")
  582. }
  583. if cks&xwindows.ENHANCED_KEY != 0 {
  584. s.WriteString("enhanced key, ")
  585. }
  586. s.WriteString("], repeat count: ")
  587. s.WriteString(fmt.Sprintf("%d", repeatCount))
  588. return s.String()
  589. }