github.com/portworx/docker@v1.12.1/pkg/term/term_windows.go (about) 1 // +build windows 2 3 package term 4 5 import ( 6 "io" 7 "os" 8 "os/signal" 9 "syscall" 10 11 "github.com/Azure/go-ansiterm/winterm" 12 "github.com/docker/docker/pkg/term/windows" 13 ) 14 15 // State holds the console mode for the terminal. 16 type State struct { 17 mode uint32 18 } 19 20 // Winsize is used for window size. 21 type Winsize struct { 22 Height uint16 23 Width uint16 24 } 25 26 const ( 27 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx 28 enableVirtualTerminalInput = 0x0200 29 enableVirtualTerminalProcessing = 0x0004 30 disableNewlineAutoReturn = 0x0008 31 ) 32 33 // vtInputSupported is true if enableVirtualTerminalInput is supported by the console 34 var vtInputSupported bool 35 36 // StdStreams returns the standard streams (stdin, stdout, stedrr). 37 func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { 38 // Turn on VT handling on all std handles, if possible. This might 39 // fail, in which case we will fall back to terminal emulation. 40 var emulateStdin, emulateStdout, emulateStderr bool 41 fd := os.Stdin.Fd() 42 if mode, err := winterm.GetConsoleMode(fd); err == nil { 43 // Validate that enableVirtualTerminalInput is supported, but do not set it. 44 if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil { 45 emulateStdin = true 46 } else { 47 vtInputSupported = true 48 } 49 // Unconditionally set the console mode back even on failure because SetConsoleMode 50 // remembers invalid bits on input handles. 51 winterm.SetConsoleMode(fd, mode) 52 } 53 54 fd = os.Stdout.Fd() 55 if mode, err := winterm.GetConsoleMode(fd); err == nil { 56 // Validate disableNewlineAutoReturn is supported, but do not set it. 57 if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { 58 emulateStdout = true 59 } else { 60 winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) 61 } 62 } 63 64 fd = os.Stderr.Fd() 65 if mode, err := winterm.GetConsoleMode(fd); err == nil { 66 // Validate disableNewlineAutoReturn is supported, but do not set it. 67 if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { 68 emulateStderr = true 69 } else { 70 winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) 71 } 72 } 73 74 if os.Getenv("ConEmuANSI") == "ON" { 75 // The ConEmu terminal emulates ANSI on output streams well. 76 emulateStdout = false 77 emulateStderr = false 78 } 79 80 if emulateStdin { 81 stdIn = windows.NewAnsiReader(syscall.STD_INPUT_HANDLE) 82 } else { 83 stdIn = os.Stdin 84 } 85 86 if emulateStdout { 87 stdOut = windows.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE) 88 } else { 89 stdOut = os.Stdout 90 } 91 92 if emulateStderr { 93 stdErr = windows.NewAnsiWriter(syscall.STD_ERROR_HANDLE) 94 } else { 95 stdErr = os.Stderr 96 } 97 98 return 99 } 100 101 // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. 102 func GetFdInfo(in interface{}) (uintptr, bool) { 103 return windows.GetHandleInfo(in) 104 } 105 106 // GetWinsize returns the window size based on the specified file descriptor. 107 func GetWinsize(fd uintptr) (*Winsize, error) { 108 info, err := winterm.GetConsoleScreenBufferInfo(fd) 109 if err != nil { 110 return nil, err 111 } 112 113 winsize := &Winsize{ 114 Width: uint16(info.Window.Right - info.Window.Left + 1), 115 Height: uint16(info.Window.Bottom - info.Window.Top + 1), 116 } 117 118 return winsize, nil 119 } 120 121 // IsTerminal returns true if the given file descriptor is a terminal. 122 func IsTerminal(fd uintptr) bool { 123 return windows.IsConsole(fd) 124 } 125 126 // RestoreTerminal restores the terminal connected to the given file descriptor 127 // to a previous state. 128 func RestoreTerminal(fd uintptr, state *State) error { 129 return winterm.SetConsoleMode(fd, state.mode) 130 } 131 132 // SaveState saves the state of the terminal connected to the given file descriptor. 133 func SaveState(fd uintptr) (*State, error) { 134 mode, e := winterm.GetConsoleMode(fd) 135 if e != nil { 136 return nil, e 137 } 138 139 return &State{mode: mode}, nil 140 } 141 142 // DisableEcho disables echo for the terminal connected to the given file descriptor. 143 // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx 144 func DisableEcho(fd uintptr, state *State) error { 145 mode := state.mode 146 mode &^= winterm.ENABLE_ECHO_INPUT 147 mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT 148 err := winterm.SetConsoleMode(fd, mode) 149 if err != nil { 150 return err 151 } 152 153 // Register an interrupt handler to catch and restore prior state 154 restoreAtInterrupt(fd, state) 155 return nil 156 } 157 158 // SetRawTerminal puts the terminal connected to the given file descriptor into 159 // raw mode and returns the previous state. On UNIX, this puts both the input 160 // and output into raw mode. On Windows, it only puts the input into raw mode. 161 func SetRawTerminal(fd uintptr) (*State, error) { 162 state, err := MakeRaw(fd) 163 if err != nil { 164 return nil, err 165 } 166 167 // Register an interrupt handler to catch and restore prior state 168 restoreAtInterrupt(fd, state) 169 return state, err 170 } 171 172 // SetRawTerminalOutput puts the output of terminal connected to the given file 173 // descriptor into raw mode. On UNIX, this does nothing and returns nil for the 174 // state. On Windows, it disables LF -> CRLF translation. 175 func SetRawTerminalOutput(fd uintptr) (*State, error) { 176 state, err := SaveState(fd) 177 if err != nil { 178 return nil, err 179 } 180 181 // Ignore failures, since disableNewlineAutoReturn might not be supported on this 182 // version of Windows. 183 winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn) 184 return state, err 185 } 186 187 // MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw 188 // mode and returns the previous state of the terminal so that it can be restored. 189 func MakeRaw(fd uintptr) (*State, error) { 190 state, err := SaveState(fd) 191 if err != nil { 192 return nil, err 193 } 194 195 mode := state.mode 196 197 // See 198 // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx 199 // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx 200 201 // Disable these modes 202 mode &^= winterm.ENABLE_ECHO_INPUT 203 mode &^= winterm.ENABLE_LINE_INPUT 204 mode &^= winterm.ENABLE_MOUSE_INPUT 205 mode &^= winterm.ENABLE_WINDOW_INPUT 206 mode &^= winterm.ENABLE_PROCESSED_INPUT 207 208 // Enable these modes 209 mode |= winterm.ENABLE_EXTENDED_FLAGS 210 mode |= winterm.ENABLE_INSERT_MODE 211 mode |= winterm.ENABLE_QUICK_EDIT_MODE 212 if vtInputSupported { 213 mode |= enableVirtualTerminalInput 214 } 215 216 err = winterm.SetConsoleMode(fd, mode) 217 if err != nil { 218 return nil, err 219 } 220 return state, nil 221 } 222 223 func restoreAtInterrupt(fd uintptr, state *State) { 224 sigchan := make(chan os.Signal, 1) 225 signal.Notify(sigchan, os.Interrupt) 226 227 go func() { 228 _ = <-sigchan 229 RestoreTerminal(fd, state) 230 os.Exit(0) 231 }() 232 }