File: rawreader_windows.go

package info (click to toggle)
golang-github-bettercap-readline 1.4%2Bgit20210228.655e48b-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 304 kB
  • sloc: makefile: 6
file content (132 lines) | stat: -rw-r--r-- 2,436 bytes parent folder | download | duplicates (2)
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
126
127
128
129
130
131
132
// +build windows

package readline

import (
	"fmt"
	"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 uint32
	var err error

	pread := &read

next:
	_, _, err = kernel.ReadConsoleInputW.Call(stdin,
		uintptr(unsafe.Pointer(ir)),
		1,
		uintptr(unsafe.Pointer(pread)),
	)
	if err != nil {
		fmt.Printf("\nerr=%v ir=%v read=%v pread=%v\n", err, ir, read, pread)
		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
}