Windows: Always enable VT emulation
Always enable VT output emulation when starting the process so that non-attaching commands can still output VT codes. Also remove the version block for using the native console and just rely on supported flags being present. Signed-off-by: John Starks <jostarks@microsoft.com>
This commit is contained in:
parent
138f9538f3
commit
4acc2c7499
6 changed files with 118 additions and 225 deletions
|
@ -47,8 +47,10 @@ type DockerCli struct {
|
||||||
isTerminalOut bool
|
isTerminalOut bool
|
||||||
// client is the http client that performs all API operations
|
// client is the http client that performs all API operations
|
||||||
client client.APIClient
|
client client.APIClient
|
||||||
// state holds the terminal state
|
// state holds the terminal input state
|
||||||
state *term.State
|
inState *term.State
|
||||||
|
// outState holds the terminal output state
|
||||||
|
outState *term.State
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize calls the init function that will setup the configuration for the client
|
// Initialize calls the init function that will setup the configuration for the client
|
||||||
|
@ -124,19 +126,31 @@ func (cli *DockerCli) ImagesFormat() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) setRawTerminal() error {
|
func (cli *DockerCli) setRawTerminal() error {
|
||||||
if cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
if os.Getenv("NORAW") == "" {
|
||||||
|
if cli.isTerminalIn {
|
||||||
state, err := term.SetRawTerminal(cli.inFd)
|
state, err := term.SetRawTerminal(cli.inFd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cli.state = state
|
cli.inState = state
|
||||||
|
}
|
||||||
|
if cli.isTerminalOut {
|
||||||
|
state, err := term.SetRawTerminalOutput(cli.outFd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cli.outState = state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
||||||
if cli.state != nil {
|
if cli.inState != nil {
|
||||||
term.RestoreTerminal(cli.inFd, cli.state)
|
term.RestoreTerminal(cli.inFd, cli.inState)
|
||||||
|
}
|
||||||
|
if cli.outState != nil {
|
||||||
|
term.RestoreTerminal(cli.outFd, cli.outState)
|
||||||
}
|
}
|
||||||
// WARNING: DO NOT REMOVE THE OS CHECK !!!
|
// WARNING: DO NOT REMOVE THE OS CHECK !!!
|
||||||
// For some reason this Close call blocks on darwin..
|
// For some reason this Close call blocks on darwin..
|
||||||
|
|
|
@ -88,7 +88,8 @@ func DisableEcho(fd uintptr, state *State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
||||||
// raw mode and returns the previous state.
|
// raw mode and returns the previous state. On UNIX, this puts both the input
|
||||||
|
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
||||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
oldState, err := MakeRaw(fd)
|
oldState, err := MakeRaw(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,6 +99,13 @@ func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
return oldState, err
|
return oldState, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
||||||
|
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
||||||
|
// state. On Windows, it disables LF -> CRLF translation.
|
||||||
|
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func handleInterrupt(fd uintptr, state *State) {
|
func handleInterrupt(fd uintptr, state *State) {
|
||||||
sigchan := make(chan os.Signal, 1)
|
sigchan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigchan, os.Interrupt)
|
signal.Notify(sigchan, os.Interrupt)
|
||||||
|
|
|
@ -9,14 +9,12 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
"github.com/docker/docker/pkg/system"
|
|
||||||
"github.com/docker/docker/pkg/term/windows"
|
"github.com/docker/docker/pkg/term/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
// State holds the console mode for the terminal.
|
// State holds the console mode for the terminal.
|
||||||
type State struct {
|
type State struct {
|
||||||
inMode, outMode uint32
|
mode uint32
|
||||||
inHandle, outHandle syscall.Handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Winsize is used for window size.
|
// Winsize is used for window size.
|
||||||
|
@ -32,143 +30,70 @@ const (
|
||||||
disableNewlineAutoReturn = 0x0008
|
disableNewlineAutoReturn = 0x0008
|
||||||
)
|
)
|
||||||
|
|
||||||
// usingNativeConsole is true if we are using the Windows native console
|
// vtInputSupported is true if enableVirtualTerminalInput is supported by the console
|
||||||
var usingNativeConsole bool
|
var vtInputSupported bool
|
||||||
|
|
||||||
// StdStreams returns the standard streams (stdin, stdout, stedrr).
|
// StdStreams returns the standard streams (stdin, stdout, stedrr).
|
||||||
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
||||||
switch {
|
// Turn on VT handling on all std handles, if possible. This might
|
||||||
case os.Getenv("ConEmuANSI") == "ON":
|
// fail, in which case we will fall back to terminal emulation.
|
||||||
|
var emulateStdin, emulateStdout, emulateStderr bool
|
||||||
|
fd := os.Stdin.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate that enableVirtualTerminalInput is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil {
|
||||||
|
emulateStdin = true
|
||||||
|
} else {
|
||||||
|
winterm.SetConsoleMode(fd, mode)
|
||||||
|
vtInputSupported = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = os.Stdout.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate disableNewlineAutoReturn is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil {
|
||||||
|
emulateStdout = true
|
||||||
|
} else {
|
||||||
|
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = os.Stderr.Fd()
|
||||||
|
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
||||||
|
// Validate disableNewlineAutoReturn is supported, but do not set it.
|
||||||
|
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil {
|
||||||
|
emulateStderr = true
|
||||||
|
} else {
|
||||||
|
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("ConEmuANSI") == "ON" {
|
||||||
// The ConEmu terminal emulates ANSI on output streams well.
|
// The ConEmu terminal emulates ANSI on output streams well.
|
||||||
return windows.ConEmuStreams()
|
emulateStdout = false
|
||||||
case os.Getenv("MSYSTEM") != "":
|
emulateStderr = false
|
||||||
// MSYS (mingw) does not emulate ANSI well.
|
|
||||||
return windows.ConsoleStreams()
|
|
||||||
default:
|
|
||||||
if useNativeConsole() {
|
|
||||||
usingNativeConsole = true
|
|
||||||
return os.Stdin, os.Stdout, os.Stderr
|
|
||||||
}
|
|
||||||
return windows.ConsoleStreams()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// useNativeConsole determines if the docker client should use the built-in
|
|
||||||
// console which supports ANSI emulation, or fall-back to the golang emulator
|
|
||||||
// (github.com/azure/go-ansiterm).
|
|
||||||
func useNativeConsole() bool {
|
|
||||||
osv := system.GetOSVersion()
|
|
||||||
|
|
||||||
// Native console is not available before major version 10
|
|
||||||
if osv.MajorVersion < 10 {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the console modes. If this fails, we can't use the native console
|
if emulateStdin {
|
||||||
state, err := getNativeConsole()
|
stdIn = windows.NewAnsiReader(syscall.STD_INPUT_HANDLE)
|
||||||
if err != nil {
|
} else {
|
||||||
return false
|
stdIn = os.Stdin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Probe the console to see if it can be enabled.
|
if emulateStdout {
|
||||||
if nil != probeNativeConsole(state) {
|
stdOut = windows.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
|
||||||
return false
|
} else {
|
||||||
|
stdOut = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
// Environment variable override
|
if emulateStderr {
|
||||||
if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
|
stdErr = windows.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
|
||||||
if e == "1" {
|
} else {
|
||||||
return true
|
stdErr = os.Stderr
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must have a post-TP5 RS1 build of Windows Server 2016/Windows 10 for
|
return
|
||||||
// the native console to be usable.
|
|
||||||
if osv.Build < 14350 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNativeConsole returns the console modes ('state') for the native Windows console
|
|
||||||
func getNativeConsole() (State, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
state State
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get the handle to stdout
|
|
||||||
if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil {
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the console mode from the consoles stdout handle
|
|
||||||
if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil {
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the handle to stdin
|
|
||||||
if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil {
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the console mode from the consoles stdin handle
|
|
||||||
if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil {
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// probeNativeConsole probes the console to determine if native can be supported,
|
|
||||||
func probeNativeConsole(state State) error {
|
|
||||||
if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
|
|
||||||
|
|
||||||
if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// enableNativeConsole turns on native console mode
|
|
||||||
func enableNativeConsole(state State) error {
|
|
||||||
// First attempt both enableVirtualTerminalProcessing and disableNewlineAutoReturn
|
|
||||||
if err := winterm.SetConsoleMode(uintptr(state.outHandle),
|
|
||||||
state.outMode|(enableVirtualTerminalProcessing|disableNewlineAutoReturn)); err != nil {
|
|
||||||
|
|
||||||
// That may fail, so fallback to trying just enableVirtualTerminalProcessing
|
|
||||||
if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
|
|
||||||
winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// disableNativeConsole turns off native console mode
|
|
||||||
func disableNativeConsole(state *State) error {
|
|
||||||
// Try and restore both in an out before error checking.
|
|
||||||
errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
|
|
||||||
errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
|
|
||||||
if errout != nil {
|
|
||||||
return errout
|
|
||||||
}
|
|
||||||
if errin != nil {
|
|
||||||
return errin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
||||||
|
@ -199,34 +124,23 @@ func IsTerminal(fd uintptr) bool {
|
||||||
// RestoreTerminal restores the terminal connected to the given file descriptor
|
// RestoreTerminal restores the terminal connected to the given file descriptor
|
||||||
// to a previous state.
|
// to a previous state.
|
||||||
func RestoreTerminal(fd uintptr, state *State) error {
|
func RestoreTerminal(fd uintptr, state *State) error {
|
||||||
if usingNativeConsole {
|
return winterm.SetConsoleMode(fd, state.mode)
|
||||||
return disableNativeConsole(state)
|
|
||||||
}
|
|
||||||
return winterm.SetConsoleMode(fd, state.outMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveState saves the state of the terminal connected to the given file descriptor.
|
// SaveState saves the state of the terminal connected to the given file descriptor.
|
||||||
func SaveState(fd uintptr) (*State, error) {
|
func SaveState(fd uintptr) (*State, error) {
|
||||||
if usingNativeConsole {
|
|
||||||
state, err := getNativeConsole()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mode, e := winterm.GetConsoleMode(fd)
|
mode, e := winterm.GetConsoleMode(fd)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, e
|
return nil, e
|
||||||
}
|
}
|
||||||
|
|
||||||
return &State{outMode: mode}, nil
|
return &State{mode: mode}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableEcho disables echo for the terminal connected to the given file descriptor.
|
// DisableEcho disables echo for the terminal connected to the given file descriptor.
|
||||||
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
||||||
func DisableEcho(fd uintptr, state *State) error {
|
func DisableEcho(fd uintptr, state *State) error {
|
||||||
mode := state.inMode
|
mode := state.mode
|
||||||
mode &^= winterm.ENABLE_ECHO_INPUT
|
mode &^= winterm.ENABLE_ECHO_INPUT
|
||||||
mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
|
mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
|
||||||
err := winterm.SetConsoleMode(fd, mode)
|
err := winterm.SetConsoleMode(fd, mode)
|
||||||
|
@ -239,8 +153,9 @@ func DisableEcho(fd uintptr, state *State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawTerminal puts the terminal connected to the given file descriptor into raw
|
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
||||||
// mode and returns the previous state.
|
// raw mode and returns the previous state. On UNIX, this puts both the input
|
||||||
|
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
||||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
state, err := MakeRaw(fd)
|
state, err := MakeRaw(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -252,6 +167,21 @@ func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
return state, err
|
return state, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
||||||
|
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
||||||
|
// state. On Windows, it disables LF -> CRLF translation.
|
||||||
|
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
||||||
|
state, err := SaveState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore failures, since disableNewlineAutoReturn might not be supported on this
|
||||||
|
// version of Windows.
|
||||||
|
winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn)
|
||||||
|
return state, err
|
||||||
|
}
|
||||||
|
|
||||||
// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
|
// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
|
||||||
// mode and returns the previous state of the terminal so that it can be restored.
|
// mode and returns the previous state of the terminal so that it can be restored.
|
||||||
func MakeRaw(fd uintptr) (*State, error) {
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
@ -260,13 +190,7 @@ func MakeRaw(fd uintptr) (*State, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mode := state.inMode
|
mode := state.mode
|
||||||
if usingNativeConsole {
|
|
||||||
if err := enableNativeConsole(*state); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mode |= enableVirtualTerminalInput
|
|
||||||
}
|
|
||||||
|
|
||||||
// See
|
// See
|
||||||
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
|
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
|
||||||
|
@ -283,6 +207,9 @@ func MakeRaw(fd uintptr) (*State, error) {
|
||||||
mode |= winterm.ENABLE_EXTENDED_FLAGS
|
mode |= winterm.ENABLE_EXTENDED_FLAGS
|
||||||
mode |= winterm.ENABLE_INSERT_MODE
|
mode |= winterm.ENABLE_INSERT_MODE
|
||||||
mode |= winterm.ENABLE_QUICK_EDIT_MODE
|
mode |= winterm.ENABLE_QUICK_EDIT_MODE
|
||||||
|
if vtInputSupported {
|
||||||
|
mode |= enableVirtualTerminalInput
|
||||||
|
}
|
||||||
|
|
||||||
err = winterm.SetConsoleMode(fd, mode)
|
err = winterm.SetConsoleMode(fd, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -27,7 +28,9 @@ type ansiReader struct {
|
||||||
command []byte
|
command []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAnsiReader(nFile int) *ansiReader {
|
// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
|
||||||
|
// Windows console input handle.
|
||||||
|
func NewAnsiReader(nFile int) io.ReadCloser {
|
||||||
initLogger()
|
initLogger()
|
||||||
file, fd := winterm.GetStdFile(nFile)
|
file, fd := winterm.GetStdFile(nFile)
|
||||||
return &ansiReader{
|
return &ansiReader{
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
ansiterm "github.com/Azure/go-ansiterm"
|
ansiterm "github.com/Azure/go-ansiterm"
|
||||||
|
@ -20,7 +21,9 @@ type ansiWriter struct {
|
||||||
parser *ansiterm.AnsiParser
|
parser *ansiterm.AnsiParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAnsiWriter(nFile int) *ansiWriter {
|
// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
|
||||||
|
// Windows console output handle.
|
||||||
|
func NewAnsiWriter(nFile int) io.Writer {
|
||||||
initLogger()
|
initLogger()
|
||||||
file, fd := winterm.GetStdFile(nFile)
|
file, fd := winterm.GetStdFile(nFile)
|
||||||
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
||||||
|
|
|
@ -3,73 +3,11 @@
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
"github.com/Azure/go-ansiterm/winterm"
|
||||||
|
|
||||||
ansiterm "github.com/Azure/go-ansiterm"
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConEmuStreams returns prepared versions of console streams,
|
|
||||||
// for proper use in ConEmu terminal.
|
|
||||||
// The ConEmu terminal emulates ANSI on output streams well by default.
|
|
||||||
func ConEmuStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
|
||||||
if IsConsole(os.Stdin.Fd()) {
|
|
||||||
stdIn = newAnsiReader(syscall.STD_INPUT_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdIn = os.Stdin
|
|
||||||
}
|
|
||||||
|
|
||||||
stdOut = os.Stdout
|
|
||||||
stdErr = os.Stderr
|
|
||||||
|
|
||||||
// WARNING (BEGIN): sourced from newAnsiWriter
|
|
||||||
|
|
||||||
logFile := ioutil.Discard
|
|
||||||
|
|
||||||
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
|
|
||||||
logFile, _ = os.Create("ansiReaderWriter.log")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger = &logrus.Logger{
|
|
||||||
Out: logFile,
|
|
||||||
Formatter: new(logrus.TextFormatter),
|
|
||||||
Level: logrus.DebugLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING (END): sourced from newAnsiWriter
|
|
||||||
|
|
||||||
return stdIn, stdOut, stdErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConsoleStreams returns a wrapped version for each standard stream referencing a console,
|
|
||||||
// that handles ANSI character sequences.
|
|
||||||
func ConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
|
||||||
if IsConsole(os.Stdin.Fd()) {
|
|
||||||
stdIn = newAnsiReader(syscall.STD_INPUT_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdIn = os.Stdin
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsConsole(os.Stdout.Fd()) {
|
|
||||||
stdOut = newAnsiWriter(syscall.STD_OUTPUT_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdOut = os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsConsole(os.Stderr.Fd()) {
|
|
||||||
stdErr = newAnsiWriter(syscall.STD_ERROR_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdErr = os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
return stdIn, stdOut, stdErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
|
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
|
||||||
func GetHandleInfo(in interface{}) (uintptr, bool) {
|
func GetHandleInfo(in interface{}) (uintptr, bool) {
|
||||||
switch t := in.(type) {
|
switch t := in.(type) {
|
||||||
|
|
Loading…
Reference in a new issue