1// +build windows
2
3package pb
4
5import (
6	"errors"
7	"fmt"
8	"os"
9	"sync"
10	"syscall"
11	"unsafe"
12)
13
14var tty = os.Stdin
15
16var (
17	kernel32 = syscall.NewLazyDLL("kernel32.dll")
18
19	// GetConsoleScreenBufferInfo retrieves information about the
20	// specified console screen buffer.
21	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
22	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
23
24	// GetConsoleMode retrieves the current input mode of a console's
25	// input buffer or the current output mode of a console screen buffer.
26	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
27	getConsoleMode = kernel32.NewProc("GetConsoleMode")
28
29	// SetConsoleMode sets the input mode of a console's input buffer
30	// or the output mode of a console screen buffer.
31	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
32	setConsoleMode = kernel32.NewProc("SetConsoleMode")
33
34	// SetConsoleCursorPosition sets the cursor position in the
35	// specified console screen buffer.
36	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx
37	setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
38)
39
40type (
41	// Defines the coordinates of the upper left and lower right corners
42	// of a rectangle.
43	// See
44	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx
45	smallRect struct {
46		Left, Top, Right, Bottom int16
47	}
48
49	// Defines the coordinates of a character cell in a console screen
50	// buffer. The origin of the coordinate system (0,0) is at the top, left cell
51	// of the buffer.
52	// See
53	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx
54	coordinates struct {
55		X, Y int16
56	}
57
58	word int16
59
60	// Contains information about a console screen buffer.
61	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
62	consoleScreenBufferInfo struct {
63		dwSize              coordinates
64		dwCursorPosition    coordinates
65		wAttributes         word
66		srWindow            smallRect
67		dwMaximumWindowSize coordinates
68	}
69)
70
71// terminalWidth returns width of the terminal.
72func terminalWidth() (width int, err error) {
73	var info consoleScreenBufferInfo
74	_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
75	if e != 0 {
76		return 0, error(e)
77	}
78	return int(info.dwSize.X), nil
79}
80
81func getCursorPos() (pos coordinates, err error) {
82	var info consoleScreenBufferInfo
83	_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
84	if e != 0 {
85		return info.dwCursorPosition, error(e)
86	}
87	return info.dwCursorPosition, nil
88}
89
90func setCursorPos(pos coordinates) error {
91	_, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0)
92	if e != 0 {
93		return error(e)
94	}
95	return nil
96}
97
98var ErrPoolWasStarted = errors.New("Bar pool was started")
99
100var echoLocked bool
101var echoLockMutex sync.Mutex
102
103var oldState word
104
105func lockEcho() (quit chan int, err error) {
106	echoLockMutex.Lock()
107	defer echoLockMutex.Unlock()
108	if echoLocked {
109		err = ErrPoolWasStarted
110		return
111	}
112	echoLocked = true
113
114	if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 {
115		err = fmt.Errorf("Can't get terminal settings: %v", e)
116		return
117	}
118
119	newState := oldState
120	const ENABLE_ECHO_INPUT = 0x0004
121	const ENABLE_LINE_INPUT = 0x0002
122	newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))
123	if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 {
124		err = fmt.Errorf("Can't set terminal settings: %v", e)
125		return
126	}
127	return
128}
129
130func unlockEcho() (err error) {
131	echoLockMutex.Lock()
132	defer echoLockMutex.Unlock()
133	if !echoLocked {
134		return
135	}
136	echoLocked = false
137	if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 {
138		err = fmt.Errorf("Can't set terminal settings")
139	}
140	return
141}
142