forked from Mirrors/bubbletea
Support receiving batched mouse events
Mouse events may trigger more than a single events simultaneously. Fixes #212.
This commit is contained in:
parent
db177f1939
commit
6301f93cb2
38
key.go
38
key.go
|
@ -292,9 +292,9 @@ var hexes = map[string]Key{
|
||||||
"1b4f44": {Type: KeyLeft, Alt: false},
|
"1b4f44": {Type: KeyLeft, Alt: false},
|
||||||
}
|
}
|
||||||
|
|
||||||
// readInput reads keypress and mouse input from a TTY and returns a message
|
// readInputs reads keypress and mouse inputs from a TTY and returns messages
|
||||||
// containing information about the key or mouse event accordingly.
|
// containing information about the key or mouse events accordingly.
|
||||||
func readInput(input io.Reader) (Msg, error) {
|
func readInputs(input io.Reader) ([]Msg, error) {
|
||||||
var buf [256]byte
|
var buf [256]byte
|
||||||
|
|
||||||
// Read and block
|
// Read and block
|
||||||
|
@ -305,20 +305,28 @@ func readInput(input io.Reader) (Msg, error) {
|
||||||
|
|
||||||
// See if it's a mouse event. For now we're parsing X10-type mouse events
|
// See if it's a mouse event. For now we're parsing X10-type mouse events
|
||||||
// only.
|
// only.
|
||||||
mouseEvent, err := parseX10MouseEvent(buf[:numBytes])
|
mouseEvent, err := parseX10MouseEvents(buf[:numBytes])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return MouseMsg(mouseEvent), nil
|
var m []Msg
|
||||||
|
for _, v := range mouseEvent {
|
||||||
|
m = append(m, MouseMsg(v))
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it a special sequence, like an arrow key?
|
// Is it a special sequence, like an arrow key?
|
||||||
if k, ok := sequences[string(buf[:numBytes])]; ok {
|
if k, ok := sequences[string(buf[:numBytes])]; ok {
|
||||||
return KeyMsg(Key{Type: k}), nil
|
return []Msg{
|
||||||
|
KeyMsg(Key{Type: k}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some of these need special handling
|
// Some of these need special handling
|
||||||
hex := fmt.Sprintf("%x", buf[:numBytes])
|
hex := fmt.Sprintf("%x", buf[:numBytes])
|
||||||
if k, ok := hexes[hex]; ok {
|
if k, ok := hexes[hex]; ok {
|
||||||
return KeyMsg(k), nil
|
return []Msg{
|
||||||
|
KeyMsg(k),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the alt key pressed? The buffer will be prefixed with an escape
|
// Is the alt key pressed? The buffer will be prefixed with an escape
|
||||||
|
@ -330,7 +338,9 @@ func readInput(input io.Reader) (Msg, error) {
|
||||||
if c == utf8.RuneError {
|
if c == utf8.RuneError {
|
||||||
return nil, errors.New("could not decode rune after removing initial escape")
|
return nil, errors.New("could not decode rune after removing initial escape")
|
||||||
}
|
}
|
||||||
return KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: []rune{c}}), nil
|
return []Msg{
|
||||||
|
KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: []rune{c}}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var runes []rune
|
var runes []rune
|
||||||
|
@ -353,15 +363,21 @@ func readInput(input io.Reader) (Msg, error) {
|
||||||
} else if len(runes) > 1 {
|
} else if len(runes) > 1 {
|
||||||
// We received multiple runes, so we know this isn't a control
|
// We received multiple runes, so we know this isn't a control
|
||||||
// character, sequence, and so on.
|
// character, sequence, and so on.
|
||||||
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
|
return []Msg{
|
||||||
|
KeyMsg(Key{Type: KeyRunes, Runes: runes}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the first rune a control character?
|
// Is the first rune a control character?
|
||||||
r := KeyType(runes[0])
|
r := KeyType(runes[0])
|
||||||
if numBytes == 1 && r <= keyUS || r == keyDEL {
|
if numBytes == 1 && r <= keyUS || r == keyDEL {
|
||||||
return KeyMsg(Key{Type: r}), nil
|
return []Msg{
|
||||||
|
KeyMsg(Key{Type: r}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Welp, it's just a regular, ol' single rune
|
// Welp, it's just a regular, ol' single rune
|
||||||
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
|
return []Msg{
|
||||||
|
KeyMsg(Key{Type: KeyRunes, Runes: runes}),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
10
key_test.go
10
key_test.go
|
@ -58,14 +58,18 @@ func TestReadInput(t *testing.T) {
|
||||||
"shift+tab": {'\x1b', '[', 'Z'},
|
"shift+tab": {'\x1b', '[', 'Z'},
|
||||||
} {
|
} {
|
||||||
t.Run(out, func(t *testing.T) {
|
t.Run(out, func(t *testing.T) {
|
||||||
msg, err := readInput(bytes.NewReader(in))
|
msgs, err := readInputs(bytes.NewReader(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if m, ok := msg.(KeyMsg); ok && m.String() != out {
|
if len(msgs) == 0 {
|
||||||
|
t.Fatalf("unexpected empty message list")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := msgs[0].(KeyMsg); ok && m.String() != out {
|
||||||
t.Fatalf(`expected a keymsg %q, got %q`, out, m)
|
t.Fatalf(`expected a keymsg %q, got %q`, out, m)
|
||||||
}
|
}
|
||||||
if m, ok := msg.(MouseMsg); ok && mouseEventTypes[m.Type] != out {
|
if m, ok := msgs[0].(MouseMsg); ok && mouseEventTypes[m.Type] != out {
|
||||||
t.Fatalf(`expected a mousemsg %q, got %q`, out, mouseEventTypes[m.Type])
|
t.Fatalf(`expected a mousemsg %q, got %q`, out, mouseEventTypes[m.Type])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
39
mouse.go
39
mouse.go
|
@ -1,6 +1,9 @@
|
||||||
package tea
|
package tea
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
// MouseMsg contains information about a mouse event and are sent to a programs
|
// MouseMsg contains information about a mouse event and are sent to a programs
|
||||||
// update function when mouse activity occurs. Note that the mouse must first
|
// update function when mouse activity occurs. Note that the mouse must first
|
||||||
|
@ -55,22 +58,33 @@ var mouseEventTypes = map[MouseEventType]string{
|
||||||
MouseMotion: "motion",
|
MouseMotion: "motion",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an X10-encoded mouse event; the simplest kind. The last release of
|
// Parse X10-encoded mouse events; the simplest kind. The last release of X10
|
||||||
// X10 was December 1986, by the way.
|
// was December 1986, by the way.
|
||||||
//
|
//
|
||||||
// X10 mouse events look like:
|
// X10 mouse events look like:
|
||||||
//
|
//
|
||||||
// ESC [M Cb Cx Cy
|
// ESC [M Cb Cx Cy
|
||||||
//
|
//
|
||||||
// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
|
||||||
func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
|
func parseX10MouseEvents(buf []byte) ([]MouseEvent, error) {
|
||||||
if len(buf) != 6 || string(buf[:3]) != "\x1b[M" {
|
var r []MouseEvent
|
||||||
return m, errors.New("not an X10 mouse event")
|
|
||||||
|
seq := []byte("\x1b[M")
|
||||||
|
if !bytes.Contains(buf, seq) {
|
||||||
|
return r, errors.New("not an X10 mouse event")
|
||||||
}
|
}
|
||||||
|
|
||||||
const byteOffset = 32
|
for _, v := range bytes.Split(buf, seq) {
|
||||||
|
if len(v) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(v) != 3 {
|
||||||
|
return r, errors.New("not an X10 mouse event")
|
||||||
|
}
|
||||||
|
|
||||||
e := buf[3] - byteOffset
|
var m MouseEvent
|
||||||
|
const byteOffset = 32
|
||||||
|
e := v[0] - byteOffset
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bitShift = 0b0000_0100
|
bitShift = 0b0000_0100
|
||||||
|
@ -125,8 +139,11 @@ func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// (1,1) is the upper left. We subtract 1 to normalize it to (0,0).
|
// (1,1) is the upper left. We subtract 1 to normalize it to (0,0).
|
||||||
m.X = int(buf[4]) - byteOffset - 1
|
m.X = int(v[1]) - byteOffset - 1
|
||||||
m.Y = int(buf[5]) - byteOffset - 1
|
m.Y = int(v[2]) - byteOffset - 1
|
||||||
|
|
||||||
return m, nil
|
r = append(r, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
117
mouse_test.go
117
mouse_test.go
|
@ -122,143 +122,172 @@ func TestParseX10MouseEvent(t *testing.T) {
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
name string
|
name string
|
||||||
buf []byte
|
buf []byte
|
||||||
expected MouseEvent
|
expected []MouseEvent
|
||||||
}{
|
}{
|
||||||
// Position.
|
// Position.
|
||||||
{
|
{
|
||||||
name: "zero position",
|
name: "zero position",
|
||||||
buf: encode(0b0010_0000, 0, 0),
|
buf: encode(0b0010_0000, 0, 0),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 0,
|
X: 0,
|
||||||
Y: 0,
|
Y: 0,
|
||||||
Type: MouseLeft,
|
Type: MouseLeft,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "max position",
|
name: "max position",
|
||||||
buf: encode(0b0010_0000, 222, 222), // Because 255 (max int8) - 32 - 1.
|
buf: encode(0b0010_0000, 222, 222), // Because 255 (max int8) - 32 - 1.
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 222,
|
X: 222,
|
||||||
Y: 222,
|
Y: 222,
|
||||||
Type: MouseLeft,
|
Type: MouseLeft,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
// Simple.
|
// Simple.
|
||||||
{
|
{
|
||||||
name: "left",
|
name: "left",
|
||||||
buf: encode(0b0000_0000, 32, 16),
|
buf: encode(0b0000_0000, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseLeft,
|
Type: MouseLeft,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "left in motion",
|
name: "left in motion",
|
||||||
buf: encode(0b0010_0000, 32, 16),
|
buf: encode(0b0010_0000, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseLeft,
|
Type: MouseLeft,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "middle",
|
name: "middle",
|
||||||
buf: encode(0b0000_0001, 32, 16),
|
buf: encode(0b0000_0001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseMiddle,
|
Type: MouseMiddle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "middle in motion",
|
name: "middle in motion",
|
||||||
buf: encode(0b0010_0001, 32, 16),
|
buf: encode(0b0010_0001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseMiddle,
|
Type: MouseMiddle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "right",
|
name: "right",
|
||||||
buf: encode(0b0000_0010, 32, 16),
|
buf: encode(0b0000_0010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRight,
|
Type: MouseRight,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "right in motion",
|
name: "right in motion",
|
||||||
buf: encode(0b0010_0010, 32, 16),
|
buf: encode(0b0010_0010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRight,
|
Type: MouseRight,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "motion",
|
name: "motion",
|
||||||
buf: encode(0b0010_0011, 32, 16),
|
buf: encode(0b0010_0011, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseMotion,
|
Type: MouseMotion,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "wheel up",
|
name: "wheel up",
|
||||||
buf: encode(0b0100_0000, 32, 16),
|
buf: encode(0b0100_0000, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseWheelUp,
|
Type: MouseWheelUp,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "wheel down",
|
name: "wheel down",
|
||||||
buf: encode(0b0100_0001, 32, 16),
|
buf: encode(0b0100_0001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseWheelDown,
|
Type: MouseWheelDown,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "release",
|
name: "release",
|
||||||
buf: encode(0b0000_0011, 32, 16),
|
buf: encode(0b0000_0011, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRelease,
|
Type: MouseRelease,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
// Combinations.
|
// Combinations.
|
||||||
{
|
{
|
||||||
name: "alt+right",
|
name: "alt+right",
|
||||||
buf: encode(0b0010_1010, 32, 16),
|
buf: encode(0b0010_1010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRight,
|
Type: MouseRight,
|
||||||
Alt: true,
|
Alt: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ctrl+right",
|
name: "ctrl+right",
|
||||||
buf: encode(0b0011_0010, 32, 16),
|
buf: encode(0b0011_0010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRight,
|
Type: MouseRight,
|
||||||
Ctrl: true,
|
Ctrl: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ctrl+alt+right",
|
name: "ctrl+alt+right",
|
||||||
buf: encode(0b0011_1010, 32, 16),
|
buf: encode(0b0011_1010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseRight,
|
Type: MouseRight,
|
||||||
|
@ -266,30 +295,36 @@ func TestParseX10MouseEvent(t *testing.T) {
|
||||||
Ctrl: true,
|
Ctrl: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "alt+wheel down",
|
name: "alt+wheel down",
|
||||||
buf: encode(0b0100_1001, 32, 16),
|
buf: encode(0b0100_1001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseWheelDown,
|
Type: MouseWheelDown,
|
||||||
Alt: true,
|
Alt: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ctrl+wheel down",
|
name: "ctrl+wheel down",
|
||||||
buf: encode(0b0101_0001, 32, 16),
|
buf: encode(0b0101_0001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseWheelDown,
|
Type: MouseWheelDown,
|
||||||
Ctrl: true,
|
Ctrl: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ctrl+alt+wheel down",
|
name: "ctrl+alt+wheel down",
|
||||||
buf: encode(0b0101_1001, 32, 16),
|
buf: encode(0b0101_1001, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseWheelDown,
|
Type: MouseWheelDown,
|
||||||
|
@ -297,55 +332,81 @@ func TestParseX10MouseEvent(t *testing.T) {
|
||||||
Ctrl: true,
|
Ctrl: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
// Unknown.
|
// Unknown.
|
||||||
{
|
{
|
||||||
name: "wheel with unknown bit",
|
name: "wheel with unknown bit",
|
||||||
buf: encode(0b0100_0010, 32, 16),
|
buf: encode(0b0100_0010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseUnknown,
|
Type: MouseUnknown,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unknown with modifier",
|
name: "unknown with modifier",
|
||||||
buf: encode(0b0100_1010, 32, 16),
|
buf: encode(0b0100_1010, 32, 16),
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: 32,
|
X: 32,
|
||||||
Y: 16,
|
Y: 16,
|
||||||
Type: MouseUnknown,
|
Type: MouseUnknown,
|
||||||
Alt: true,
|
Alt: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
// Overflow position.
|
// Overflow position.
|
||||||
{
|
{
|
||||||
name: "overflow position",
|
name: "overflow position",
|
||||||
buf: encode(0b0010_0000, 250, 223), // Because 255 (max int8) - 32 - 1.
|
buf: encode(0b0010_0000, 250, 223), // Because 255 (max int8) - 32 - 1.
|
||||||
expected: MouseEvent{
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
X: -6,
|
X: -6,
|
||||||
Y: -33,
|
Y: -33,
|
||||||
Type: MouseLeft,
|
Type: MouseLeft,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
// Batched events.
|
||||||
|
{
|
||||||
|
name: "batched events",
|
||||||
|
buf: append(encode(0b0010_0000, 32, 16), encode(0b0000_0011, 64, 32)...),
|
||||||
|
expected: []MouseEvent{
|
||||||
|
{
|
||||||
|
X: 32,
|
||||||
|
Y: 16,
|
||||||
|
Type: MouseLeft,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 64,
|
||||||
|
Y: 32,
|
||||||
|
Type: MouseRelease,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range tt {
|
for i := range tt {
|
||||||
tc := tt[i]
|
tc := tt[i]
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
actual, err := parseX10MouseEvent(tc.buf)
|
actual, err := parseX10MouseEvents(tc.buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v",
|
t.Fatalf("unexpected error for test: %v",
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.expected != actual {
|
for i := range tc.expected {
|
||||||
|
if tc.expected[i] != actual[i] {
|
||||||
t.Fatalf("expected %#v but got %#v",
|
t.Fatalf("expected %#v but got %#v",
|
||||||
tc.expected,
|
tc.expected[i],
|
||||||
actual,
|
actual[i],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,7 +438,7 @@ func TestParseX10MouseEvent_error(t *testing.T) {
|
||||||
tc := tt[i]
|
tc := tt[i]
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := parseX10MouseEvent(tc.buf)
|
_, err := parseX10MouseEvents(tc.buf)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error but got nil")
|
t.Fatalf("expected error but got nil")
|
||||||
|
|
4
tea.go
4
tea.go
|
@ -421,7 +421,7 @@ func (p *Program) StartReturningModel() (Model, error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := readInput(cancelReader)
|
msgs, err := readInputs(cancelReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, io.EOF) && !errors.Is(err, errCanceled) {
|
if !errors.Is(err, io.EOF) && !errors.Is(err, errCanceled) {
|
||||||
errs <- err
|
errs <- err
|
||||||
|
@ -430,8 +430,10 @@ func (p *Program) StartReturningModel() (Model, error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, msg := range msgs {
|
||||||
p.msgs <- msg
|
p.msgs <- msg
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
defer close(readLoopDone)
|
defer close(readLoopDone)
|
||||||
|
|
Loading…
Reference in New Issue