diff options
Diffstat (limited to 'vendor/github.com/gookit/color/detect_windows.go')
-rw-r--r-- | vendor/github.com/gookit/color/detect_windows.go | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/vendor/github.com/gookit/color/detect_windows.go b/vendor/github.com/gookit/color/detect_windows.go new file mode 100644 index 000000000..7707d9ca2 --- /dev/null +++ b/vendor/github.com/gookit/color/detect_windows.go @@ -0,0 +1,243 @@ +// +build windows + +// Display color on windows +// refer: +// golang.org/x/sys/windows +// golang.org/x/crypto/ssh/terminal +// https://docs.microsoft.com/en-us/windows/console +package color + +import ( + "os" + "syscall" + "unsafe" + + "github.com/xo/terminfo" + "golang.org/x/sys/windows" +) + +// related docs +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +var ( + // isMSys bool + kernel32 *syscall.LazyDLL + + procGetConsoleMode *syscall.LazyProc + procSetConsoleMode *syscall.LazyProc +) + +func init() { + if !SupportColor() { + isLikeInCmd = true + return + } + + // if disabled. + if !Enable { + return + } + + // if at windows's ConEmu, Cmder, putty ... terminals not need VTP + + // -------- try force enable colors on windows terminal ------- + tryEnableVTP(needVTP) + + // fetch console screen buffer info + // err := getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo) +} + +// try force enable colors on windows terminal +func tryEnableVTP(enable bool) bool { + if !enable { + return false + } + + debugf("True-Color by enable VirtualTerminalProcessing on windows") + + initKernel32Proc() + + // enable colors on windows terminal + if tryEnableOnCONOUT() { + return true + } + + return tryEnableOnStdout() +} + +func initKernel32Proc() { + if kernel32 != nil { + return + } + + // load related windows dll + // https://docs.microsoft.com/en-us/windows/console/setconsolemode + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") +} + +func tryEnableOnCONOUT() bool { + outHandle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + saveInternalError(err) + return false + } + + err = EnableVirtualTerminalProcessing(outHandle, true) + if err != nil { + saveInternalError(err) + return false + } + + return true +} + +func tryEnableOnStdout() bool { + // try direct open syscall.Stdout + err := EnableVirtualTerminalProcessing(syscall.Stdout, true) + if err != nil { + saveInternalError(err) + return false + } + + return true +} + +// Get the Windows Version and Build Number +var ( + winVersion, _, buildNumber = windows.RtlGetNtVersionNumbers() +) + +// refer +// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57 +// https://github.com/gookit/color/issues/25#issuecomment-738727917 +// detects the Color Level Supported on windows: cmd, powerShell +func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP bool) { + if os.Getenv("ConEmuANSI") == "ON" { + debugf("support True Color by ConEmuANSI=ON") + // ConEmuANSI is "ON" for generic ANSI support + // but True Color option is enabled by default + // I am just assuming that people wouldn't have disabled it + // Even if it is not enabled then ConEmu will auto round off + // accordingly + return terminfo.ColorLevelMillions, false + } + + // Before Windows 10 Build Number 10586, console never supported ANSI Colors + if buildNumber < 10586 || winVersion < 10 { + // Detect if using ANSICON on older systems + if os.Getenv("ANSICON") != "" { + conVersion := os.Getenv("ANSICON_VER") + // 8 bit Colors were only supported after v1.81 release + if conVersion >= "181" { + return terminfo.ColorLevelHundreds, false + } + return terminfo.ColorLevelBasic, false + } + + return terminfo.ColorLevelNone, false + } + + // True Color is not available before build 14931 so fallback to 8 bit color. + if buildNumber < 14931 { + return terminfo.ColorLevelHundreds, true + } + + // Windows 10 build 14931 is the first release that supports 16m/TrueColor + debugf("support True Color on windows version is >= build 14931") + return terminfo.ColorLevelMillions, true +} + +/************************************************************* + * render full color code on windows(8,16,24bit color) + *************************************************************/ + +// docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters +const ( + // equals to docs page's ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 + EnableVirtualTerminalProcessingMode uint32 = 0x4 +) + +// EnableVirtualTerminalProcessing Enable virtual terminal processing +// +// ref from github.com/konsorten/go-windows-terminal-sequences +// doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +// +// Usage: +// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) +// // support print color text +// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) +func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { + var mode uint32 + // Check if it is currently in the terminal + // err := syscall.GetConsoleMode(syscall.Stdout, &mode) + err := syscall.GetConsoleMode(stream, &mode) + if err != nil { + // fmt.Println("EnableVirtualTerminalProcessing", err) + return err + } + + if enable { + mode |= EnableVirtualTerminalProcessingMode + } else { + mode &^= EnableVirtualTerminalProcessingMode + } + + ret, _, err := procSetConsoleMode.Call(uintptr(stream), uintptr(mode)) + if ret == 0 { + return err + } + + return nil +} + +// renderColorCodeOnCmd enable cmd color render. +// func renderColorCodeOnCmd(fn func()) { +// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) +// // if is not in terminal, will clear color tag. +// if err != nil { +// // panic(err) +// fn() +// return +// } +// +// // force open color render +// old := ForceOpenColor() +// fn() +// // revert color setting +// supportColor = old +// +// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) +// if err != nil { +// panic(err) +// } +// } + +/************************************************************* + * render simple color code on windows + *************************************************************/ + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + initKernel32Proc() + + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// IsTerminal returns true if the given file descriptor is a terminal. +// +// Usage: +// fd := os.Stdout.Fd() +// fd := uintptr(syscall.Stdout) // for windows +// IsTerminal(fd) +func IsTerminal(fd uintptr) bool { + initKernel32Proc() + + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} |