diff --git a/system/syscall_windows.go b/system/syscall_windows.go index 3a3a55b..7cf4431 100644 --- a/system/syscall_windows.go +++ b/system/syscall_windows.go @@ -1,5 +1,34 @@ package system +import ( + "fmt" + "syscall" +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +// GetOSVersion gets the operating system version on Windows. Note that +// docker.exe must be manifested to get the correct version information. +func GetOSVersion() (OSVersion, error) { + var err error + osv := OSVersion{} + osv.Version, err = syscall.GetVersion() + if err != nil { + return osv, fmt.Errorf("Failed to call GetVersion()") + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + return osv, nil +} + // Unmount is a platform-specific helper function to call // the unmount syscall. Not supported on Windows func Unmount(dest string) { diff --git a/term/term_windows.go b/term/term_windows.go index da9295e..04870d1 100644 --- a/term/term_windows.go +++ b/term/term_windows.go @@ -7,9 +7,11 @@ import ( "io" "os" "os/signal" + "syscall" "github.com/Azure/go-ansiterm/winterm" "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/term/windows" ) @@ -36,10 +38,66 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { // MSYS (mingw) does not emulate ANSI well. return windows.ConsoleStreams() default: + if useNativeConsole() { + 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, err := system.GetOSVersion() + if err != nil { + return false + } + + // Native console is not available major version 10 + if osv.MajorVersion < 10 { + return false + } + + // Must have a late pre-release TP4 build of Windows Server 2016/Windows 10 TH2 or later + if osv.Build < 10578 { + return false + } + + // Environment variable override + if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" { + if e == "1" { + return true + } + return false + } + + // Get the handle to stdout + stdOutHandle, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE) + if err != nil { + return false + } + + // Get the console mode from the consoles stdout handle + var mode uint32 + if err := syscall.GetConsoleMode(stdOutHandle, &mode); err != nil { + return false + } + + // Legacy mode does not have native ANSI emulation. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx + const enableVirtualTerminalProcessing = 0x0004 + if mode&enableVirtualTerminalProcessing == 0 { + return false + } + + // TODO Windows (Post TP4). The native emulator still has issues which + // mean it shouldn't be enabled for everyone. Change this next line to true + // to change the default to "enable if available". In the meantime, users + // can still try it out by using USE_NATIVE_CONSOLE env variable. + return false +} + // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. func GetFdInfo(in interface{}) (uintptr, bool) { return windows.GetHandleInfo(in)