summaryrefslogtreecommitdiff
path: root/vendor/github.com/Azure/go-ansiterm/winterm/api.go
blob: 6055e33b912b2be3eafb60c77684f1843e174f1b (plain)
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
// +build windows

package winterm

import (
	"fmt"
	"syscall"
	"unsafe"
)

//===========================================================================================================
// IMPORTANT NOTE:
//
//	The methods below make extensive use of the "unsafe" package to obtain the required pointers.
//	Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
//	variables) the pointers reference *before* the API completes.
//
//  As a result, in those cases, the code must hint that the variables remain in active by invoking the
//	dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
//	require unsafe pointers.
//
//	If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
//	the garbage collector the variables remain in use if:
//
//	-- The value is not a pointer (e.g., int32, struct)
//	-- The value is not referenced by the method after passing the pointer to Windows
//
//	See http://golang.org/doc/go1.3.
//===========================================================================================================

var (
	kernel32DLL = syscall.NewLazyDLL("kernel32.dll")

	getConsoleCursorInfoProc       = kernel32DLL.NewProc("GetConsoleCursorInfo")
	setConsoleCursorInfoProc       = kernel32DLL.NewProc("SetConsoleCursorInfo")
	setConsoleCursorPositionProc   = kernel32DLL.NewProc("SetConsoleCursorPosition")
	setConsoleModeProc             = kernel32DLL.NewProc("SetConsoleMode")
	getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
	setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
	scrollConsoleScreenBufferProc  = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
	setConsoleTextAttributeProc    = kernel32DLL.NewProc("SetConsoleTextAttribute")
	setConsoleWindowInfoProc       = kernel32DLL.NewProc("SetConsoleWindowInfo")
	writeConsoleOutputProc         = kernel32DLL.NewProc("WriteConsoleOutputW")
	readConsoleInputProc           = kernel32DLL.NewProc("ReadConsoleInputW")
	waitForSingleObjectProc        = kernel32DLL.NewProc("WaitForSingleObject")
)

// Windows Console constants
const (
	// Console modes
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
	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
	ENABLE_AUTO_POSITION          = 0x0100
	ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200

	ENABLE_PROCESSED_OUTPUT            = 0x0001
	ENABLE_WRAP_AT_EOL_OUTPUT          = 0x0002
	ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
	DISABLE_NEWLINE_AUTO_RETURN        = 0x0008
	ENABLE_LVB_GRID_WORLDWIDE          = 0x0010

	// Character attributes
	// Note:
	// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
	//    Clearing all foreground or background colors results in black; setting all creates white.
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
	FOREGROUND_BLUE      uint16 = 0x0001
	FOREGROUND_GREEN     uint16 = 0x0002
	FOREGROUND_RED       uint16 = 0x0004
	FOREGROUND_INTENSITY uint16 = 0x0008
	FOREGROUND_MASK      uint16 = 0x000F

	BACKGROUND_BLUE      uint16 = 0x0010
	BACKGROUND_GREEN     uint16 = 0x0020
	BACKGROUND_RED       uint16 = 0x0040
	BACKGROUND_INTENSITY uint16 = 0x0080
	BACKGROUND_MASK      uint16 = 0x00F0

	COMMON_LVB_MASK          uint16 = 0xFF00
	COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
	COMMON_LVB_UNDERSCORE    uint16 = 0x8000

	// Input event types
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
	KEY_EVENT                = 0x0001
	MOUSE_EVENT              = 0x0002
	WINDOW_BUFFER_SIZE_EVENT = 0x0004
	MENU_EVENT               = 0x0008
	FOCUS_EVENT              = 0x0010

	// WaitForSingleObject return codes
	WAIT_ABANDONED = 0x00000080
	WAIT_FAILED    = 0xFFFFFFFF
	WAIT_SIGNALED  = 0x0000000
	WAIT_TIMEOUT   = 0x00000102

	// WaitForSingleObject wait duration
	WAIT_INFINITE       = 0xFFFFFFFF
	WAIT_ONE_SECOND     = 1000
	WAIT_HALF_SECOND    = 500
	WAIT_QUARTER_SECOND = 250
)

// Windows API Console types
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
type (
	CHAR_INFO struct {
		UnicodeChar uint16
		Attributes  uint16
	}

	CONSOLE_CURSOR_INFO struct {
		Size    uint32
		Visible int32
	}

	CONSOLE_SCREEN_BUFFER_INFO struct {
		Size              COORD
		CursorPosition    COORD
		Attributes        uint16
		Window            SMALL_RECT
		MaximumWindowSize COORD
	}

	COORD struct {
		X int16
		Y int16
	}

	SMALL_RECT struct {
		Left   int16
		Top    int16
		Right  int16
		Bottom int16
	}

	// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
	INPUT_RECORD struct {
		EventType uint16
		KeyEvent  KEY_EVENT_RECORD
	}

	KEY_EVENT_RECORD struct {
		KeyDown         int32
		RepeatCount     uint16
		VirtualKeyCode  uint16
		VirtualScanCode uint16
		UnicodeChar     uint16
		ControlKeyState uint32
	}

	WINDOW_BUFFER_SIZE struct {
		Size COORD
	}
)

// boolToBOOL converts a Go bool into a Windows int32.
func boolToBOOL(f bool) int32 {
	if f {
		return int32(1)
	} else {
		return int32(0)
	}
}

// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
	r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
	return checkError(r1, r2, err)
}

// SetConsoleCursorInfo sets the size and visiblity of the console cursor.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
	r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
	return checkError(r1, r2, err)
}

// SetConsoleCursorPosition location of the console cursor.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
	r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
	use(coord)
	return checkError(r1, r2, err)
}

// GetConsoleMode gets the console mode for given file descriptor
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
func GetConsoleMode(handle uintptr) (mode uint32, err error) {
	err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
	return mode, err
}

// SetConsoleMode sets the console mode for given file descriptor
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
func SetConsoleMode(handle uintptr, mode uint32) error {
	r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
	use(mode)
	return checkError(r1, r2, err)
}

// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
	info := CONSOLE_SCREEN_BUFFER_INFO{}
	err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
	if err != nil {
		return nil, err
	}
	return &info, nil
}

func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
	r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
	use(scrollRect)
	use(clipRect)
	use(destOrigin)
	use(char)
	return checkError(r1, r2, err)
}

// SetConsoleScreenBufferSize sets the size of the console screen buffer.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
	r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
	use(coord)
	return checkError(r1, r2, err)
}

// SetConsoleTextAttribute sets the attributes of characters written to the
// console screen buffer by the WriteFile or WriteConsole function.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
	r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
	use(attribute)
	return checkError(r1, r2, err)
}

// SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
// Note that the size and location must be within and no larger than the backing console screen buffer.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
	r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
	use(isAbsolute)
	use(rect)
	return checkError(r1, r2, err)
}

// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
	r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
	use(buffer)
	use(bufferSize)
	use(bufferCoord)
	return checkError(r1, r2, err)
}

// ReadConsoleInput reads (and removes) data from the console input buffer.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
	r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
	use(buffer)
	return checkError(r1, r2, err)
}

// WaitForSingleObject waits for the passed handle to be signaled.
// It returns true if the handle was signaled; false otherwise.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
	r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
	switch r1 {
	case WAIT_ABANDONED, WAIT_TIMEOUT:
		return false, nil
	case WAIT_SIGNALED:
		return true, nil
	}
	use(msWait)
	return false, err
}

// String helpers
func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
	return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
}

func (coord COORD) String() string {
	return fmt.Sprintf("%v,%v", coord.X, coord.Y)
}

func (rect SMALL_RECT) String() string {
	return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
}

// checkError evaluates the results of a Windows API call and returns the error if it failed.
func checkError(r1, r2 uintptr, err error) error {
	// Windows APIs return non-zero to indicate success
	if r1 != 0 {
		return nil
	}

	// Return the error if provided, otherwise default to EINVAL
	if err != nil {
		return err
	}
	return syscall.EINVAL
}

// coordToPointer converts a COORD into a uintptr (by fooling the type system).
func coordToPointer(c COORD) uintptr {
	// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
	return uintptr(*((*uint32)(unsafe.Pointer(&c))))
}

// use is a no-op, but the compiler cannot see that it is.
// Calling use(p) ensures that p is kept live until that point.
func use(p interface{}) {}