term_windows: use golang.org/x/sys/windows
The parts of github.com/Azure/go-ansiterm/winterm that were used
is now provided by golang.org/x/sys/windows, so switch to that
package, as it's more actively maintained.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn
4 years ago
3 | 3 | "io" |
4 | 4 | "os" |
5 | 5 | "os/signal" |
6 | "syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE | |
7 | ||
8 | "github.com/Azure/go-ansiterm/winterm" | |
6 | ||
9 | 7 | windowsconsole "github.com/moby/term/windows" |
8 | "golang.org/x/sys/windows" | |
10 | 9 | ) |
11 | 10 | |
12 | 11 | // State holds the console mode for the terminal. |
27 | 26 | func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { |
28 | 27 | // Turn on VT handling on all std handles, if possible. This might |
29 | 28 | // fail, in which case we will fall back to terminal emulation. |
30 | var emulateStdin, emulateStdout, emulateStderr bool | |
31 | fd := os.Stdin.Fd() | |
32 | if mode, err := winterm.GetConsoleMode(fd); err == nil { | |
29 | var ( | |
30 | emulateStdin, emulateStdout, emulateStderr bool | |
31 | ||
32 | mode uint32 | |
33 | ) | |
34 | ||
35 | fd := windows.Handle(os.Stdin.Fd()) | |
36 | if err := windows.GetConsoleMode(fd, &mode); err == nil { | |
33 | 37 | // Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. |
34 | if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { | |
38 | if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { | |
35 | 39 | emulateStdin = true |
36 | 40 | } else { |
37 | 41 | vtInputSupported = true |
38 | 42 | } |
39 | 43 | // Unconditionally set the console mode back even on failure because SetConsoleMode |
40 | 44 | // remembers invalid bits on input handles. |
41 | winterm.SetConsoleMode(fd, mode) | |
42 | } | |
43 | ||
44 | fd = os.Stdout.Fd() | |
45 | if mode, err := winterm.GetConsoleMode(fd); err == nil { | |
45 | _ = windows.SetConsoleMode(fd, mode) | |
46 | } | |
47 | ||
48 | fd = windows.Handle(os.Stdout.Fd()) | |
49 | if err := windows.GetConsoleMode(fd, &mode); err == nil { | |
46 | 50 | // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. |
47 | if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil { | |
51 | if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { | |
48 | 52 | emulateStdout = true |
49 | 53 | } else { |
50 | winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
54 | _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
51 | 55 | } |
52 | 56 | } |
53 | 57 | |
54 | fd = os.Stderr.Fd() | |
55 | if mode, err := winterm.GetConsoleMode(fd); err == nil { | |
58 | fd = windows.Handle(os.Stderr.Fd()) | |
59 | if err := windows.GetConsoleMode(fd, &mode); err == nil { | |
56 | 60 | // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. |
57 | if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil { | |
61 | if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { | |
58 | 62 | emulateStderr = true |
59 | 63 | } else { |
60 | winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
64 | _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
61 | 65 | } |
62 | 66 | } |
63 | 67 | |
66 | 70 | // go-ansiterm hasn't switch to x/sys/windows. |
67 | 71 | // TODO: switch back to x/sys/windows once go-ansiterm has switched |
68 | 72 | if emulateStdin { |
69 | stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE) | |
73 | stdIn = windowsconsole.NewAnsiReader(windows.STD_INPUT_HANDLE) | |
70 | 74 | } else { |
71 | 75 | stdIn = os.Stdin |
72 | 76 | } |
73 | 77 | |
74 | 78 | if emulateStdout { |
75 | stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE) | |
79 | stdOut = windowsconsole.NewAnsiWriter(windows.STD_OUTPUT_HANDLE) | |
76 | 80 | } else { |
77 | 81 | stdOut = os.Stdout |
78 | 82 | } |
79 | 83 | |
80 | 84 | if emulateStderr { |
81 | stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE) | |
85 | stdErr = windowsconsole.NewAnsiWriter(windows.STD_ERROR_HANDLE) | |
82 | 86 | } else { |
83 | 87 | stdErr = os.Stderr |
84 | 88 | } |
93 | 97 | |
94 | 98 | // GetWinsize returns the window size based on the specified file descriptor. |
95 | 99 | func GetWinsize(fd uintptr) (*Winsize, error) { |
96 | info, err := winterm.GetConsoleScreenBufferInfo(fd) | |
97 | if err != nil { | |
100 | var info windows.ConsoleScreenBufferInfo | |
101 | if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { | |
98 | 102 | return nil, err |
99 | 103 | } |
100 | 104 | |
108 | 112 | |
109 | 113 | // IsTerminal returns true if the given file descriptor is a terminal. |
110 | 114 | func IsTerminal(fd uintptr) bool { |
111 | return windowsconsole.IsConsole(fd) | |
115 | var mode uint32 | |
116 | err := windows.GetConsoleMode(windows.Handle(fd), &mode) | |
117 | return err == nil | |
112 | 118 | } |
113 | 119 | |
114 | 120 | // RestoreTerminal restores the terminal connected to the given file descriptor |
115 | 121 | // to a previous state. |
116 | 122 | func RestoreTerminal(fd uintptr, state *State) error { |
117 | return winterm.SetConsoleMode(fd, state.mode) | |
123 | return windows.SetConsoleMode(windows.Handle(fd), state.mode) | |
118 | 124 | } |
119 | 125 | |
120 | 126 | // SaveState saves the state of the terminal connected to the given file descriptor. |
121 | 127 | func SaveState(fd uintptr) (*State, error) { |
122 | mode, e := winterm.GetConsoleMode(fd) | |
123 | if e != nil { | |
124 | return nil, e | |
128 | var mode uint32 | |
129 | ||
130 | if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil { | |
131 | return nil, err | |
125 | 132 | } |
126 | 133 | |
127 | 134 | return &State{mode: mode}, nil |
131 | 138 | // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx |
132 | 139 | func DisableEcho(fd uintptr, state *State) error { |
133 | 140 | mode := state.mode |
134 | mode &^= winterm.ENABLE_ECHO_INPUT | |
135 | mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT | |
136 | err := winterm.SetConsoleMode(fd, mode) | |
141 | mode &^= windows.ENABLE_ECHO_INPUT | |
142 | mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | |
143 | err := windows.SetConsoleMode(windows.Handle(fd), mode) | |
137 | 144 | if err != nil { |
138 | 145 | return err |
139 | 146 | } |
168 | 175 | |
169 | 176 | // Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this |
170 | 177 | // version of Windows. |
171 | winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN) | |
178 | _ = windows.SetConsoleMode(windows.Handle(fd), state.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) | |
172 | 179 | return state, err |
173 | 180 | } |
174 | 181 | |
187 | 194 | // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx |
188 | 195 | |
189 | 196 | // Disable these modes |
190 | mode &^= winterm.ENABLE_ECHO_INPUT | |
191 | mode &^= winterm.ENABLE_LINE_INPUT | |
192 | mode &^= winterm.ENABLE_MOUSE_INPUT | |
193 | mode &^= winterm.ENABLE_WINDOW_INPUT | |
194 | mode &^= winterm.ENABLE_PROCESSED_INPUT | |
197 | mode &^= windows.ENABLE_ECHO_INPUT | |
198 | mode &^= windows.ENABLE_LINE_INPUT | |
199 | mode &^= windows.ENABLE_MOUSE_INPUT | |
200 | mode &^= windows.ENABLE_WINDOW_INPUT | |
201 | mode &^= windows.ENABLE_PROCESSED_INPUT | |
195 | 202 | |
196 | 203 | // Enable these modes |
197 | mode |= winterm.ENABLE_EXTENDED_FLAGS | |
198 | mode |= winterm.ENABLE_INSERT_MODE | |
199 | mode |= winterm.ENABLE_QUICK_EDIT_MODE | |
204 | mode |= windows.ENABLE_EXTENDED_FLAGS | |
205 | mode |= windows.ENABLE_INSERT_MODE | |
206 | mode |= windows.ENABLE_QUICK_EDIT_MODE | |
200 | 207 | if vtInputSupported { |
201 | mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT | |
202 | } | |
203 | ||
204 | err = winterm.SetConsoleMode(fd, mode) | |
208 | mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT | |
209 | } | |
210 | ||
211 | err = windows.SetConsoleMode(windows.Handle(fd), mode) | |
205 | 212 | if err != nil { |
206 | 213 | return nil, err |
207 | 214 | } |
214 | 221 | |
215 | 222 | go func() { |
216 | 223 | _ = <-sigchan |
217 | RestoreTerminal(fd, state) | |
224 | _ = RestoreTerminal(fd, state) | |
218 | 225 | os.Exit(0) |
219 | 226 | }() |
220 | 227 | } |