github.com/pmorton/docker@v1.5.0/pkg/term/term.go (about)

     1  // +build !windows
     2  
     3  package term
     4  
     5  import (
     6  	"errors"
     7  	"os"
     8  	"os/signal"
     9  	"syscall"
    10  	"unsafe"
    11  )
    12  
    13  var (
    14  	ErrInvalidState = errors.New("Invalid terminal state")
    15  )
    16  
    17  type State struct {
    18  	termios Termios
    19  }
    20  
    21  type Winsize struct {
    22  	Height uint16
    23  	Width  uint16
    24  	x      uint16
    25  	y      uint16
    26  }
    27  
    28  func GetWinsize(fd uintptr) (*Winsize, error) {
    29  	ws := &Winsize{}
    30  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
    31  	// Skipp errno = 0
    32  	if err == 0 {
    33  		return ws, nil
    34  	}
    35  	return ws, err
    36  }
    37  
    38  func SetWinsize(fd uintptr, ws *Winsize) error {
    39  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
    40  	// Skipp errno = 0
    41  	if err == 0 {
    42  		return nil
    43  	}
    44  	return err
    45  }
    46  
    47  // IsTerminal returns true if the given file descriptor is a terminal.
    48  func IsTerminal(fd uintptr) bool {
    49  	var termios Termios
    50  	return tcget(fd, &termios) == 0
    51  }
    52  
    53  // Restore restores the terminal connected to the given file descriptor to a
    54  // previous state.
    55  func RestoreTerminal(fd uintptr, state *State) error {
    56  	if state == nil {
    57  		return ErrInvalidState
    58  	}
    59  	if err := tcset(fd, &state.termios); err != 0 {
    60  		return err
    61  	}
    62  	return nil
    63  }
    64  
    65  func SaveState(fd uintptr) (*State, error) {
    66  	var oldState State
    67  	if err := tcget(fd, &oldState.termios); err != 0 {
    68  		return nil, err
    69  	}
    70  
    71  	return &oldState, nil
    72  }
    73  
    74  func DisableEcho(fd uintptr, state *State) error {
    75  	newState := state.termios
    76  	newState.Lflag &^= syscall.ECHO
    77  
    78  	if err := tcset(fd, &newState); err != 0 {
    79  		return err
    80  	}
    81  	handleInterrupt(fd, state)
    82  	return nil
    83  }
    84  
    85  func SetRawTerminal(fd uintptr) (*State, error) {
    86  	oldState, err := MakeRaw(fd)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	handleInterrupt(fd, oldState)
    91  	return oldState, err
    92  }
    93  
    94  func handleInterrupt(fd uintptr, state *State) {
    95  	sigchan := make(chan os.Signal, 1)
    96  	signal.Notify(sigchan, os.Interrupt)
    97  
    98  	go func() {
    99  		_ = <-sigchan
   100  		RestoreTerminal(fd, state)
   101  		os.Exit(0)
   102  	}()
   103  }