1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
//go:build windows
// +build windows
package logger
import (
"os"
"strings"
"syscall"
"unsafe"
)
const SupportsColorEscapes = true
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var getConsoleMode = kernel32.NewProc("GetConsoleMode")
var setConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
var getConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
type consoleScreenBufferInfo struct {
dwSizeX int16
dwSizeY int16
dwCursorPositionX int16
dwCursorPositionY int16
wAttributes uint16
srWindowLeft int16
srWindowTop int16
srWindowRight int16
srWindowBottom int16
dwMaximumWindowSizeX int16
dwMaximumWindowSizeY int16
}
func GetTerminalInfo(file *os.File) TerminalInfo {
fd := file.Fd()
// Is this file descriptor a terminal?
var unused uint32
isTTY, _, _ := syscall.Syscall(getConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&unused)), 0)
// Get the width of the window
var info consoleScreenBufferInfo
syscall.Syscall(getConsoleScreenBufferInfo.Addr(), 2, fd, uintptr(unsafe.Pointer(&info)), 0)
return TerminalInfo{
IsTTY: isTTY != 0,
Width: int(info.dwSizeX) - 1,
Height: int(info.dwSizeY) - 1,
UseColorEscapes: !hasNoColorEnvironmentVariable(),
}
}
const (
FOREGROUND_BLUE uint8 = 1 << iota
FOREGROUND_GREEN
FOREGROUND_RED
FOREGROUND_INTENSITY
BACKGROUND_BLUE
BACKGROUND_GREEN
BACKGROUND_RED
BACKGROUND_INTENSITY
)
var windowsEscapeSequenceMap = map[string]uint8{
TerminalColors.Reset: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
TerminalColors.Dim: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
TerminalColors.Bold: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
// Apparently underlines only work with the CJK locale on Windows :(
TerminalColors.Underline: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
TerminalColors.Red: FOREGROUND_RED,
TerminalColors.Green: FOREGROUND_GREEN,
TerminalColors.Blue: FOREGROUND_BLUE,
TerminalColors.Cyan: FOREGROUND_GREEN | FOREGROUND_BLUE,
TerminalColors.Magenta: FOREGROUND_RED | FOREGROUND_BLUE,
TerminalColors.Yellow: FOREGROUND_RED | FOREGROUND_GREEN,
TerminalColors.RedBgRed: FOREGROUND_RED | BACKGROUND_RED,
TerminalColors.RedBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_RED,
TerminalColors.GreenBgGreen: FOREGROUND_GREEN | BACKGROUND_GREEN,
TerminalColors.GreenBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN,
TerminalColors.BlueBgBlue: FOREGROUND_BLUE | BACKGROUND_BLUE,
TerminalColors.BlueBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_BLUE,
TerminalColors.CyanBgCyan: FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_BLUE,
TerminalColors.CyanBgBlack: BACKGROUND_GREEN | BACKGROUND_BLUE,
TerminalColors.MagentaBgMagenta: FOREGROUND_RED | FOREGROUND_BLUE | BACKGROUND_RED | BACKGROUND_BLUE,
TerminalColors.MagentaBgBlack: BACKGROUND_RED | BACKGROUND_BLUE,
TerminalColors.YellowBgYellow: FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_RED | BACKGROUND_GREEN,
TerminalColors.YellowBgBlack: BACKGROUND_RED | BACKGROUND_GREEN,
}
func writeStringWithColor(file *os.File, text string) {
fd := file.Fd()
i := 0
for i < len(text) {
// Find the escape
if text[i] != 033 {
i++
continue
}
// Find the 'm'
window := text[i:]
if len(window) > 8 {
window = window[:8]
}
m := strings.IndexByte(window, 'm')
if m == -1 {
i++
continue
}
m += i + 1
// Find the escape sequence
attributes, ok := windowsEscapeSequenceMap[text[i:m]]
if !ok {
i++
continue
}
// Write out the text before the escape sequence
file.WriteString(text[:i])
// Apply the escape sequence
text = text[m:]
i = 0
setConsoleTextAttribute.Call(fd, uintptr(attributes))
}
// Write out the remaining text
file.WriteString(text)
}
|