-
Notifications
You must be signed in to change notification settings - Fork 7
/
driver.go
125 lines (105 loc) · 2.71 KB
/
driver.go
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
package input
import (
"bytes"
"io"
"unicode/utf8"
"github.com/erikgeiser/coninput"
"github.com/muesli/cancelreader"
)
// Driver represents an ANSI terminal input Driver.
// It reads input events and parses ANSI sequences from the terminal input
// buffer.
type Driver struct {
rd cancelreader.CancelReader
table map[string]Key // table is a lookup table for key sequences.
term string // term is the terminal name $TERM.
// paste is the bracketed paste mode buffer.
// When nil, bracketed paste mode is disabled.
paste []byte
buf [256]byte // do we need a larger buffer?
// prevMouseState keeps track of the previous mouse state to determine mouse
// up button events.
prevMouseState coninput.ButtonState // nolint: unused
flags int // control the behavior of the driver.
}
// NewDriver returns a new ANSI input driver.
// This driver uses ANSI control codes compatible with VT100/VT200 terminals,
// and XTerm. It supports reading Terminfo databases to overwrite the default
// key sequences.
func NewDriver(r io.Reader, term string, flags int) (*Driver, error) {
d := new(Driver)
cr, err := newCancelreader(r)
if err != nil {
return nil, err
}
d.rd = cr
d.table = buildKeysTable(flags, term)
d.term = term
d.flags = flags
return d, nil
}
// Cancel cancels the underlying reader.
func (d *Driver) Cancel() bool {
return d.rd.Cancel()
}
// Close closes the underlying reader.
func (d *Driver) Close() error {
return d.rd.Close()
}
func (d *Driver) readEvents() (e []Event, err error) {
nb, err := d.rd.Read(d.buf[:])
if err != nil {
return nil, err
}
buf := d.buf[:nb]
// Lookup table first
if bytes.HasPrefix(buf, []byte{'\x1b'}) {
if k, ok := d.table[string(buf)]; ok {
e = append(e, KeyDownEvent(k))
return
}
}
var i int
for i < len(buf) {
nb, ev := ParseSequence(buf[i:])
// Handle bracketed-paste
if d.paste != nil {
if _, ok := ev.(PasteEndEvent); !ok {
d.paste = append(d.paste, buf[i])
i++
continue
}
}
switch ev.(type) {
case UnknownCsiEvent, UnknownSs3Event, UnknownEvent:
// If the sequence is not recognized by the parser, try looking it up.
if k, ok := d.table[string(buf[i:i+nb])]; ok {
ev = KeyDownEvent(k)
}
case PasteStartEvent:
d.paste = []byte{}
case PasteEndEvent:
// Decode the captured data into runes.
var paste []rune
for len(d.paste) > 0 {
r, w := utf8.DecodeRune(d.paste)
if r != utf8.RuneError {
paste = append(paste, r)
}
d.paste = d.paste[w:]
}
d.paste = nil // reset the buffer
e = append(e, PasteEvent(paste))
case nil:
i++
continue
}
if mevs, ok := ev.(MultiEvent); ok {
e = append(e, []Event(mevs)...)
} else {
e = append(e, ev)
}
i += nb
}
return
}