github.com/portworx/docker@v1.12.1/pkg/term/term.go (about) 1 // +build !windows 2 3 // Package term provides provides structures and helper functions to work with 4 // terminal (state, sizes). 5 package term 6 7 import ( 8 "errors" 9 "io" 10 "os" 11 "os/signal" 12 "syscall" 13 ) 14 15 var ( 16 // ErrInvalidState is returned if the state of the terminal is invalid. 17 ErrInvalidState = errors.New("Invalid terminal state") 18 ) 19 20 // State represents the state of the terminal. 21 type State struct { 22 termios Termios 23 } 24 25 // Winsize represents the size of the terminal window. 26 type Winsize struct { 27 Height uint16 28 Width uint16 29 x uint16 30 y uint16 31 } 32 33 // StdStreams returns the standard streams (stdin, stdout, stedrr). 34 func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { 35 return os.Stdin, os.Stdout, os.Stderr 36 } 37 38 // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. 39 func GetFdInfo(in interface{}) (uintptr, bool) { 40 var inFd uintptr 41 var isTerminalIn bool 42 if file, ok := in.(*os.File); ok { 43 inFd = file.Fd() 44 isTerminalIn = IsTerminal(inFd) 45 } 46 return inFd, isTerminalIn 47 } 48 49 // IsTerminal returns true if the given file descriptor is a terminal. 50 func IsTerminal(fd uintptr) bool { 51 var termios Termios 52 return tcget(fd, &termios) == 0 53 } 54 55 // RestoreTerminal restores the terminal connected to the given file descriptor 56 // to a previous state. 57 func RestoreTerminal(fd uintptr, state *State) error { 58 if state == nil { 59 return ErrInvalidState 60 } 61 if err := tcset(fd, &state.termios); err != 0 { 62 return err 63 } 64 return nil 65 } 66 67 // SaveState saves the state of the terminal connected to the given file descriptor. 68 func SaveState(fd uintptr) (*State, error) { 69 var oldState State 70 if err := tcget(fd, &oldState.termios); err != 0 { 71 return nil, err 72 } 73 74 return &oldState, nil 75 } 76 77 // DisableEcho applies the specified state to the terminal connected to the file 78 // descriptor, with echo disabled. 79 func DisableEcho(fd uintptr, state *State) error { 80 newState := state.termios 81 newState.Lflag &^= syscall.ECHO 82 83 if err := tcset(fd, &newState); err != 0 { 84 return err 85 } 86 handleInterrupt(fd, state) 87 return nil 88 } 89 90 // SetRawTerminal puts the terminal connected to the given file descriptor into 91 // raw mode and returns the previous state. On UNIX, this puts both the input 92 // and output into raw mode. On Windows, it only puts the input into raw mode. 93 func SetRawTerminal(fd uintptr) (*State, error) { 94 oldState, err := MakeRaw(fd) 95 if err != nil { 96 return nil, err 97 } 98 handleInterrupt(fd, oldState) 99 return oldState, err 100 } 101 102 // SetRawTerminalOutput puts the output of terminal connected to the given file 103 // descriptor into raw mode. On UNIX, this does nothing and returns nil for the 104 // state. On Windows, it disables LF -> CRLF translation. 105 func SetRawTerminalOutput(fd uintptr) (*State, error) { 106 return nil, nil 107 } 108 109 func handleInterrupt(fd uintptr, state *State) { 110 sigchan := make(chan os.Signal, 1) 111 signal.Notify(sigchan, os.Interrupt) 112 113 go func() { 114 _ = <-sigchan 115 RestoreTerminal(fd, state) 116 }() 117 }