-
Notifications
You must be signed in to change notification settings - Fork 45
/
console_windows.go
188 lines (167 loc) · 3.68 KB
/
console_windows.go
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//go:build windows
package log
import (
"io"
"os"
"sync"
"syscall"
"unsafe"
)
func isTerminal(fd uintptr, _, _ string) bool {
var mode uint32
err := syscall.GetConsoleMode(syscall.Handle(fd), &mode)
if err != nil {
return false
}
return true
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode").Call
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute").Call
muConsole sync.Mutex
onceConsole sync.Once
isvt bool
)
// WriteEntry implements Writer
func (w *ConsoleWriter) WriteEntry(e *Entry) (n int, err error) {
onceConsole.Do(func() { isvt = isVirtualTerminal() })
out := w.Writer
if out == nil {
out = os.Stderr
}
if isvt {
n, err = w.write(out, e.buf)
} else {
n, err = w.writew(out, e.buf)
}
return
}
func (w *ConsoleWriter) writew(out io.Writer, p []byte) (n int, err error) {
muConsole.Lock()
defer muConsole.Unlock()
b := bbpool.Get().(*bb)
b.B = b.B[:0]
defer bbpool.Put(b)
n, err = w.write(b, p)
if err != nil {
return
}
n = 0
// uintptr color
const (
Black = 0
Blue = 1
Green = 2
Aqua = 3
Red = 4
Purple = 5
Yellow = 6
White = 7
Gray = 8
)
// color print
var cprint = func(color uintptr, b []byte) {
if color != White {
procSetConsoleTextAttribute(uintptr(syscall.Stderr), color)
defer procSetConsoleTextAttribute(uintptr(syscall.Stderr), White)
}
var i int
i, err = out.Write(b)
n += i
}
b2 := bbpool.Get().(*bb)
b2.B = b2.B[:0]
defer bbpool.Put(b2)
var color uintptr = White
var length = len(b.B)
var c uint32
for i := 0; i < length; i++ {
if b.B[i] == '\x1b' {
switch {
case length-i > 3 &&
b.B[i+1] == '[' &&
'0' <= b.B[i+2] && b.B[i+2] <= '9' &&
b.B[i+3] == 'm':
c = uint32(b.B[i+2] - '0')
i += 3
case length-i > 4 &&
b.B[i+1] == '[' &&
'0' <= b.B[i+2] && b.B[i+2] <= '9' &&
'0' <= b.B[i+3] && b.B[i+3] <= '9' &&
b.B[i+4] == 'm':
c = uint32(b.B[i+2]-'0')*10 + uint32(b.B[i+3]-'0')
i += 4
}
if len(b2.B) > 0 {
cprint(color, b2.B)
}
b2.B = b2.B[:0]
switch c {
case 0: // Reset
color = White
case 30: // Black
color = Black
case 90: // Gray
color = Gray
case 31, 91: // Red, BrightRed
color = Red
case 32, 92: // Green, BrightGreen
color = Green
case 33, 93: // Yellow, BrightYellow
color = Yellow
case 34, 94: // Blue, BrightBlue
color = Blue
case 35, 95: // Magenta, BrightMagenta
color = Purple
case 36, 96: // Cyan, BrightCyan
color = Aqua
case 37, 97: // White, BrightWhite
color = White
default:
color = White
}
} else {
b2.B = append(b2.B, b.B[i])
}
}
if len(b2.B) != 0 {
cprint(White, b2.B)
}
return
}
func isVirtualTerminal() bool {
var h syscall.Handle
var b [64]uint16
var n uint32
// open registry
err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, syscall.StringToUTF16Ptr(`SOFTWARE\Microsoft\Windows NT\CurrentVersion`), 0, syscall.KEY_READ, &h)
if err != nil {
return false
}
defer syscall.RegCloseKey(h)
// read windows build number
n = uint32(len(b))
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(`CurrentBuild`), nil, nil, (*byte)(unsafe.Pointer(&b[0])), &n)
if err != nil {
return false
}
for i := 0; i < len(b); i++ {
if b[i] == 0 {
break
}
n = n*10 + uint32(b[i]-'0')
}
// return if lower than windows 10 16257
if n < 16257 {
return false
}
// get console mode
err = syscall.GetConsoleMode(syscall.Stderr, &n)
if err != nil {
return false
}
// enable ENABLE_VIRTUAL_TERMINAL_PROCESSING
ret, _, _ := procSetConsoleMode(uintptr(syscall.Stderr), uintptr(n|0x4))
return ret != 0
}