tty_unix.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // +build !windows
  2. // +build !plan9
  3. package tty
  4. import (
  5. "bufio"
  6. "os"
  7. "os/signal"
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/sys/unix"
  11. )
  12. type TTY struct {
  13. in *os.File
  14. bin *bufio.Reader
  15. out *os.File
  16. termios syscall.Termios
  17. ws chan WINSIZE
  18. ss chan os.Signal
  19. }
  20. func open() (*TTY, error) {
  21. tty := new(TTY)
  22. in, err := os.Open("/dev/tty")
  23. if err != nil {
  24. return nil, err
  25. }
  26. tty.in = in
  27. tty.bin = bufio.NewReader(in)
  28. out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
  29. if err != nil {
  30. return nil, err
  31. }
  32. tty.out = out
  33. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 {
  34. return nil, err
  35. }
  36. newios := tty.termios
  37. newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
  38. newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/
  39. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 {
  40. return nil, err
  41. }
  42. tty.ws = make(chan WINSIZE)
  43. tty.ss = make(chan os.Signal, 1)
  44. signal.Notify(tty.ss, syscall.SIGWINCH)
  45. go func() {
  46. for sig := range tty.ss {
  47. switch sig {
  48. case syscall.SIGWINCH:
  49. if w, h, err := tty.size(); err == nil {
  50. tty.ws <- WINSIZE{
  51. W: w,
  52. H: h,
  53. }
  54. }
  55. default:
  56. }
  57. }
  58. }()
  59. return tty, nil
  60. }
  61. func (tty *TTY) buffered() bool {
  62. return tty.bin.Buffered() > 0
  63. }
  64. func (tty *TTY) readRune() (rune, error) {
  65. r, _, err := tty.bin.ReadRune()
  66. return r, err
  67. }
  68. func (tty *TTY) close() error {
  69. close(tty.ss)
  70. close(tty.ws)
  71. _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0)
  72. return err
  73. }
  74. func (tty *TTY) size() (int, int, error) {
  75. var dim [4]uint16
  76. if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 {
  77. return -1, -1, err
  78. }
  79. return int(dim[1]), int(dim[0]), nil
  80. }
  81. func (tty *TTY) input() *os.File {
  82. return tty.in
  83. }
  84. func (tty *TTY) output() *os.File {
  85. return tty.out
  86. }
  87. func (tty *TTY) raw() (func() error, error) {
  88. termios, err := unix.IoctlGetTermios(int(tty.in.Fd()), ioctlReadTermios)
  89. if err != nil {
  90. return nil, err
  91. }
  92. termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
  93. termios.Oflag &^= unix.OPOST
  94. termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
  95. termios.Cflag &^= unix.CSIZE | unix.PARENB
  96. termios.Cflag |= unix.CS8
  97. termios.Cc[unix.VMIN] = 1
  98. termios.Cc[unix.VTIME] = 0
  99. if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil {
  100. return nil, err
  101. }
  102. return func() error {
  103. if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil {
  104. return err
  105. }
  106. return nil
  107. }, nil
  108. }
  109. func (tty *TTY) sigwinch() chan WINSIZE {
  110. return tty.ws
  111. }