DockerCLI/vendor/github.com/moby/term/term_windows.go

177 lines
4.7 KiB
Go

package term
import (
"fmt"
"io"
"os"
"os/signal"
windowsconsole "github.com/moby/term/windows"
"golang.org/x/sys/windows"
)
// terminalState holds the platform-specific state / console mode for the terminal.
type terminalState struct {
mode uint32
}
// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
var vtInputSupported bool
func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
// Turn on VT handling on all std handles, if possible. This might
// fail, in which case we will fall back to terminal emulation.
var (
emulateStdin, emulateStdout, emulateStderr bool
mode uint32
)
fd := windows.Handle(os.Stdin.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
emulateStdin = true
} else {
vtInputSupported = true
}
// Unconditionally set the console mode back even on failure because SetConsoleMode
// remembers invalid bits on input handles.
_ = windows.SetConsoleMode(fd, mode)
}
fd = windows.Handle(os.Stdout.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStdout = true
} else {
_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}
}
fd = windows.Handle(os.Stderr.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStderr = true
} else {
_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}
}
if emulateStdin {
h := uint32(windows.STD_INPUT_HANDLE)
stdIn = windowsconsole.NewAnsiReader(int(h))
} else {
stdIn = os.Stdin
}
if emulateStdout {
h := uint32(windows.STD_OUTPUT_HANDLE)
stdOut = windowsconsole.NewAnsiWriter(int(h))
} else {
stdOut = os.Stdout
}
if emulateStderr {
h := uint32(windows.STD_ERROR_HANDLE)
stdErr = windowsconsole.NewAnsiWriter(int(h))
} else {
stdErr = os.Stderr
}
return stdIn, stdOut, stdErr
}
func getFdInfo(in interface{}) (uintptr, bool) {
return windowsconsole.GetHandleInfo(in)
}
func getWinsize(fd uintptr) (*Winsize, error) {
var info windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
return nil, err
}
winsize := &Winsize{
Width: uint16(info.Window.Right - info.Window.Left + 1),
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
}
return winsize, nil
}
func setWinsize(fd uintptr, ws *Winsize) error {
return fmt.Errorf("not implemented on Windows")
}
func isTerminal(fd uintptr) bool {
var mode uint32
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
return err == nil
}
func restoreTerminal(fd uintptr, state *State) error {
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}
func saveState(fd uintptr) (*State, error) {
var mode uint32
if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
return nil, err
}
return &State{mode: mode}, nil
}
func disableEcho(fd uintptr, state *State) error {
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
mode := state.mode
mode &^= windows.ENABLE_ECHO_INPUT
mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
err := windows.SetConsoleMode(windows.Handle(fd), mode)
if err != nil {
return err
}
// Register an interrupt handler to catch and restore prior state
restoreAtInterrupt(fd, state)
return nil
}
func setRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}
// Register an interrupt handler to catch and restore prior state
restoreAtInterrupt(fd, oldState)
return oldState, err
}
func setRawTerminalOutput(fd uintptr) (*State, error) {
oldState, err := saveState(fd)
if err != nil {
return nil, err
}
// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
// version of Windows.
_ = windows.SetConsoleMode(windows.Handle(fd), oldState.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
return oldState, err
}
func restoreAtInterrupt(fd uintptr, state *State) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt)
go func() {
_ = <-sigchan
_ = RestoreTerminal(fd, state)
os.Exit(0)
}()
}