runereader_posix.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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. "fmt"
  11. "os"
  12. "syscall"
  13. "unsafe"
  14. )
  15. type runeReaderState struct {
  16. term syscall.Termios
  17. buf *bufio.Reader
  18. }
  19. func newRuneReaderState(input *os.File) runeReaderState {
  20. return runeReaderState{
  21. buf: bufio.NewReader(input),
  22. }
  23. }
  24. // For reading runes we just want to disable echo.
  25. func (rr *RuneReader) SetTermMode() error {
  26. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.Input.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
  27. return err
  28. }
  29. newState := rr.state.term
  30. newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG
  31. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.Input.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
  32. return err
  33. }
  34. return nil
  35. }
  36. func (rr *RuneReader) RestoreTermMode() error {
  37. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.Input.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
  38. return err
  39. }
  40. return nil
  41. }
  42. func (rr *RuneReader) ReadRune() (rune, int, error) {
  43. r, size, err := rr.state.buf.ReadRune()
  44. if err != nil {
  45. return r, size, err
  46. }
  47. // parse ^[ sequences to look for arrow keys
  48. if r == '\033' {
  49. r, size, err = rr.state.buf.ReadRune()
  50. if err != nil {
  51. return r, size, err
  52. }
  53. if r != '[' {
  54. return r, size, fmt.Errorf("Unexpected Escape Sequence: %q", []rune{'\033', r})
  55. }
  56. r, size, err = rr.state.buf.ReadRune()
  57. if err != nil {
  58. return r, size, err
  59. }
  60. switch r {
  61. case 'D':
  62. return KeyArrowLeft, 1, nil
  63. case 'C':
  64. return KeyArrowRight, 1, nil
  65. case 'A':
  66. return KeyArrowUp, 1, nil
  67. case 'B':
  68. return KeyArrowDown, 1, nil
  69. }
  70. return r, size, fmt.Errorf("Unknown Escape Sequence: %q", []rune{'\033', '[', r})
  71. }
  72. return r, size, err
  73. }