...

Source file src/github.com/mattn/go-tty/tty_unix.go

Documentation: github.com/mattn/go-tty

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

View as plain text