forked from edsrzf/fineline
/
windows.go
172 lines (148 loc) · 4.02 KB
/
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
package fineline
import (
"fmt"
"os"
"strings"
"syscall"
"unsafe"
)
type LineReader struct {
lineReader
// console handle
h uintptr
rows int
origTerm uint32
}
var (
modkernel32 = loadDll("kernel32.dll")
procFillConsoleOutputCharacter = getSysProcAddr(modkernel32, "FillConsoleOutputCharacterW")
procGetConsoleMode = getSysProcAddr(modkernel32, "GetConsoleMode")
procGetConsoleScreenBufferInfo = getSysProcAddr(modkernel32, "GetConsoleScreenBufferInfo")
procSetConsoleCursorPosition = getSysProcAddr(modkernel32, "SetConsoleCursorPosition")
procSetConsoleMode = getSysProcAddr(modkernel32, "SetConsoleMode")
)
func loadDll(fname string) uint32 {
h, e := syscall.LoadLibrary(fname)
if e != 0 {
panic("LoadLibrary failed")
}
return h
}
func getSysProcAddr(m uint32, pname string) uintptr {
p, e := syscall.GetProcAddress(m, pname)
if e != 0 {
panic("GetProcAddress failed on " + pname)
}
return uintptr(p)
}
const (
_ENABLE_PROCESSED_INPUT = 0x0001
_ENABLE_LINE_INPUT = 0x0002
_ENABLE_ECHO_INPUT = 0x0004
_ENABLE_WINDOW_INPUT = 0x0008
_ENABLE_MOUSE_INPUT = 0x0010
_ENABLE_INSERT_MODE = 0x0020
_ENABLE_QUICK_EDIT_MODE = 0x0040
_ENABLE_EXTENDED_FLAGS = 0x0080
)
type coord struct {
x, y int16
}
type smallRect struct {
left, top, right, bottom int16
}
type consoleScreenBufferInfo struct {
dwSize coord
dwCursorPosition coord
wAttributes uint16
srWindow smallRect
dwMaximumWindowSize coord
}
// enable raw mode and gather metrics, like number of columns
func (l *LineReader) raw() {
// STD_OUTPUT_HANDLE
h, errno := syscall.GetStdHandle(-11)
t.h = uintptr(h)
if int32(t.h) == -1 {
err := os.Errno(errno)
panic(err)
}
ok, _, e := syscall.Syscall(procGetConsoleMode, 2,
t.h, uintptr(unsafe.Pointer(&t.origTerm)), 0)
if ok == 0 {
err := os.NewSyscallError("GetConsoleMode", int(e))
panic(err)
}
raw := t.origTerm
raw &^= _ENABLE_LINE_INPUT | _ENABLE_ECHO_INPUT | _ENABLE_PROCESSED_INPUT | _ENABLE_WINDOW_INPUT
ok, _, e = syscall.Syscall(procSetConsoleMode, 2, t.h, uintptr(raw), 0)
if ok == 0 {
err := os.NewSyscallError("SetConsoleMode", int(e))
panic(err)
}
win := t.getConsoleInfo()
t.cols = int(win.dwSize.x)
t.rows = int(win.dwSize.y)
t.buf = new(buffer)
}
func (l *LineReader) restore() {
ok, _, e := syscall.Syscall(procSetConsoleMode, 2, t.h, uintptr(t.origTerm), 0)
if ok == 0 {
err := os.NewSyscallError("SetConsoleMode", int(e))
panic(err)
}
}
// x is absolute, y is relative
func (l *LineReader) setCursor(x, y int) {
pos := t.getCursorPos()
pos.x = int16(x)
pos.y += int16(y)
t.setCursorPos(pos)
}
func (l *LineReader) eraseToEnd() {
pos := t.getCursorPos()
length := t.cols*(t.rows - int(pos.y) + 1) - int(pos.x)
t.fillConsoleOutputCharacter(' ' << 8, length, pos)
}
func (l *LineReader) printCandidates() {
str := "\n\x1b[0G" + strings.Join(t.candidates, "\n\x1b[0G") + "\n"
fmt.Print(str)
t.refreshLine()
}
func (l *LineReader) clearScreen() {
pos := coord{}
t.fillConsoleOutputCharacter(' ' << 8, t.cols*t.rows, pos)
t.setCursorPos(pos)
}
func (l *LineReader) getCursorPos() coord {
win := t.getConsoleInfo()
return win.dwCursorPosition
}
func (l *LineReader) setCursorPos(pos coord) {
ok, _, e := syscall.Syscall(procSetConsoleCursorPosition, 2, t.h,
*(*uintptr)(unsafe.Pointer(&pos)), 0)
if ok == 0 {
err := os.NewSyscallError("SetConsoleCursorPosition", int(e))
panic(err)
}
}
func (l *LineReader) getConsoleInfo() *consoleScreenBufferInfo {
var win consoleScreenBufferInfo
ok, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo, 2, t.h,
uintptr(unsafe.Pointer(&win)), 0)
if ok == 0 {
err := os.NewSyscallError("GetConsoleScreenBufferInfo", int(e))
panic(err)
}
return &win
}
func (l *LineReader) fillConsoleOutputCharacter(char uint16, length int, pos coord) {
coord := uintptr(unsafe.Pointer(&pos))
out := uintptr(unsafe.Pointer(new(int)))
ok, _, e := syscall.Syscall6(procFillConsoleOutputCharacter, 5,
t.h, uintptr(char), uintptr(length), coord, out, 0)
if ok == 0 {
err := os.NewSyscallError("FillConsoleOutputCharacter", int(e))
panic(err)
}
}