util_linux.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  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. "fmt"
  17. "os"
  18. "os/signal"
  19. "strconv"
  20. "strings"
  21. "sync"
  22. "syscall"
  23. "time"
  24. "unicode/utf8"
  25. "unsafe"
  26. )
  27. /*
  28. attachReader attaches a character reader on Windows.
  29. */
  30. func attachReader() (getch, error) {
  31. // Detect the current terminal
  32. keys, err := detectTerminal()
  33. if err == nil {
  34. var inFd int
  35. // Open the tty for reading and signals
  36. inFd, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
  37. if err == nil {
  38. var out *os.File
  39. // Open a file descriptor on tty to perform ioctl calls
  40. out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
  41. if err == nil {
  42. gl := &getchLinux{make(chan os.Signal), inFd, out, keys, syscall.Termios{},
  43. make(chan *internalKeyEvent), make(chan bool), &sync.WaitGroup{}}
  44. in := uintptr(gl.inFd)
  45. // Notify signal channel when I/O is possible
  46. // Signal: SIGIO 29 I/O now possible (4.2 BSD)
  47. signal.Notify(gl.sigio, syscall.SIGIO)
  48. // Set the file status flag for /dev/tty to
  49. // O_ASYNC Enable generation of signals on the file descriptor
  50. // O_NONBLOCK Do not wait for input
  51. err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
  52. if err == nil {
  53. // Set the process ID that will receive SIGIO signals for events
  54. // on the file descriptor in.
  55. err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
  56. if err == nil {
  57. // Read the current serial port settings (terminal attributes)
  58. err = ioctl(gl.out.Fd(), syscall.TCGETS, &gl.origTios)
  59. // Reconfigure terminal attributes - see Linux termios
  60. tios := gl.origTios
  61. // Unsetting the following input mode flags means:
  62. // IGNBRK Don't ignore BREAK condition on input
  63. // BRKINT Breaks don't cause SIGINT to be send and read as \0
  64. // PARMRK Bytes with parity or framing errors are not marked
  65. // ISTRIP Do not strip off the eighth bit
  66. // INLCR Do not translate newline to carriage return on input
  67. // IGNCR No not ignore carriage return on input
  68. // ICRNL Do not translate carriage return to newline on input
  69. // IXON Do not enable XON/XOFF flow control on input
  70. tios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK |
  71. syscall.ISTRIP | syscall.INLCR | syscall.IGNCR |
  72. syscall.ICRNL | syscall.IXON
  73. // Unsetting the following local mode flags means:
  74. // ECHO Do not echo input characters
  75. // ECHONL Do not echo newline characters
  76. // ICANON Do not operate in canonical mode - i.e. no line buffering
  77. // ISIG Do not generate signals when receiving either INTR, QUIT,
  78. // SUSP, or DSUSP characters
  79. // IEXTEN Do not enable implementation-defined input processing
  80. tios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON |
  81. syscall.ISIG | syscall.IEXTEN
  82. // Unsetting the following control mode flags means:
  83. // CSIZE Clear any character size mask
  84. // PARENB Do not enable parity generation on output and
  85. // parity checking for input
  86. tios.Cflag &^= syscall.CSIZE | syscall.PARENB
  87. // Set character size mask 8 bit
  88. tios.Cflag |= syscall.CS8
  89. // Set minimum number of characters for noncanonical read
  90. tios.Cc[syscall.VMIN] = 1
  91. // Set timeout in deciseconds for noncanonical read
  92. tios.Cc[syscall.VTIME] = 0
  93. err = ioctl(gl.out.Fd(), syscall.TCSETS, &tios)
  94. if err == nil {
  95. // All is well we can start listening for events
  96. go gl.eventListener()
  97. return gl, nil
  98. }
  99. }
  100. }
  101. }
  102. }
  103. }
  104. return nil, err
  105. }
  106. // Getch object implementation for linux
  107. // =====================================
  108. /*
  109. getchLinux is the Linux based getch implementation.
  110. */
  111. type getchLinux struct {
  112. sigio chan os.Signal // Channel to receive input signals
  113. inFd int // Open file descriptor of /dev/tty (reading / signals)
  114. out *os.File // Open file descriptor on /dev/tty (setting attributes)
  115. escKeys []string // Escape sequences for non-printable keys
  116. origTios syscall.Termios // Original terminal attributes
  117. keyEventChan chan *internalKeyEvent // Channel for input events
  118. stopChan chan bool // Channel for shutdown event
  119. wg *sync.WaitGroup // Waitgroup for shutdown process
  120. }
  121. var posBuf = make([]byte, 20)
  122. /*
  123. CursorPosition returns the current cursor position.
  124. */
  125. func (gl *getchLinux) CursorPosition() (int, int, error) {
  126. var n, x, y int
  127. var err error
  128. // Do a normal terminal communication via stdin and stdout
  129. gl.out.WriteString("\033[6n") // Ask via ANSI escape code
  130. if n, err = os.Stdin.Read(posBuf); n > 2 && err == nil {
  131. if res := string(posBuf[:n]); res[:2] == "\033[" {
  132. // Parse the result
  133. posStr := strings.Split(res[2:n-1], ";")
  134. if len(posStr) == 2 {
  135. if y, err = strconv.Atoi(posStr[0]); err == nil {
  136. x, err = strconv.Atoi(posStr[1])
  137. }
  138. }
  139. }
  140. }
  141. return x - 1, y, err
  142. }
  143. /*
  144. SetCursorPosition sets the current cursor position
  145. */
  146. func (gl *getchLinux) SetCursorPosition(x, y int) error {
  147. // Set position via ANSI escape code
  148. gl.out.WriteString("\033[")
  149. gl.out.WriteString(fmt.Sprint(y))
  150. gl.out.WriteString(";")
  151. gl.out.WriteString(fmt.Sprint(x + 1))
  152. gl.out.WriteString("H")
  153. return nil
  154. }
  155. /*
  156. GetKey returns the next key event or an error. This function blocks if no key
  157. event is available.
  158. */
  159. func (gl *getchLinux) GetKey() *internalKeyEvent {
  160. return <-gl.keyEventChan
  161. }
  162. /*
  163. GetKeyAsync returns the next key event or an error. This function does not block if no key
  164. event is available - in this case nil is returned.
  165. */
  166. func (gl *getchLinux) GetKeyAsync() *internalKeyEvent {
  167. select {
  168. case e := <-gl.keyEventChan:
  169. return e
  170. default:
  171. }
  172. return nil
  173. }
  174. /*
  175. Close detaches the character reader.
  176. */
  177. func (gl *getchLinux) Close() {
  178. // Send stop command and wait for the eventListener thread to end
  179. gl.wg.Add(1)
  180. // Empty all pending keys
  181. for gl.GetKeyAsync() != nil {
  182. time.Sleep(10 * time.Millisecond)
  183. }
  184. gl.stopChan <- true
  185. gl.wg.Wait()
  186. // Reset the original terminal state
  187. ioctl(gl.out.Fd(), syscall.TCSETS, &gl.origTios)
  188. // Ignoring errors here since we are closing
  189. gl.out.Close()
  190. syscall.Close(gl.inFd)
  191. }
  192. /*
  193. eventListener is a thread which will listen for events.
  194. */
  195. func (gl *getchLinux) eventListener() {
  196. // Create input buffer
  197. buf := make([]byte, 128)
  198. // Loop until an exit is requested
  199. loop:
  200. for true {
  201. select { // Wait for input event or shutdown - block until one is received
  202. case <-gl.sigio: // Check for input event
  203. for {
  204. n, err := syscall.Read(gl.inFd, buf)
  205. if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
  206. break // Nothing more to read - wait for more
  207. } else if err != nil {
  208. gl.keyEventChan <- &internalKeyEvent{err: err}
  209. }
  210. // Either stop or send the event - block if the channel is full
  211. select {
  212. case <-gl.stopChan:
  213. break loop
  214. case gl.keyEventChan <- gl.buildKeyEvent(buf[:n]):
  215. continue
  216. }
  217. }
  218. case <-gl.stopChan: // Check for shutdown event
  219. break loop
  220. }
  221. }
  222. gl.wg.Done()
  223. }
  224. /*
  225. buildKeyEvent builds an internalKeyEvent from a terminal input.
  226. */
  227. func (gl *getchLinux) buildKeyEvent(buf []byte) *internalKeyEvent {
  228. ike := &internalKeyEvent{&KeyEvent{}, nil}
  229. if len(buf) > 0 {
  230. bufstr := string(buf)
  231. // Check for escape sequence
  232. if buf[0] == '\033' && len(buf) > 1 {
  233. for i, ek := range gl.escKeys {
  234. if strings.HasPrefix(bufstr, ek) {
  235. // Found a known termkey - set the code
  236. termkey := termKey(0xFFFF - i)
  237. // Codes for non-printable keys
  238. ike.Rune = 0x00
  239. switch termkey {
  240. case termKeyF1:
  241. ike.Code = KeyF1
  242. case termKeyF2:
  243. ike.Code = KeyF2
  244. case termKeyF3:
  245. ike.Code = KeyF3
  246. case termKeyF4:
  247. ike.Code = KeyF4
  248. case termKeyF5:
  249. ike.Code = KeyF5
  250. case termKeyF6:
  251. ike.Code = KeyF6
  252. case termKeyF7:
  253. ike.Code = KeyF7
  254. case termKeyF8:
  255. ike.Code = KeyF8
  256. case termKeyF9:
  257. ike.Code = KeyF9
  258. case termKeyF10:
  259. ike.Code = KeyF10
  260. case termKeyF11:
  261. ike.Code = KeyF11
  262. case termKeyF12:
  263. ike.Code = KeyF12
  264. case termKeyInsert:
  265. ike.Code = KeyInsert
  266. case termKeyDelete:
  267. ike.Code = KeyDelete
  268. case termKeyHome:
  269. ike.Code = KeyHome
  270. case termKeyHome1:
  271. ike.Code = KeyHome
  272. case termKeyEnd:
  273. ike.Code = KeyEnd
  274. case termKeyEnd1:
  275. ike.Code = KeyEnd
  276. case termKeyPgup:
  277. ike.Code = KeyPgup
  278. case termKeyPgdn:
  279. ike.Code = KeyPgdn
  280. case termKeyArrowUp:
  281. ike.Code = KeyArrowUp
  282. case termKeyArrowDown:
  283. ike.Code = KeyArrowDown
  284. case termKeyArrowLeft:
  285. ike.Code = KeyArrowLeft
  286. case termKeyArrowRight:
  287. ike.Code = KeyArrowRight
  288. }
  289. }
  290. }
  291. if ike.KeyEvent.Code == "" {
  292. // Escape sequence was not detected
  293. ike.err = &ErrUnknownEscapeSequence{buf}
  294. }
  295. } else {
  296. // Not an escape sequence
  297. de, deSize := utf8.DecodeRune(buf)
  298. if len(buf) > deSize {
  299. // There was more than one character - append the raw input buffer
  300. ike.RawBuf = make([]byte, len(buf))
  301. copy(ike.RawBuf, buf)
  302. }
  303. // Check for printable character
  304. ike.Rune = de
  305. switch de {
  306. case 'a': // Letters:
  307. ike.Code = KeyA
  308. case 'b':
  309. ike.Code = KeyB
  310. case 'c':
  311. ike.Code = KeyC
  312. case 'd':
  313. ike.Code = KeyD
  314. case 'e':
  315. ike.Code = KeyE
  316. case 'f':
  317. ike.Code = KeyF
  318. case 'g':
  319. ike.Code = KeyG
  320. case 'h':
  321. ike.Code = KeyH
  322. case 'i':
  323. ike.Code = KeyI
  324. case 'j':
  325. ike.Code = KeyJ
  326. case 'k':
  327. ike.Code = KeyK
  328. case 'l':
  329. ike.Code = KeyL
  330. case 'm':
  331. ike.Code = KeyM
  332. case 'n':
  333. ike.Code = KeyN
  334. case 'o':
  335. ike.Code = KeyO
  336. case 'p':
  337. ike.Code = KeyP
  338. case 'q':
  339. ike.Code = KeyQ
  340. case 'r':
  341. ike.Code = KeyR
  342. case 's':
  343. ike.Code = KeyS
  344. case 't':
  345. ike.Code = KeyT
  346. case 'u':
  347. ike.Code = KeyU
  348. case 'v':
  349. ike.Code = KeyV
  350. case 'w':
  351. ike.Code = KeyW
  352. case 'x':
  353. ike.Code = KeyX
  354. case 'y':
  355. ike.Code = KeyY
  356. case 'z':
  357. ike.Code = KeyZ
  358. case '0': // Numbers
  359. ike.Code = Key0
  360. case '1':
  361. ike.Code = Key1
  362. case '2':
  363. ike.Code = Key2
  364. case '3':
  365. ike.Code = Key3
  366. case '4':
  367. ike.Code = Key4
  368. case '5':
  369. ike.Code = Key5
  370. case '6':
  371. ike.Code = Key6
  372. case '7':
  373. ike.Code = Key7
  374. case '8':
  375. ike.Code = Key8
  376. case '9':
  377. ike.Code = Key9
  378. /*
  379. The following symbols are returned as an Key_UNKNOWN
  380. as they depend on the actual keyboard layout
  381. case '`': // Symbols
  382. ike.Code = KeyBacktick
  383. case '-':
  384. ike.Code = KeyMinus
  385. case '=':
  386. ike.Code = KeyEqual
  387. case '\\':
  388. ike.Code = KeyBackslash
  389. case ',':
  390. ike.Code = KeyComma
  391. case '.':
  392. ike.Code = KeyDot
  393. case '/':
  394. ike.Code = KeySlash
  395. case ';':
  396. ike.Code = KeySemiColon
  397. case '\'':
  398. ike.Code = KeyQuote
  399. case '#':
  400. ike.Code = KeyHash
  401. case ']':
  402. ike.Code = KeyBracketOpen
  403. case '[':
  404. ike.Code = KeyBracketClose
  405. */
  406. case 'A': // Letters:
  407. ike.Code = KeyA
  408. ike.Shift = true
  409. case 'B':
  410. ike.Code = KeyB
  411. ike.Shift = true
  412. case 'C':
  413. ike.Code = KeyC
  414. ike.Shift = true
  415. case 'D':
  416. ike.Code = KeyD
  417. ike.Shift = true
  418. case 'E':
  419. ike.Code = KeyE
  420. ike.Shift = true
  421. case 'F':
  422. ike.Code = KeyF
  423. ike.Shift = true
  424. case 'G':
  425. ike.Code = KeyG
  426. ike.Shift = true
  427. case 'H':
  428. ike.Code = KeyH
  429. ike.Shift = true
  430. case 'I':
  431. ike.Code = KeyI
  432. ike.Shift = true
  433. case 'J':
  434. ike.Code = KeyJ
  435. ike.Shift = true
  436. case 'K':
  437. ike.Code = KeyK
  438. ike.Shift = true
  439. case 'L':
  440. ike.Code = KeyL
  441. ike.Shift = true
  442. case 'M':
  443. ike.Code = KeyM
  444. ike.Shift = true
  445. case 'N':
  446. ike.Code = KeyN
  447. ike.Shift = true
  448. case 'O':
  449. ike.Code = KeyO
  450. ike.Shift = true
  451. case 'P':
  452. ike.Code = KeyP
  453. ike.Shift = true
  454. case 'Q':
  455. ike.Code = KeyQ
  456. ike.Shift = true
  457. case 'R':
  458. ike.Code = KeyR
  459. ike.Shift = true
  460. case 'S':
  461. ike.Code = KeyS
  462. ike.Shift = true
  463. case 'T':
  464. ike.Code = KeyT
  465. ike.Shift = true
  466. case 'U':
  467. ike.Code = KeyU
  468. ike.Shift = true
  469. case 'V':
  470. ike.Code = KeyV
  471. ike.Shift = true
  472. case 'W':
  473. ike.Code = KeyW
  474. ike.Shift = true
  475. case 'X':
  476. ike.Code = KeyX
  477. ike.Shift = true
  478. case 'Y':
  479. ike.Code = KeyY
  480. ike.Shift = true
  481. case 'Z':
  482. ike.Code = KeyZ
  483. ike.Shift = true
  484. case 0x01:
  485. ike.Code = KeyA
  486. ike.Ctrl = true
  487. case 0x02:
  488. ike.Code = KeyB
  489. ike.Ctrl = true
  490. case 0x03:
  491. ike.Code = KeyC
  492. ike.Ctrl = true
  493. case 0x04:
  494. ike.Code = KeyD
  495. ike.Ctrl = true
  496. case 0x05:
  497. ike.Code = KeyE
  498. ike.Ctrl = true
  499. case 0x06:
  500. ike.Code = KeyF
  501. ike.Ctrl = true
  502. case 0x07:
  503. ike.Code = KeyG
  504. ike.Ctrl = true
  505. case 0x08:
  506. ike.Code = KeyH
  507. ike.Ctrl = true
  508. case 0x09:
  509. ike.Code = KeyI
  510. ike.Ctrl = true
  511. case 0x0A:
  512. ike.Code = KeyJ
  513. ike.Ctrl = true
  514. case 0x0B:
  515. ike.Code = KeyK
  516. ike.Ctrl = true
  517. case 0x0C:
  518. ike.Code = KeyL
  519. ike.Ctrl = true
  520. case 0x0D:
  521. ike.Code = KeyM
  522. ike.Ctrl = true
  523. case 0x0E:
  524. ike.Code = KeyN
  525. ike.Ctrl = true
  526. case 0x0F:
  527. ike.Code = KeyO
  528. ike.Ctrl = true
  529. case 0x10:
  530. ike.Code = KeyP
  531. ike.Ctrl = true
  532. case 0x11:
  533. ike.Code = KeyQ
  534. ike.Ctrl = true
  535. case 0x12:
  536. ike.Code = KeyR
  537. ike.Ctrl = true
  538. case 0x13:
  539. ike.Code = KeyS
  540. ike.Ctrl = true
  541. case 0x14:
  542. ike.Code = KeyT
  543. ike.Ctrl = true
  544. case 0x15:
  545. ike.Code = KeyU
  546. ike.Ctrl = true
  547. case 0x16:
  548. ike.Code = KeyV
  549. ike.Ctrl = true
  550. case 0x17:
  551. ike.Code = KeyW
  552. ike.Ctrl = true
  553. case 0x18:
  554. ike.Code = KeyX
  555. ike.Ctrl = true
  556. case 0x19:
  557. ike.Code = KeyY
  558. ike.Ctrl = true
  559. case 0x1a:
  560. ike.Code = KeyZ
  561. ike.Ctrl = true
  562. default:
  563. ike.Code = KeyUnknown
  564. }
  565. // Check for non-printable keys
  566. // Note: KeyCommand was not recognised
  567. switch de {
  568. case '\t':
  569. ike.Code = KeyTab
  570. ike.Rune = 0x00
  571. case '\r':
  572. ike.Code = KeyEnter
  573. ike.Rune = 0x00
  574. case 127:
  575. ike.Code = KeyBackspace
  576. ike.Rune = 0x00
  577. case 27:
  578. ike.Code = KeyEsc
  579. ike.Rune = 0x00
  580. }
  581. }
  582. }
  583. return ike
  584. }
  585. // Escape sequences for common terminals
  586. // =====================================
  587. /*
  588. termKey models a linux terminal key
  589. */
  590. type termKey uint16
  591. /*
  592. Recognized escape sequences
  593. */
  594. const (
  595. termKeyF1 termKey = 0xFFFF - iota
  596. termKeyF2
  597. termKeyF3
  598. termKeyF4
  599. termKeyF5
  600. termKeyF6
  601. termKeyF7
  602. termKeyF8
  603. termKeyF9
  604. termKeyF10
  605. termKeyF11
  606. termKeyF12
  607. termKeyInsert
  608. termKeyDelete
  609. termKeyHome
  610. termKeyHome1 // Alternative key
  611. termKeyEnd
  612. termKeyEnd1 // Alternative key
  613. termKeyPgup
  614. termKeyPgdn
  615. termKeyArrowUp
  616. termKeyArrowUp1 // Alternative key
  617. termKeyArrowDown
  618. termKeyArrowDown1 // Alternative key
  619. termKeyArrowLeft
  620. termKeyArrowLeft1 // Alternative key
  621. termKeyArrowRight
  622. termKeyArrowRight1 // Alternative key
  623. )
  624. /*
  625. Mappings from terminal type to escape sequence for a particular termKey (see above).
  626. */
  627. var (
  628. etermKeys = []string{
  629. "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~",
  630. "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
  631. "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~",
  632. "\x1b[7~", "\x1b[8~", "\x1b[8~", "\x1b[5~", "\x1b[6~",
  633. "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
  634. "\x1b[C", "\x1bOC",
  635. }
  636. screenKeys = []string{
  637. "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~",
  638. "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~",
  639. "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[1~", "\x1b[4~",
  640. "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1bOA", "\x1b[B",
  641. "\x1bOB", "\x1b[D", "\x1bOD", "\x1b[C", "\x1bOC",
  642. }
  643. xtermKeys = []string{
  644. "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~",
  645. "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~",
  646. "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[H", "\x1bOH", "\x1b[F",
  647. "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1bOA", "\x1b[B",
  648. "\x1bOB", "\x1b[D", "\x1bOD", "\x1b[C", "\x1bOC",
  649. }
  650. rxvtKeys = []string{
  651. "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~",
  652. "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
  653. "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~",
  654. "\x1b[7~", "\x1b[8~", "\x1b[8~", "\x1b[5~", "\x1b[6~",
  655. "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
  656. "\x1b[C", "\x1bOC",
  657. }
  658. linuxKeys = []string{
  659. "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E",
  660. "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~",
  661. "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~",
  662. "\x1b[1~", "\x1b[4~", "\x1b[4~", "\x1b[5~", "\x1b[6~",
  663. "\x1b[A", "\x1bOA", "\x1b[B", "\x1bOB", "\x1b[D", "\x1bOD",
  664. "\x1b[C", "\x1bOC",
  665. }
  666. DefaultTermMappings = []struct {
  667. name string
  668. keys []string
  669. }{
  670. {"Eterm", etermKeys},
  671. {"screen", screenKeys},
  672. {"xterm", xtermKeys},
  673. {"xterm-256color", xtermKeys},
  674. {"rxvt-unicode", rxvtKeys},
  675. {"rxvt-256color", rxvtKeys},
  676. {"linux", linuxKeys},
  677. }
  678. CompatibilityTermMappings = []struct {
  679. namePart string
  680. keys []string
  681. }{
  682. {"xterm", xtermKeys},
  683. {"rxvt", rxvtKeys},
  684. {"linux", linuxKeys},
  685. {"Eterm", etermKeys},
  686. {"screen", screenKeys},
  687. {"cygwin", xtermKeys},
  688. {"st", xtermKeys},
  689. }
  690. )
  691. /*
  692. detectTerminal detects the current terminal and returns the appropriate escape
  693. sequence mapping for termKeys.
  694. */
  695. func detectTerminal() ([]string, error) {
  696. var keys []string
  697. name := os.Getenv("TERM")
  698. if name == "" {
  699. return nil, fmt.Errorf("Cannot determine terminal - TERM environment variable not set")
  700. }
  701. err := fmt.Errorf("Terminal %s is not supported", name)
  702. // Look for the right mapping for the current terminal
  703. for _, t := range DefaultTermMappings {
  704. if name == t.name {
  705. keys = t.keys
  706. err = nil
  707. break
  708. }
  709. }
  710. if keys == nil {
  711. // Try compatibility mappings if there was no direct match
  712. for _, t := range CompatibilityTermMappings {
  713. if strings.Contains(name, t.namePart) {
  714. keys = t.keys
  715. err = nil
  716. break
  717. }
  718. }
  719. }
  720. return keys, err
  721. }
  722. // Util functions
  723. // ==============
  724. /*
  725. fcntl does an OS system call of the same name to manipulate a file descriptor.
  726. */
  727. func fcntl(fd uintptr, cmd int, arg int) error {
  728. var err error
  729. _, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
  730. uintptr(arg))
  731. // Follow convention of syscall.Errno to convert from Errno to error
  732. if e != 0 {
  733. err = os.NewSyscallError("SYS_FCNTL", e)
  734. }
  735. return err
  736. }
  737. /*
  738. ioctl does an OS system call of the same name to manipulates the underlying
  739. device parameters of special files such as terminals.
  740. */
  741. func ioctl(fd uintptr, cmd int, termios *syscall.Termios) error {
  742. var err error
  743. r, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(cmd),
  744. uintptr(unsafe.Pointer(termios)))
  745. if r != 0 {
  746. err = os.NewSyscallError("SYS_IOCTL", e)
  747. }
  748. return err
  749. }