input_windows.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. package liner
  2. import (
  3. "bufio"
  4. "os"
  5. "syscall"
  6. "unsafe"
  7. )
  8. var (
  9. kernel32 = syscall.NewLazyDLL("kernel32.dll")
  10. procGetStdHandle = kernel32.NewProc("GetStdHandle")
  11. procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
  12. procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents")
  13. procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
  14. procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
  15. procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
  16. procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
  17. procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
  18. )
  19. // These names are from the Win32 api, so they use underscores (contrary to
  20. // what golint suggests)
  21. const (
  22. std_input_handle = uint32(-10 & 0xFFFFFFFF)
  23. std_output_handle = uint32(-11 & 0xFFFFFFFF)
  24. std_error_handle = uint32(-12 & 0xFFFFFFFF)
  25. invalid_handle_value = ^uintptr(0)
  26. )
  27. type inputMode uint32
  28. // State represents an open terminal
  29. type State struct {
  30. commonState
  31. handle syscall.Handle
  32. hOut syscall.Handle
  33. origMode inputMode
  34. defaultMode inputMode
  35. key interface{}
  36. repeat uint16
  37. }
  38. const (
  39. enableEchoInput = 0x4
  40. enableInsertMode = 0x20
  41. enableLineInput = 0x2
  42. enableMouseInput = 0x10
  43. enableProcessedInput = 0x1
  44. enableQuickEditMode = 0x40
  45. enableWindowInput = 0x8
  46. )
  47. // NewLiner initializes a new *State, and sets the terminal into raw mode. To
  48. // restore the terminal to its previous state, call State.Close().
  49. func NewLiner() *State {
  50. var s State
  51. hIn, _, _ := procGetStdHandle.Call(uintptr(std_input_handle))
  52. s.handle = syscall.Handle(hIn)
  53. hOut, _, _ := procGetStdHandle.Call(uintptr(std_output_handle))
  54. s.hOut = syscall.Handle(hOut)
  55. s.terminalSupported = true
  56. if m, err := TerminalMode(); err == nil {
  57. s.origMode = m.(inputMode)
  58. mode := s.origMode
  59. mode &^= enableEchoInput
  60. mode &^= enableInsertMode
  61. mode &^= enableLineInput
  62. mode &^= enableMouseInput
  63. mode |= enableWindowInput
  64. mode.ApplyMode()
  65. } else {
  66. s.inputRedirected = true
  67. s.r = bufio.NewReader(os.Stdin)
  68. }
  69. s.getColumns()
  70. s.outputRedirected = s.columns <= 0
  71. return &s
  72. }
  73. // These names are from the Win32 api, so they use underscores (contrary to
  74. // what golint suggests)
  75. const (
  76. focus_event = 0x0010
  77. key_event = 0x0001
  78. menu_event = 0x0008
  79. mouse_event = 0x0002
  80. window_buffer_size_event = 0x0004
  81. )
  82. type input_record struct {
  83. eventType uint16
  84. pad uint16
  85. blob [16]byte
  86. }
  87. type key_event_record struct {
  88. KeyDown int32
  89. RepeatCount uint16
  90. VirtualKeyCode uint16
  91. VirtualScanCode uint16
  92. Char int16
  93. ControlKeyState uint32
  94. }
  95. // These names are from the Win32 api, so they use underscores (contrary to
  96. // what golint suggests)
  97. const (
  98. vk_tab = 0x09
  99. vk_prior = 0x21
  100. vk_next = 0x22
  101. vk_end = 0x23
  102. vk_home = 0x24
  103. vk_left = 0x25
  104. vk_up = 0x26
  105. vk_right = 0x27
  106. vk_down = 0x28
  107. vk_insert = 0x2d
  108. vk_delete = 0x2e
  109. vk_f1 = 0x70
  110. vk_f2 = 0x71
  111. vk_f3 = 0x72
  112. vk_f4 = 0x73
  113. vk_f5 = 0x74
  114. vk_f6 = 0x75
  115. vk_f7 = 0x76
  116. vk_f8 = 0x77
  117. vk_f9 = 0x78
  118. vk_f10 = 0x79
  119. vk_f11 = 0x7a
  120. vk_f12 = 0x7b
  121. bKey = 0x42
  122. fKey = 0x46
  123. yKey = 0x59
  124. )
  125. const (
  126. shiftPressed = 0x0010
  127. leftAltPressed = 0x0002
  128. leftCtrlPressed = 0x0008
  129. rightAltPressed = 0x0001
  130. rightCtrlPressed = 0x0004
  131. modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed
  132. )
  133. // inputWaiting only returns true if the next call to readNext will return immediately.
  134. func (s *State) inputWaiting() bool {
  135. var num uint32
  136. ok, _, _ := procGetNumberOfConsoleInputEvents.Call(uintptr(s.handle), uintptr(unsafe.Pointer(&num)))
  137. if ok == 0 {
  138. // call failed, so we cannot guarantee a non-blocking readNext
  139. return false
  140. }
  141. // during a "paste" input events are always an odd number, and
  142. // the last one results in a blocking readNext, so return false
  143. // when num is 1 or 0.
  144. return num > 1
  145. }
  146. func (s *State) readNext() (interface{}, error) {
  147. if s.repeat > 0 {
  148. s.repeat--
  149. return s.key, nil
  150. }
  151. var input input_record
  152. pbuf := uintptr(unsafe.Pointer(&input))
  153. var rv uint32
  154. prv := uintptr(unsafe.Pointer(&rv))
  155. for {
  156. ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv)
  157. if ok == 0 {
  158. return nil, err
  159. }
  160. if input.eventType == window_buffer_size_event {
  161. xy := (*coord)(unsafe.Pointer(&input.blob[0]))
  162. s.columns = int(xy.x)
  163. if s.columns > 1 {
  164. s.columns--
  165. }
  166. return winch, nil
  167. }
  168. if input.eventType != key_event {
  169. continue
  170. }
  171. ke := (*key_event_record)(unsafe.Pointer(&input.blob[0]))
  172. if ke.KeyDown == 0 {
  173. continue
  174. }
  175. if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed {
  176. s.key = shiftTab
  177. } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
  178. ke.ControlKeyState&modKeys == rightAltPressed) {
  179. s.key = altB
  180. } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
  181. ke.ControlKeyState&modKeys == rightAltPressed) {
  182. s.key = altF
  183. } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
  184. ke.ControlKeyState&modKeys == rightAltPressed) {
  185. s.key = altY
  186. } else if ke.Char > 0 {
  187. s.key = rune(ke.Char)
  188. } else {
  189. switch ke.VirtualKeyCode {
  190. case vk_prior:
  191. s.key = pageUp
  192. case vk_next:
  193. s.key = pageDown
  194. case vk_end:
  195. s.key = end
  196. case vk_home:
  197. s.key = home
  198. case vk_left:
  199. s.key = left
  200. if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
  201. if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
  202. s.key = wordLeft
  203. }
  204. }
  205. case vk_right:
  206. s.key = right
  207. if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
  208. if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
  209. s.key = wordRight
  210. }
  211. }
  212. case vk_up:
  213. s.key = up
  214. case vk_down:
  215. s.key = down
  216. case vk_insert:
  217. s.key = insert
  218. case vk_delete:
  219. s.key = del
  220. case vk_f1:
  221. s.key = f1
  222. case vk_f2:
  223. s.key = f2
  224. case vk_f3:
  225. s.key = f3
  226. case vk_f4:
  227. s.key = f4
  228. case vk_f5:
  229. s.key = f5
  230. case vk_f6:
  231. s.key = f6
  232. case vk_f7:
  233. s.key = f7
  234. case vk_f8:
  235. s.key = f8
  236. case vk_f9:
  237. s.key = f9
  238. case vk_f10:
  239. s.key = f10
  240. case vk_f11:
  241. s.key = f11
  242. case vk_f12:
  243. s.key = f12
  244. default:
  245. // Eat modifier keys
  246. // TODO: return Action(Unknown) if the key isn't a
  247. // modifier.
  248. continue
  249. }
  250. }
  251. if ke.RepeatCount > 1 {
  252. s.repeat = ke.RepeatCount - 1
  253. }
  254. return s.key, nil
  255. }
  256. }
  257. // Close returns the terminal to its previous mode
  258. func (s *State) Close() error {
  259. s.origMode.ApplyMode()
  260. return nil
  261. }
  262. func (s *State) startPrompt() {
  263. if m, err := TerminalMode(); err == nil {
  264. s.defaultMode = m.(inputMode)
  265. mode := s.defaultMode
  266. mode &^= enableProcessedInput
  267. mode.ApplyMode()
  268. }
  269. }
  270. func (s *State) restartPrompt() {
  271. }
  272. func (s *State) stopPrompt() {
  273. s.defaultMode.ApplyMode()
  274. }
  275. // TerminalSupported returns true because line editing is always
  276. // supported on Windows.
  277. func TerminalSupported() bool {
  278. return true
  279. }
  280. func (mode inputMode) ApplyMode() error {
  281. hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
  282. if hIn == invalid_handle_value || hIn == 0 {
  283. return err
  284. }
  285. ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode))
  286. if ok != 0 {
  287. err = nil
  288. }
  289. return err
  290. }
  291. // TerminalMode returns the current terminal input mode as an InputModeSetter.
  292. //
  293. // This function is provided for convenience, and should
  294. // not be necessary for most users of liner.
  295. func TerminalMode() (ModeApplier, error) {
  296. var mode inputMode
  297. hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
  298. if hIn == invalid_handle_value || hIn == 0 {
  299. return nil, err
  300. }
  301. ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode)))
  302. if ok != 0 {
  303. err = nil
  304. }
  305. return mode, err
  306. }