util_linux.go 19 KB

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