1// +build windows
2
3package readline
4
5import (
6	"reflect"
7	"syscall"
8	"unsafe"
9)
10
11var (
12	kernel = NewKernel()
13	stdout = uintptr(syscall.Stdout)
14	stdin  = uintptr(syscall.Stdin)
15)
16
17type Kernel struct {
18	SetConsoleCursorPosition,
19	SetConsoleTextAttribute,
20	FillConsoleOutputCharacterW,
21	FillConsoleOutputAttribute,
22	ReadConsoleInputW,
23	GetConsoleScreenBufferInfo,
24	GetConsoleCursorInfo,
25	GetStdHandle CallFunc
26}
27
28type short int16
29type word uint16
30type dword uint32
31type wchar uint16
32
33type _COORD struct {
34	x short
35	y short
36}
37
38func (c *_COORD) ptr() uintptr {
39	return uintptr(*(*int32)(unsafe.Pointer(c)))
40}
41
42const (
43	EVENT_KEY                = 0x0001
44	EVENT_MOUSE              = 0x0002
45	EVENT_WINDOW_BUFFER_SIZE = 0x0004
46	EVENT_MENU               = 0x0008
47	EVENT_FOCUS              = 0x0010
48)
49
50type _KEY_EVENT_RECORD struct {
51	bKeyDown          int32
52	wRepeatCount      word
53	wVirtualKeyCode   word
54	wVirtualScanCode  word
55	unicodeChar       wchar
56	dwControlKeyState dword
57}
58
59// KEY_EVENT_RECORD          KeyEvent;
60// MOUSE_EVENT_RECORD        MouseEvent;
61// WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
62// MENU_EVENT_RECORD         MenuEvent;
63// FOCUS_EVENT_RECORD        FocusEvent;
64type _INPUT_RECORD struct {
65	EventType word
66	Padding   uint16
67	Event     [16]byte
68}
69
70type _CONSOLE_SCREEN_BUFFER_INFO struct {
71	dwSize              _COORD
72	dwCursorPosition    _COORD
73	wAttributes         word
74	srWindow            _SMALL_RECT
75	dwMaximumWindowSize _COORD
76}
77
78type _SMALL_RECT struct {
79	left   short
80	top    short
81	right  short
82	bottom short
83}
84
85type _CONSOLE_CURSOR_INFO struct {
86	dwSize   dword
87	bVisible bool
88}
89
90type CallFunc func(u ...uintptr) error
91
92func NewKernel() *Kernel {
93	k := &Kernel{}
94	kernel32 := syscall.NewLazyDLL("kernel32.dll")
95	v := reflect.ValueOf(k).Elem()
96	t := v.Type()
97	for i := 0; i < t.NumField(); i++ {
98		name := t.Field(i).Name
99		f := kernel32.NewProc(name)
100		v.Field(i).Set(reflect.ValueOf(k.Wrap(f)))
101	}
102	return k
103}
104
105func (k *Kernel) Wrap(p *syscall.LazyProc) CallFunc {
106	return func(args ...uintptr) error {
107		var r0 uintptr
108		var e1 syscall.Errno
109		size := uintptr(len(args))
110		if len(args) <= 3 {
111			buf := make([]uintptr, 3)
112			copy(buf, args)
113			r0, _, e1 = syscall.Syscall(p.Addr(), size,
114				buf[0], buf[1], buf[2])
115		} else {
116			buf := make([]uintptr, 6)
117			copy(buf, args)
118			r0, _, e1 = syscall.Syscall6(p.Addr(), size,
119				buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
120			)
121		}
122
123		if int(r0) == 0 {
124			if e1 != 0 {
125				return error(e1)
126			} else {
127				return syscall.EINVAL
128			}
129		}
130		return nil
131	}
132
133}
134
135func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) {
136	t := new(_CONSOLE_SCREEN_BUFFER_INFO)
137	err := kernel.GetConsoleScreenBufferInfo(
138		stdout,
139		uintptr(unsafe.Pointer(t)),
140	)
141	return t, err
142}
143
144func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) {
145	t := new(_CONSOLE_CURSOR_INFO)
146	err := kernel.GetConsoleCursorInfo(stdout, uintptr(unsafe.Pointer(t)))
147	return t, err
148}
149
150func SetConsoleCursorPosition(c *_COORD) error {
151	return kernel.SetConsoleCursorPosition(stdout, c.ptr())
152}
153