summaryrefslogtreecommitdiff
path: root/vendor/github.com/chzyer/readline/rawreader_windows.go
blob: 073ef150a59542f641a6f6aacffad23c56414455 (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
// +build windows

package readline

import "unsafe"

const (
	VK_CANCEL   = 0x03
	VK_BACK     = 0x08
	VK_TAB      = 0x09
	VK_RETURN   = 0x0D
	VK_SHIFT    = 0x10
	VK_CONTROL  = 0x11
	VK_MENU     = 0x12
	VK_ESCAPE   = 0x1B
	VK_LEFT     = 0x25
	VK_UP       = 0x26
	VK_RIGHT    = 0x27
	VK_DOWN     = 0x28
	VK_DELETE   = 0x2E
	VK_LSHIFT   = 0xA0
	VK_RSHIFT   = 0xA1
	VK_LCONTROL = 0xA2
	VK_RCONTROL = 0xA3
)

// RawReader translate input record to ANSI escape sequence.
// To provides same behavior as unix terminal.
type RawReader struct {
	ctrlKey bool
	altKey  bool
}

func NewRawReader() *RawReader {
	r := new(RawReader)
	return r
}

// only process one action in one read
func (r *RawReader) Read(buf []byte) (int, error) {
	ir := new(_INPUT_RECORD)
	var read int
	var err error
next:
	err = kernel.ReadConsoleInputW(stdin,
		uintptr(unsafe.Pointer(ir)),
		1,
		uintptr(unsafe.Pointer(&read)),
	)
	if err != nil {
		return 0, err
	}
	if ir.EventType != EVENT_KEY {
		goto next
	}
	ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0]))
	if ker.bKeyDown == 0 { // keyup
		if r.ctrlKey || r.altKey {
			switch ker.wVirtualKeyCode {
			case VK_RCONTROL, VK_LCONTROL:
				r.ctrlKey = false
			case VK_MENU: //alt
				r.altKey = false
			}
		}
		goto next
	}

	if ker.unicodeChar == 0 {
		var target rune
		switch ker.wVirtualKeyCode {
		case VK_RCONTROL, VK_LCONTROL:
			r.ctrlKey = true
		case VK_MENU: //alt
			r.altKey = true
		case VK_LEFT:
			target = CharBackward
		case VK_RIGHT:
			target = CharForward
		case VK_UP:
			target = CharPrev
		case VK_DOWN:
			target = CharNext
		}
		if target != 0 {
			return r.write(buf, target)
		}
		goto next
	}
	char := rune(ker.unicodeChar)
	if r.ctrlKey {
		switch char {
		case 'A':
			char = CharLineStart
		case 'E':
			char = CharLineEnd
		case 'R':
			char = CharBckSearch
		case 'S':
			char = CharFwdSearch
		}
	} else if r.altKey {
		switch char {
		case VK_BACK:
			char = CharBackspace
		}
		return r.writeEsc(buf, char)
	}
	return r.write(buf, char)
}

func (r *RawReader) writeEsc(b []byte, char rune) (int, error) {
	b[0] = '\033'
	n := copy(b[1:], []byte(string(char)))
	return n + 1, nil
}

func (r *RawReader) write(b []byte, char rune) (int, error) {
	n := copy(b, []byte(string(char)))
	return n, nil
}

func (r *RawReader) Close() error {
	return nil
}