-
Notifications
You must be signed in to change notification settings - Fork 14
/
buffer_windows.go
150 lines (122 loc) · 3.24 KB
/
buffer_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
// Copyright 2013-2015 Bowery, Inc.
package prompt
import (
"unsafe"
)
var (
fillConsoleOutputCharacter = kernel.NewProc("FillConsoleOutputCharacterW")
setConsoleCursorPosition = kernel.NewProc("SetConsoleCursorPosition")
)
// Refresh rewrites the prompt and buffer.
func (buf *Buffer) Refresh() error {
csbi := new(consoleScreenBufferInfo)
ret, _, err := getConsoleScreenBufferInfo.Call(buf.Out.Fd(),
uintptr(unsafe.Pointer(csbi)))
if ret == 0 {
return err
}
// If we're not echoing just write prompt.
if !buf.Echo {
err = buf.delLine(csbi)
if err != nil {
return err
}
err = buf.mvLeftEdge(csbi)
if err != nil {
return err
}
_, err = buf.Out.Write([]byte(buf.Prompt))
return err
}
prLen := len(buf.Prompt)
start := 0
size := buf.size
pos := buf.pos
// Get slice range that should be visible.
for prLen+pos >= buf.Cols {
start++
size--
pos--
}
for prLen+size > buf.Cols {
size--
}
err = buf.delLine(csbi)
if err != nil {
return err
}
err = buf.mvLeftEdge(csbi)
if err != nil {
return err
}
_, err = buf.Out.Write([]byte(buf.Prompt))
if err != nil {
return err
}
_, err = buf.Out.Write(toBytes(buf.data[start : size+start]))
if err != nil {
return err
}
return buf.mvToCol(csbi, pos+prLen)
}
// ClsScreen clears the screen and refreshes.
func (buf *Buffer) ClsScreen() error {
var written uint32
coords := new(coord)
csbi := new(consoleScreenBufferInfo)
ret, _, err := getConsoleScreenBufferInfo.Call(buf.Out.Fd(),
uintptr(unsafe.Pointer(csbi)))
if ret == 0 {
return err
}
// Clear everything from 0,0.
ret, _, err = fillConsoleOutputCharacter.Call(buf.Out.Fd(), uintptr(' '),
uintptr(csbi.size.x*csbi.size.y), uintptr(*(*int32)(unsafe.Pointer(coords))),
uintptr(unsafe.Pointer(&written)))
if ret == 0 {
return err
}
// Set cursor at 0,0.
ret, _, err = setConsoleCursorPosition.Call(buf.Out.Fd(),
uintptr(*(*int32)(unsafe.Pointer(coords))))
if ret == 0 {
return err
}
return buf.Refresh()
}
// delLine deletes the line the csbi cursor is positioned on.
// TODO: Possible refresh jittering reason, instead we should copy the Unix
// code and write over contents and then remove everything to the right.
func (buf *Buffer) delLine(csbi *consoleScreenBufferInfo) error {
var written uint32
coords := &coord{y: csbi.cursorPosition.y}
ret, _, err := fillConsoleOutputCharacter.Call(buf.Out.Fd(), uintptr(' '),
uintptr(csbi.size.x), uintptr(*(*int32)(unsafe.Pointer(coords))),
uintptr(unsafe.Pointer(&written)))
if ret == 0 {
return err
}
return nil
}
// mvLeftEdge moves the cursor to the beginning of the line the csbi cursor
// is positioned on.
func (buf *Buffer) mvLeftEdge(csbi *consoleScreenBufferInfo) error {
coords := &coord{y: csbi.cursorPosition.y}
ret, _, err := setConsoleCursorPosition.Call(buf.Out.Fd(),
uintptr(*(*int32)(unsafe.Pointer(coords))))
if ret == 0 {
return err
}
return nil
}
// mvTolCol moves the cursor to the col on the line the csbi cursor is
// positioned on.
func (buf *Buffer) mvToCol(csbi *consoleScreenBufferInfo, x int) error {
coords := &coord{x: int16(x), y: csbi.cursorPosition.y}
ret, _, err := setConsoleCursorPosition.Call(buf.Out.Fd(),
uintptr(*(*int32)(unsafe.Pointer(coords))))
if ret == 0 {
return err
}
return nil
}