126 lines
2.7 KiB
Go
126 lines
2.7 KiB
Go
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
|
|
}
|