返回 DeepSeek-Reasonix
glyph_fit_windows.go
根目录 / internal / cli / glyph_fit_windows.go
1 //go:build windows
2
3 package cli
4
5 import (
6 "os"
7 "sync"
8 "unicode/utf16"
9 "unsafe"
10
11 "golang.org/x/sys/windows"
12 )
13
14 const (
15 consoleTextmodeBuffer = 1
16 codePageUSASCII = 20127
17 )
18
19 var (
20 kernel32DLL = windows.NewLazySystemDLL("kernel32.dll")
21 procCreateConsoleScreenBuffer = kernel32DLL.NewProc("CreateConsoleScreenBuffer")
22 procWideCharToMultiByte = kernel32DLL.NewProc("WideCharToMultiByte")
23 )
24
25 // measureBuffer is a never-activated screen buffer of the process's console:
26 // it shares the window's font, so writing a rune into it and reading the cursor
27 // back gives the width the console draws without touching the visible screen.
28 var measureBuffer = sync.OnceValue(func() windows.Handle {
29 h, _, _ := procCreateConsoleScreenBuffer.Call(
30 uintptr(windows.GENERIC_READ|windows.GENERIC_WRITE),
31 uintptr(windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE),
32 0, consoleTextmodeBuffer, 0)
33 if h == 0 || windows.Handle(h) == windows.InvalidHandle {
34 return windows.InvalidHandle
35 }
36 return windows.Handle(h)
37 })
38
39 // newConsoleGlyphFit returns nil unless out is a console the process can
40 // measure. A pseudoconsole measures ambiguous-width runes as one cell, so under
41 // Windows Terminal or an SSH session nothing is replaced.
42 func newConsoleGlyphFit(out *os.File) *glyphFit {
43 var mode uint32
44 if windows.GetConsoleMode(windows.Handle(out.Fd()), &mode) != nil {
45 return nil
46 }
47 h := measureBuffer()
48 if h == windows.InvalidHandle {
49 return nil
50 }
51 return newGlyphFit(func(r rune) int { return consoleCells(h, r) }, asciiBestFit)
52 }
53
54 func consoleCells(h windows.Handle, r rune) int {
55 if windows.SetConsoleCursorPosition(h, windows.Coord{}) != nil {
56 return 0
57 }
58 u := utf16.Encode([]rune{r})
59 var written uint32
60 if windows.WriteConsole(h, &u[0], uint32(len(u)), &written, nil) != nil {
61 return 0
62 }
63 var info windows.ConsoleScreenBufferInfo
64 if windows.GetConsoleScreenBufferInfo(h, &info) != nil || info.CursorPosition.Y != 0 {
65 return 0
66 }
67 return int(info.CursorPosition.X)
68 }
69
70 // asciiBestFit asks the OS for its US-ASCII best-fit of r, whose unmapped
71 // answer is the code page's default character.
72 func asciiBestFit(r rune) rune {
73 u := utf16.Encode([]rune{r})
74 var out [2]byte
75 n, _, _ := procWideCharToMultiByte.Call(codePageUSASCII, 0,
76 uintptr(unsafe.Pointer(&u[0])), uintptr(len(u)),
77 uintptr(unsafe.Pointer(&out[0])), uintptr(len(out)), 0, 0)
78 if n != 1 || out[0] < 0x20 || out[0] > 0x7e {
79 return 0
80 }
81 return rune(out[0])
82 }
83
83 lines GO