1package terminal
2
3import (
4	"bytes"
5	"syscall"
6	"unsafe"
7)
8
9var COORDINATE_SYSTEM_BEGIN Short = 0
10
11// shared variable to save the cursor location from CursorSave()
12var cursorLoc Coord
13
14type Cursor struct {
15	In  FileReader
16	Out FileWriter
17}
18
19func (c *Cursor) Up(n int) {
20	c.cursorMove(0, n)
21}
22
23func (c *Cursor) Down(n int) {
24	c.cursorMove(0, -1*n)
25}
26
27func (c *Cursor) Forward(n int) {
28	c.cursorMove(n, 0)
29}
30
31func (c *Cursor) Back(n int) {
32	c.cursorMove(-1*n, 0)
33}
34
35// save the cursor location
36func (c *Cursor) Save() {
37	cursorLoc, _ = c.Location(nil)
38}
39
40func (c *Cursor) Restore() {
41	handle := syscall.Handle(c.Out.Fd())
42	// restore it to the original position
43	procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursorLoc))))
44}
45
46func (cur Coord) CursorIsAtLineEnd(size *Coord) bool {
47	return cur.X == size.X
48}
49
50func (cur Coord) CursorIsAtLineBegin() bool {
51	return cur.X == 0
52}
53
54func (c *Cursor) cursorMove(x int, y int) {
55	handle := syscall.Handle(c.Out.Fd())
56
57	var csbi consoleScreenBufferInfo
58	procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
59
60	var cursor Coord
61	cursor.X = csbi.cursorPosition.X + Short(x)
62	cursor.Y = csbi.cursorPosition.Y + Short(y)
63
64	procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor))))
65}
66
67func (c *Cursor) NextLine(n int) {
68	c.Up(n)
69	c.HorizontalAbsolute(0)
70}
71
72func (c *Cursor) PreviousLine(n int) {
73	c.Down(n)
74	c.HorizontalAbsolute(0)
75}
76
77// for comparability purposes between windows
78// in windows we don't have to print out a new line
79func (c *Cursor) MoveNextLine(cur Coord, terminalSize *Coord) {
80	c.NextLine(1)
81}
82
83func (c *Cursor) HorizontalAbsolute(x int) {
84	handle := syscall.Handle(c.Out.Fd())
85
86	var csbi consoleScreenBufferInfo
87	procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
88
89	var cursor Coord
90	cursor.X = Short(x)
91	cursor.Y = csbi.cursorPosition.Y
92
93	if csbi.size.X < cursor.X {
94		cursor.X = csbi.size.X
95	}
96
97	procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor))))
98}
99
100func (c *Cursor) Show() {
101	handle := syscall.Handle(c.Out.Fd())
102
103	var cci consoleCursorInfo
104	procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
105	cci.visible = 1
106
107	procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
108}
109
110func (c *Cursor) Hide() {
111	handle := syscall.Handle(c.Out.Fd())
112
113	var cci consoleCursorInfo
114	procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
115	cci.visible = 0
116
117	procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
118}
119
120func (c *Cursor) Location(buf *bytes.Buffer) (Coord, error) {
121	handle := syscall.Handle(c.Out.Fd())
122
123	var csbi consoleScreenBufferInfo
124	procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
125
126	return csbi.cursorPosition, nil
127}
128
129func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) {
130	handle := syscall.Handle(c.Out.Fd())
131
132	var csbi consoleScreenBufferInfo
133	procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
134	// windows' coordinate system begins at (0, 0)
135	csbi.size.X--
136	csbi.size.Y--
137	return &csbi.size, nil
138}
139