@@ -21,6 +21,7 @@ import (
21
21
//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
22
22
//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
23
23
//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
24
+ //go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
24
25
//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
25
26
//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
26
27
//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
@@ -44,6 +45,7 @@ import (
44
45
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
45
46
//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
46
47
//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
48
+ //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
47
49
//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
48
50
//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll"
49
51
63
65
_DuplicateHandle ,
64
66
_ExitProcess ,
65
67
_FreeEnvironmentStringsW ,
68
+ _GetConsoleMode ,
66
69
_GetEnvironmentStringsW ,
67
70
_GetProcAddress ,
68
71
_GetProcessAffinityMask ,
86
89
_VirtualFree ,
87
90
_WSAGetOverlappedResult ,
88
91
_WaitForSingleObject ,
92
+ _WriteConsoleW ,
89
93
_WriteFile ,
90
94
_timeBeginPeriod stdFunction
91
95
@@ -254,11 +258,90 @@ func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
254
258
// assume fd is real windows handle.
255
259
handle = fd
256
260
}
261
+ isASCII := true
262
+ b := (* [1 << 30 ]byte )(buf )[:n ]
263
+ for _ , x := range b {
264
+ if x >= 0x80 {
265
+ isASCII = false
266
+ break
267
+ }
268
+ }
269
+
270
+ if ! isASCII {
271
+ var m uint32
272
+ isConsole := stdcall2 (_GetConsoleMode , handle , uintptr (unsafe .Pointer (& m ))) != 0
273
+ // If this is a console output, various non-unicode code pages can be in use.
274
+ // Use the dedicated WriteConsole call to ensure unicode is printed correctly.
275
+ if isConsole {
276
+ return int32 (writeConsole (handle , buf , n ))
277
+ }
278
+ }
257
279
var written uint32
258
280
stdcall5 (_WriteFile , handle , uintptr (buf ), uintptr (n ), uintptr (unsafe .Pointer (& written )), 0 )
259
281
return int32 (written )
260
282
}
261
283
284
+ var (
285
+ utf16ConsoleBack [1000 ]uint16
286
+ utf16ConsoleBackLock mutex
287
+ )
288
+
289
+ // writeConsole writes bufLen bytes from buf to the console File.
290
+ // It returns the number of bytes written.
291
+ func writeConsole (handle uintptr , buf unsafe.Pointer , bufLen int32 ) int {
292
+ const surr2 = (surrogateMin + surrogateMax + 1 ) / 2
293
+
294
+ // Do not use defer for unlock. May cause issues when printing a panic.
295
+ lock (& utf16ConsoleBackLock )
296
+
297
+ b := (* [1 << 30 ]byte )(buf )[:bufLen ]
298
+ s := * (* string )(unsafe .Pointer (& b ))
299
+
300
+ utf16tmp := utf16ConsoleBack [:]
301
+
302
+ total := len (s )
303
+ w := 0
304
+ for len (s ) > 0 {
305
+ if w >= len (utf16tmp )- 2 {
306
+ writeConsoleUTF16 (handle , utf16tmp [:w ])
307
+ w = 0
308
+ }
309
+ r , n := charntorune (s )
310
+ s = s [n :]
311
+ if r < 0x10000 {
312
+ utf16tmp [w ] = uint16 (r )
313
+ w ++
314
+ } else {
315
+ r -= 0x10000
316
+ utf16tmp [w ] = surrogateMin + uint16 (r >> 10 )& 0x3ff
317
+ utf16tmp [w + 1 ] = surr2 + uint16 (r )& 0x3ff
318
+ w += 2
319
+ }
320
+ }
321
+ writeConsoleUTF16 (handle , utf16tmp [:w ])
322
+ unlock (& utf16ConsoleBackLock )
323
+ return total
324
+ }
325
+
326
+ // writeConsoleUTF16 is the dedicated windows calls that correctly prints
327
+ // to the console regardless of the current code page. Input is utf-16 code points.
328
+ // The handle must be a console handle.
329
+ func writeConsoleUTF16 (handle uintptr , b []uint16 ) {
330
+ l := uint32 (len (b ))
331
+ if l == 0 {
332
+ return
333
+ }
334
+ var written uint32
335
+ stdcall5 (_WriteConsoleW ,
336
+ handle ,
337
+ uintptr (unsafe .Pointer (& b [0 ])),
338
+ uintptr (l ),
339
+ uintptr (unsafe .Pointer (& written )),
340
+ 0 ,
341
+ )
342
+ return
343
+ }
344
+
262
345
//go:nosplit
263
346
func semasleep (ns int64 ) int32 {
264
347
// store ms in ns to save stack space
0 commit comments