1// +build windows
2
3package readline
4
5import "unsafe"
6
7const (
8	VK_CANCEL   = 0x03
9	VK_BACK     = 0x08
10	VK_TAB      = 0x09
11	VK_RETURN   = 0x0D
12	VK_SHIFT    = 0x10
13	VK_CONTROL  = 0x11
14	VK_MENU     = 0x12
15	VK_ESCAPE   = 0x1B
16	VK_LEFT     = 0x25
17	VK_UP       = 0x26
18	VK_RIGHT    = 0x27
19	VK_DOWN     = 0x28
20	VK_DELETE   = 0x2E
21	VK_LSHIFT   = 0xA0
22	VK_RSHIFT   = 0xA1
23	VK_LCONTROL = 0xA2
24	VK_RCONTROL = 0xA3
25)
26
27// RawReader translate input record to ANSI escape sequence.
28// To provides same behavior as unix terminal.
29type RawReader struct {
30	ctrlKey bool
31	altKey  bool
32}
33
34func NewRawReader() *RawReader {
35	r := new(RawReader)
36	return r
37}
38
39// only process one action in one read
40func (r *RawReader) Read(buf []byte) (int, error) {
41	ir := new(_INPUT_RECORD)
42	var read int
43	var err error
44next:
45	err = kernel.ReadConsoleInputW(stdin,
46		uintptr(unsafe.Pointer(ir)),
47		1,
48		uintptr(unsafe.Pointer(&read)),
49	)
50	if err != nil {
51		return 0, err
52	}
53	if ir.EventType != EVENT_KEY {
54		goto next
55	}
56	ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0]))
57	if ker.bKeyDown == 0 { // keyup
58		if r.ctrlKey || r.altKey {
59			switch ker.wVirtualKeyCode {
60			case VK_RCONTROL, VK_LCONTROL:
61				r.ctrlKey = false
62			case VK_MENU: //alt
63				r.altKey = false
64			}
65		}
66		goto next
67	}
68
69	if ker.unicodeChar == 0 {
70		var target rune
71		switch ker.wVirtualKeyCode {
72		case VK_RCONTROL, VK_LCONTROL:
73			r.ctrlKey = true
74		case VK_MENU: //alt
75			r.altKey = true
76		case VK_LEFT:
77			target = CharBackward
78		case VK_RIGHT:
79			target = CharForward
80		case VK_UP:
81			target = CharPrev
82		case VK_DOWN:
83			target = CharNext
84		}
85		if target != 0 {
86			return r.write(buf, target)
87		}
88		goto next
89	}
90	char := rune(ker.unicodeChar)
91	if r.ctrlKey {
92		switch char {
93		case 'A':
94			char = CharLineStart
95		case 'E':
96			char = CharLineEnd
97		case 'R':
98			char = CharBckSearch
99		case 'S':
100			char = CharFwdSearch
101		}
102	} else if r.altKey {
103		switch char {
104		case VK_BACK:
105			char = CharBackspace
106		}
107		return r.writeEsc(buf, char)
108	}
109	return r.write(buf, char)
110}
111
112func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
113	b[0] = '\033'
114	n := copy(b[1:], []byte(string(char)))
115	return n + 1, nil
116}
117
118func (r *RawReader) write(b []byte, char rune) (int, error) {
119	n := copy(b, []byte(string(char)))
120	return n, nil
121}
122
123func (r *RawReader) Close() error {
124	return nil
125}
126