runereader_posix.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // +build !windows
  2. // The terminal mode manipulation code is derived heavily from:
  3. // https://github.com/golang/crypto/blob/master/ssh/terminal/util.go:
  4. // Copyright 2011 The Go Authors. All rights reserved.
  5. // Use of this source code is governed by a BSD-style
  6. // license that can be found in the LICENSE file.
  7. package terminal
  8. import (
  9. "bufio"
  10. "bytes"
  11. "fmt"
  12. "syscall"
  13. "unsafe"
  14. )
  15. type runeReaderState struct {
  16. term syscall.Termios
  17. reader *bufio.Reader
  18. buf *bytes.Buffer
  19. }
  20. func newRuneReaderState(input FileReader) runeReaderState {
  21. buf := new(bytes.Buffer)
  22. return runeReaderState{
  23. reader: bufio.NewReader(&BufferedReader{
  24. In: input,
  25. Buffer: buf,
  26. }),
  27. buf: buf,
  28. }
  29. }
  30. func (rr *RuneReader) Buffer() *bytes.Buffer {
  31. return rr.state.buf
  32. }
  33. // For reading runes we just want to disable echo.
  34. func (rr *RuneReader) SetTermMode() error {
  35. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
  36. return err
  37. }
  38. newState := rr.state.term
  39. newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG
  40. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
  41. return err
  42. }
  43. return nil
  44. }
  45. func (rr *RuneReader) RestoreTermMode() error {
  46. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
  47. return err
  48. }
  49. return nil
  50. }
  51. func (rr *RuneReader) ReadRune() (rune, int, error) {
  52. r, size, err := rr.state.reader.ReadRune()
  53. if err != nil {
  54. return r, size, err
  55. }
  56. // parse ^[ sequences to look for arrow keys
  57. if r == '\033' {
  58. if rr.state.reader.Buffered() == 0 {
  59. // no more characters so must be `Esc` key
  60. return KeyEscape, 1, nil
  61. }
  62. r, size, err = rr.state.reader.ReadRune()
  63. if err != nil {
  64. return r, size, err
  65. }
  66. if r != '[' {
  67. return r, size, fmt.Errorf("Unexpected Escape Sequence: %q", []rune{'\033', r})
  68. }
  69. r, size, err = rr.state.reader.ReadRune()
  70. if err != nil {
  71. return r, size, err
  72. }
  73. switch r {
  74. case 'D':
  75. return KeyArrowLeft, 1, nil
  76. case 'C':
  77. return KeyArrowRight, 1, nil
  78. case 'A':
  79. return KeyArrowUp, 1, nil
  80. case 'B':
  81. return KeyArrowDown, 1, nil
  82. case 'H': // Home button
  83. return SpecialKeyHome, 1, nil
  84. case 'F': // End button
  85. return SpecialKeyEnd, 1, nil
  86. case '3': // Delete Button
  87. // discard the following '~' key from buffer
  88. rr.state.reader.Discard(1)
  89. return SpecialKeyDelete, 1, nil
  90. default:
  91. // discard the following '~' key from buffer
  92. rr.state.reader.Discard(1)
  93. return IgnoreKey, 1, nil
  94. }
  95. return r, size, fmt.Errorf("Unknown Escape Sequence: %q", []rune{'\033', '[', r})
  96. }
  97. return r, size, err
  98. }