runereader_windows.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package terminal
  2. import (
  3. "os"
  4. "syscall"
  5. "unsafe"
  6. )
  7. var (
  8. dll = syscall.NewLazyDLL("kernel32.dll")
  9. setConsoleMode = dll.NewProc("SetConsoleMode")
  10. getConsoleMode = dll.NewProc("GetConsoleMode")
  11. readConsoleInput = dll.NewProc("ReadConsoleInputW")
  12. )
  13. const (
  14. EVENT_KEY = 0x0001
  15. // key codes for arrow keys
  16. // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
  17. VK_LEFT = 0x25
  18. VK_UP = 0x26
  19. VK_RIGHT = 0x27
  20. VK_DOWN = 0x28
  21. RIGHT_CTRL_PRESSED = 0x0004
  22. LEFT_CTRL_PRESSED = 0x0008
  23. ENABLE_ECHO_INPUT uint32 = 0x0004
  24. ENABLE_LINE_INPUT uint32 = 0x0002
  25. ENABLE_PROCESSED_INPUT uint32 = 0x0001
  26. )
  27. type inputRecord struct {
  28. eventType uint16
  29. padding uint16
  30. event [16]byte
  31. }
  32. type keyEventRecord struct {
  33. bKeyDown int32
  34. wRepeatCount uint16
  35. wVirtualKeyCode uint16
  36. wVirtualScanCode uint16
  37. unicodeChar uint16
  38. wdControlKeyState uint32
  39. }
  40. type runeReaderState struct {
  41. term uint32
  42. }
  43. func newRuneReaderState(input *os.File) runeReaderState {
  44. return runeReaderState{}
  45. }
  46. func (rr *RuneReader) SetTermMode() error {
  47. r, _, err := getConsoleMode.Call(uintptr(rr.Input.Fd()), uintptr(unsafe.Pointer(&rr.state.term)))
  48. // windows return 0 on error
  49. if r == 0 {
  50. return err
  51. }
  52. newState := rr.state.term
  53. newState &^= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT
  54. r, _, err = setConsoleMode.Call(uintptr(rr.Input.Fd()), uintptr(newState))
  55. // windows return 0 on error
  56. if r == 0 {
  57. return err
  58. }
  59. return nil
  60. }
  61. func (rr *RuneReader) RestoreTermMode() error {
  62. r, _, err := setConsoleMode.Call(uintptr(rr.Input.Fd()), uintptr(rr.state.term))
  63. // windows return 0 on error
  64. if r == 0 {
  65. return err
  66. }
  67. return nil
  68. }
  69. func (rr *RuneReader) ReadRune() (rune, int, error) {
  70. ir := &inputRecord{}
  71. bytesRead := 0
  72. for {
  73. rv, _, e := readConsoleInput.Call(rr.Input.Fd(), uintptr(unsafe.Pointer(ir)), 1, uintptr(unsafe.Pointer(&bytesRead)))
  74. // windows returns non-zero to indicate success
  75. if rv == 0 && e != nil {
  76. return 0, 0, e
  77. }
  78. if ir.eventType != EVENT_KEY {
  79. continue
  80. }
  81. // the event data is really a c struct union, so here we have to do an usafe
  82. // cast to put the data into the keyEventRecord (since we have already verified
  83. // above that this event does correspond to a key event
  84. key := (*keyEventRecord)(unsafe.Pointer(&ir.event[0]))
  85. // we only care about key down events
  86. if key.bKeyDown == 0 {
  87. continue
  88. }
  89. if key.wdControlKeyState&(LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED) != 0 && key.unicodeChar == 'C' {
  90. return KeyInterrupt, bytesRead, nil
  91. }
  92. // not a normal character so look up the input sequence from the
  93. // virtual key code mappings (VK_*)
  94. if key.unicodeChar == 0 {
  95. switch key.wVirtualKeyCode {
  96. case VK_DOWN:
  97. return KeyArrowDown, bytesRead, nil
  98. case VK_LEFT:
  99. return KeyArrowLeft, bytesRead, nil
  100. case VK_RIGHT:
  101. return KeyArrowRight, bytesRead, nil
  102. case VK_UP:
  103. return KeyArrowUp, bytesRead, nil
  104. default:
  105. // not a virtual key that we care about so just continue on to
  106. // the next input key
  107. continue
  108. }
  109. }
  110. r := rune(key.unicodeChar)
  111. return r, bytesRead, nil
  112. }
  113. }