Skip to content

Commit

Permalink
feat(mouse): add extended mouse & shift key support
Browse files Browse the repository at this point in the history
Support SGR(1006) mouse mode
Support parsing shift key press
Support additional mouse buttons
Report which button was released
Report button motion
  • Loading branch information
aymanbagabas committed Oct 4, 2023
1 parent 99d7d4b commit 6e316e7
Show file tree
Hide file tree
Showing 11 changed files with 1,033 additions and 220 deletions.
14 changes: 3 additions & 11 deletions examples/mouse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ package main
// coordinates and events.

import (
"fmt"
"log"

tea "github.com/charmbracelet/bubbletea"
)

func main() {
p := tea.NewProgram(model{}, tea.WithAltScreen(), tea.WithMouseAllMotion())
p := tea.NewProgram(model{}, tea.WithMouseAllMotion())
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}

type model struct {
init bool
mouseEvent tea.MouseEvent
}

Expand All @@ -34,20 +32,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

case tea.MouseMsg:
m.init = true
m.mouseEvent = tea.MouseEvent(msg)
return m, tea.Printf("(X: %d, Y: %d) %s", msg.X, msg.Y, tea.MouseEvent(msg))
}

return m, nil
}

func (m model) View() string {
s := "Do mouse stuff. When you're done press q to quit.\n\n"

if m.init {
e := m.mouseEvent
s += fmt.Sprintf("(X: %d, Y: %d) %s", e.X, e.Y, e)
}
s := "Do mouse stuff. When you're done press q to quit.\n"

return s
}
16 changes: 13 additions & 3 deletions key.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
b := buf[:numBytes]

var i, w int
for i, w = 0, 0; i < len(b); i += w {
for i, w = 0, 07; i < len(b); i += w {
var msg Msg
w, msg = detectOneMsg(b[i:])
select {
Expand All @@ -570,11 +570,21 @@ func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {

var unknownCSIRe = regexp.MustCompile(`^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]`)

var mouseSGRRegex = regexp.MustCompile(`(\d+);(\d+);(\d+)([Mm])`)

func detectOneMsg(b []byte) (w int, msg Msg) {
// Detect mouse events.
// X10 mouse events have a length of 6 bytes
const mouseEventLen = 6
if len(b) >= mouseEventLen && b[0] == '\x1b' && b[1] == '[' && b[2] == 'M' {
return mouseEventLen, MouseMsg(parseX10MouseEvent(b))
if len(b) >= mouseEventLen && b[0] == '\x1b' && b[1] == '[' {
switch b[2] {
case 'M':
return mouseEventLen, MouseMsg(parseX10MouseEvent(b))
case '<':
if mouseSGRRegex.Match(b[3:]) {
return mouseEventLen, MouseMsg(parseSGRMouseEvent(b))
}
}
}

// Detect escape sequence and control characters other than NUL,
Expand Down
28 changes: 17 additions & 11 deletions key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func TestDetectOneMsg(t *testing.T) {
// Mouse event.
seqTest{
[]byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
MouseMsg{X: 32, Y: 16, Type: MouseWheelUp},
MouseMsg{X: 32, Y: 16, Type: MouseWheelUp, Button: MouseButtonWheelUp, Action: MouseActionPress},
},
// Runes.
seqTest{
Expand Down Expand Up @@ -297,27 +297,33 @@ func TestReadInput(t *testing.T) {
[]byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
[]Msg{
MouseMsg{
X: 32,
Y: 16,
Type: MouseWheelUp,
X: 32,
Y: 16,
Type: MouseWheelUp,
Button: MouseButtonWheelUp,
Action: MouseActionPress,
},
},
},
{"left release",
{"left motion release",
[]byte{
'\x1b', '[', 'M', byte(32) + 0b0010_0000, byte(32 + 33), byte(16 + 33),
'\x1b', '[', 'M', byte(32) + 0b0000_0011, byte(64 + 33), byte(32 + 33),
},
[]Msg{
MouseMsg(MouseEvent{
X: 32,
Y: 16,
Type: MouseLeft,
X: 32,
Y: 16,
Type: MouseLeft,
Button: MouseButtonLeft,
Action: MouseActionMotion,
}),
MouseMsg(MouseEvent{
X: 64,
Y: 32,
Type: MouseRelease,
X: 64,
Y: 32,
Type: MouseRelease,
Button: MouseButtonNone,
Action: MouseActionRelease,
}),
},
},
Expand Down

0 comments on commit 6e316e7

Please sign in to comment.