util_windows.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. package getch
  10. /*
  11. The code in this file was inspired by:
  12. http://code.google.com/p/termbox
  13. Pure Go termbox implementation
  14. */
  15. import (
  16. "sync"
  17. "syscall"
  18. "time"
  19. "unsafe"
  20. )
  21. /*
  22. attachReader attaches a character reader on Windows.
  23. */
  24. func attachReader() (getch, error) {
  25. // Create an event object which can receive input events
  26. eobj, err := createEvent()
  27. if err == nil {
  28. gw := &getchWindows{eobj, 0, 0, make(chan *internalKeyEvent), make(chan bool),
  29. &sync.WaitGroup{}}
  30. // Open the console input device
  31. if gw.in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0); err == nil {
  32. if gw.out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0); err == nil {
  33. // All is well we can start listening for events
  34. go gw.eventListener()
  35. // Make sure the input buffer is clear
  36. time.Sleep(100 * time.Millisecond)
  37. for gw.GetKeyAsync() != nil {
  38. time.Sleep(100 * time.Millisecond)
  39. }
  40. return gw, nil
  41. }
  42. }
  43. gw.Close()
  44. }
  45. return nil, err
  46. }
  47. // Getch object implementation for windows
  48. // =======================================
  49. /*
  50. getchWindows is the Windows based getch implementation.
  51. */
  52. type getchWindows struct {
  53. eobj syscall.Handle // Event object handle
  54. in syscall.Handle // Console input device handler
  55. out syscall.Handle // Console output device handle
  56. keyEventChan chan *internalKeyEvent // Channel for input events
  57. stopChan chan bool // Channel for shutdown event
  58. wg *sync.WaitGroup // Waitgroup for shutdown process
  59. }
  60. /*
  61. CursorPosition returns the current cursor position.
  62. */
  63. func (gw *getchWindows) CursorPosition() (int, int, error) {
  64. var x, y int
  65. var err error
  66. info, err := getConsoleScreenBufferInfo(gw.out)
  67. if err == nil {
  68. x = int(info.cursorPosition.x)
  69. y = int(info.cursorPosition.y)
  70. }
  71. return x, y, err
  72. }
  73. /*
  74. SetCursorPosition sets the current cursor position
  75. */
  76. func (gw *getchWindows) SetCursorPosition(x, y int) error {
  77. return setConsoleCursorPosition(gw.out, &winCOORD{int16(x), int16(y)})
  78. }
  79. /*
  80. GetKey returns the next key event or an error. This function blocks if no key
  81. event is available.
  82. */
  83. func (gw *getchWindows) GetKey() *internalKeyEvent {
  84. return <-gw.keyEventChan
  85. }
  86. /*
  87. GetKeyAsync returns the next key event or an error. This function does not block if no key
  88. event is available - in this case nil is returned.
  89. */
  90. func (gw *getchWindows) GetKeyAsync() *internalKeyEvent {
  91. select {
  92. case e := <-gw.keyEventChan:
  93. return e
  94. default:
  95. }
  96. return nil
  97. }
  98. /*
  99. Close detaches the character reader.
  100. */
  101. func (gw *getchWindows) Close() {
  102. // Send stop command and wait for the eventListener thread to end
  103. gw.wg.Add(1)
  104. // Empty all pending keys
  105. for gw.GetKeyAsync() != nil {
  106. time.Sleep(10 * time.Millisecond)
  107. }
  108. gw.stopChan <- true
  109. gw.wg.Wait()
  110. // Ignoring errors here since we are closing
  111. syscall.Close(gw.in)
  112. syscall.Close(gw.out)
  113. syscall.Close(gw.eobj)
  114. }
  115. /*
  116. eventListener is a thread which will listen for events.
  117. */
  118. func (gw *getchWindows) eventListener() {
  119. var ir inputRecord
  120. // Loop until an exit is requested
  121. loop:
  122. for true {
  123. ok, err := waitForMultipleObjects([]syscall.Handle{gw.in, gw.eobj})
  124. if err != nil {
  125. gw.keyEventChan <- &internalKeyEvent{err: err}
  126. }
  127. // Check if we should stop
  128. select {
  129. case <-gw.stopChan:
  130. break loop
  131. default:
  132. }
  133. if err == nil && ok {
  134. if err = readConsoleInput(gw.in, &ir); err != nil {
  135. gw.keyEventChan <- &internalKeyEvent{err: err}
  136. } else {
  137. // Only analyse actual keyboard events
  138. if ir.eventType == 0x1 {
  139. if kr := (*winKeyEvent)(unsafe.Pointer(&ir.event)); kr.keyDown == 1 {
  140. gw.keyEventChan <- gw.buildKeyEvent(kr)
  141. }
  142. }
  143. }
  144. }
  145. }
  146. gw.wg.Done()
  147. }
  148. /*
  149. buildKeyEvent builds an internalKeyEvent from a windows key event.
  150. */
  151. func (gw *getchWindows) buildKeyEvent(wke *winKeyEvent) *internalKeyEvent {
  152. ike := &internalKeyEvent{&KeyEvent{}, nil}
  153. ike.Alt = wke.controlKeyState&(0x0001|0x0002) != 0 // Check if right alt or left alt is pressed
  154. ike.Ctrl = wke.controlKeyState&(0x0004|0x0008) != 0 // Check if right ctrl or left ctrl is pressed
  155. ike.Shift = wke.controlKeyState&0x0010 != 0 // Check if shift is pressed
  156. ike.Rune = rune(wke.unicodeChar)
  157. // Check for printable character
  158. switch wke.virtualKeyCode {
  159. case 0x41: // Letters
  160. ike.Code = KeyA
  161. case 0x42:
  162. ike.Code = KeyB
  163. case 0x43:
  164. ike.Code = KeyC
  165. case 0x44:
  166. ike.Code = KeyD
  167. case 0x45:
  168. ike.Code = KeyE
  169. case 0x46:
  170. ike.Code = KeyF
  171. case 0x47:
  172. ike.Code = KeyG
  173. case 0x48:
  174. ike.Code = KeyH
  175. case 0x49:
  176. ike.Code = KeyI
  177. case 0x4a:
  178. ike.Code = KeyJ
  179. case 0x4b:
  180. ike.Code = KeyK
  181. case 0x4c:
  182. ike.Code = KeyL
  183. case 0x4d:
  184. ike.Code = KeyM
  185. case 0x4e:
  186. ike.Code = KeyN
  187. case 0x4f:
  188. ike.Code = KeyO
  189. case 0x50:
  190. ike.Code = KeyP
  191. case 0x51:
  192. ike.Code = KeyQ
  193. case 0x52:
  194. ike.Code = KeyR
  195. case 0x53:
  196. ike.Code = KeyS
  197. case 0x54:
  198. ike.Code = KeyT
  199. case 0x55:
  200. ike.Code = KeyU
  201. case 0x56:
  202. ike.Code = KeyV
  203. case 0x57:
  204. ike.Code = KeyW
  205. case 0x58:
  206. ike.Code = KeyX
  207. case 0x59:
  208. ike.Code = KeyY
  209. case 0x5a:
  210. ike.Code = KeyZ
  211. case 0x30: // Numbers
  212. ike.Code = Key0
  213. case 0x31:
  214. ike.Code = Key1
  215. case 0x32:
  216. ike.Code = Key2
  217. case 0x33:
  218. ike.Code = Key3
  219. case 0x34:
  220. ike.Code = Key4
  221. case 0x35:
  222. ike.Code = Key5
  223. case 0x36:
  224. ike.Code = Key6
  225. case 0x37:
  226. ike.Code = Key7
  227. case 0x38:
  228. ike.Code = Key8
  229. case 0x39:
  230. ike.Code = Key9
  231. case 0xdf: // Symbols
  232. ike.Code = KeyBacktick
  233. case 0xbd:
  234. ike.Code = KeyMinus
  235. case 0xbb:
  236. ike.Code = KeyEqual
  237. case 0xdc:
  238. ike.Code = KeyBackslash
  239. case 0xbc:
  240. ike.Code = KeyComma
  241. case 0xbe:
  242. ike.Code = KeyDot
  243. case 0xbf:
  244. ike.Code = KeySlash
  245. case 0xba:
  246. ike.Code = KeySemiColon
  247. case 0xc0:
  248. ike.Code = KeyQuote
  249. case 0xde:
  250. ike.Code = KeyHash
  251. case 0xdb:
  252. ike.Code = KeyBracketOpen
  253. case 0xdd:
  254. ike.Code = KeyBracketClose
  255. default:
  256. // Key pressed cannot be a printable character
  257. ike.Rune = 0x00
  258. }
  259. // Check for non-printable keys
  260. switch wke.virtualKeyCode {
  261. case 0x70:
  262. ike.Code = KeyF1
  263. case 0x71:
  264. ike.Code = KeyF2
  265. case 0x72:
  266. ike.Code = KeyF3
  267. case 0x73:
  268. ike.Code = KeyF4
  269. case 0x74:
  270. ike.Code = KeyF5
  271. case 0x75:
  272. ike.Code = KeyF6
  273. case 0x76:
  274. ike.Code = KeyF7
  275. case 0x77:
  276. ike.Code = KeyF8
  277. case 0x78:
  278. ike.Code = KeyF9
  279. case 0x79:
  280. ike.Code = KeyF10
  281. case 0x7a:
  282. ike.Code = KeyF11
  283. case 0x7b:
  284. ike.Code = KeyF12
  285. case 0x9:
  286. ike.Code = KeyTab
  287. case 0xd:
  288. ike.Code = KeyEnter
  289. case 0x8:
  290. ike.Code = KeyBackspace
  291. case 0x1b:
  292. ike.Code = KeyEsc
  293. case 0x2d:
  294. ike.Code = KeyInsert
  295. case 0x2e:
  296. ike.Code = KeyDelete
  297. case 0x24:
  298. ike.Code = KeyHome
  299. case 0x23:
  300. ike.Code = KeyEnd
  301. case 0x21:
  302. ike.Code = KeyPgup
  303. case 0x22:
  304. ike.Code = KeyPgdn
  305. case 0x26:
  306. ike.Code = KeyArrowUp
  307. case 0x28:
  308. ike.Code = KeyArrowDown
  309. case 0x25:
  310. ike.Code = KeyArrowLeft
  311. case 0x27:
  312. ike.Code = KeyArrowRight
  313. case 0x5b:
  314. ike.Code = KeyCommand
  315. }
  316. return ike
  317. }
  318. // OS specific magic
  319. // =================
  320. var kernel32 = syscall.NewLazyDLL("kernel32.dll")
  321. var syscallFunc = syscall.Syscall6
  322. /*
  323. createEvent creates an event object.
  324. https://msdn.microsoft.com/en-gb/library/windows/desktop/ms682396(v=vs.85).aspx
  325. */
  326. func createEvent() (syscall.Handle, error) {
  327. var err error
  328. r0, _, e1 := syscallFunc(kernel32.NewProc("CreateEventW").Addr(),
  329. 4, 0, 0, 0, 0, 0, 0)
  330. if int(r0) == 0 {
  331. if e1 != 0 {
  332. err = error(e1)
  333. } else {
  334. err = syscall.EINVAL
  335. }
  336. }
  337. return syscall.Handle(r0), err
  338. }
  339. /*
  340. waitForMultipleObjects waits (100ms) for an input event. Returns if an event was
  341. received and any encountered errors.
  342. https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
  343. */
  344. func waitForMultipleObjects(objects []syscall.Handle) (bool, error) {
  345. var err error
  346. r0, _, e1 := syscall.Syscall6(kernel32.NewProc("WaitForMultipleObjects").Addr(), 4,
  347. uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])), 0, 10, 0, 0)
  348. if uint32(r0) == 0xFFFFFFFF {
  349. if e1 != 0 {
  350. err = error(e1)
  351. } else {
  352. err = syscall.EINVAL
  353. }
  354. }
  355. return uint32(r0) != 0x00000102 && err == nil, err
  356. }
  357. /*
  358. winConsoleScreenBufferInfo is a CONSOLE_SCREEN_BUFFER_INFO which contains
  359. information about a console screen buffer.
  360. https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
  361. */
  362. type winConsoleScreenBufferInfo struct {
  363. size winCOORD
  364. cursorPosition winCOORD
  365. attributes uint16
  366. window struct {
  367. left int16
  368. top int16
  369. right int16
  370. bottom int16
  371. }
  372. maximumWindowSize winCOORD
  373. }
  374. var tempWinConsoleScreenBufferInfo = &winConsoleScreenBufferInfo{} // Temp space to prevent unnecessary heap allocations
  375. /*
  376. getConsoleScreenBufferInfo retrieves information about the specified console
  377. screen buffer.
  378. https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
  379. */
  380. func getConsoleScreenBufferInfo(h syscall.Handle) (*winConsoleScreenBufferInfo, error) {
  381. var err error
  382. var ret *winConsoleScreenBufferInfo
  383. r0, _, e1 := syscall.Syscall(kernel32.NewProc("GetConsoleScreenBufferInfo").Addr(), 2,
  384. uintptr(h), uintptr(unsafe.Pointer(tempWinConsoleScreenBufferInfo)), 0)
  385. if int(r0) == 0 {
  386. if e1 != 0 {
  387. err = error(e1)
  388. } else {
  389. err = syscall.EINVAL
  390. }
  391. } else {
  392. ret = tempWinConsoleScreenBufferInfo
  393. }
  394. return ret, err
  395. }
  396. /*
  397. winCOORD is COORD structure which defines the coordinates of a character cell
  398. in a console screen buffer.
  399. https://docs.microsoft.com/en-us/windows/console/coord-str
  400. */
  401. type winCOORD struct {
  402. x int16
  403. y int16
  404. }
  405. /*
  406. setConsoleCursorPosition sets the cursor position.
  407. https://docs.microsoft.com/en-us/windows/console/setconsolecursorposition
  408. */
  409. func setConsoleCursorPosition(h syscall.Handle, pos *winCOORD) error {
  410. var err error
  411. r0, _, e1 := syscall.Syscall(kernel32.NewProc("SetConsoleCursorPosition").Addr(), 2,
  412. uintptr(h), uintptr(*(*int32)(unsafe.Pointer(pos))), 0)
  413. if int(r0) == 0 {
  414. if e1 != 0 {
  415. err = error(e1)
  416. } else {
  417. err = syscall.EINVAL
  418. }
  419. }
  420. return err
  421. }
  422. /*
  423. inputRecord is a record read with readConsoleInput
  424. https://docs.microsoft.com/en-us/windows/console/input-record-str
  425. */
  426. type inputRecord struct {
  427. eventType uint16
  428. _ [2]byte
  429. event [16]byte
  430. }
  431. /*
  432. keyEventWin interprets the event from inputRecord as a windows key event
  433. https://docs.microsoft.com/en-us/windows/console/key-event-record-str
  434. */
  435. type winKeyEvent struct {
  436. keyDown int32 // key is pressed
  437. repeatCount uint16 // repeat count, which indicates that a key is being held down
  438. virtualKeyCode uint16 // identifies the given key in a device-independent manner
  439. virtualScanCode uint16 // represents the device-dependent value generated by the keyboard hardware
  440. unicodeChar uint16 // translated Unicode character
  441. controlKeyState uint32 // state of the control keys
  442. }
  443. var temp uint32 // Temp space to prevent unnecessary heap allocations
  444. /*
  445. readConsoleInput reads data from a console input buffer.
  446. https://docs.microsoft.com/en-us/windows/console/readconsoleinput
  447. */
  448. func readConsoleInput(h syscall.Handle, ir *inputRecord) error {
  449. var err error
  450. r0, _, e1 := syscall.Syscall6(kernel32.NewProc("ReadConsoleInputW").Addr(),
  451. 4, uintptr(h), uintptr(unsafe.Pointer(ir)), 1, uintptr(unsafe.Pointer(&temp)), 0, 0)
  452. if int(r0) == 0 {
  453. if e1 != 0 {
  454. err = error(e1)
  455. } else {
  456. err = syscall.EINVAL
  457. }
  458. }
  459. return err
  460. }