Implement decoding of mouse events in Go
This commit is contained in:
parent
16c7681c7c
commit
37cebbc817
@ -74,6 +74,9 @@ type Loop struct {
|
|||||||
// Called when a key event happens
|
// Called when a key event happens
|
||||||
OnKeyEvent func(event *KeyEvent) error
|
OnKeyEvent func(event *KeyEvent) error
|
||||||
|
|
||||||
|
// Called when a mouse event happens
|
||||||
|
OnMouseEvent func(event *MouseEvent) error
|
||||||
|
|
||||||
// Called when text is received either from a key event or directly from the terminal
|
// Called when text is received either from a key event or directly from the terminal
|
||||||
// Called with an empty string when bracketed paste ends
|
// Called with an empty string when bracketed paste ends
|
||||||
OnText func(text string, from_key_event bool, in_bracketed_paste bool) error
|
OnText func(text string, from_key_event bool, in_bracketed_paste bool) error
|
||||||
|
|||||||
109
tools/tui/loop/mouse.go
Normal file
109
tools/tui/loop/mouse.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package loop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type MouseEventType int
|
||||||
|
type MouseButtonFlag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MOUSE_PRESS MouseEventType = iota
|
||||||
|
MOUSE_RELEASE
|
||||||
|
MOUSE_MOVE
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
SHIFT_INDICATOR int = 1 << 2
|
||||||
|
ALT_INDICATOR = 1 << 3
|
||||||
|
CTRL_INDICATOR = 1 << 4
|
||||||
|
MOTION_INDICATOR = 1 << 5
|
||||||
|
)
|
||||||
|
|
||||||
|
const NONE, LEFT, MIDDLE, RIGHT, FOURTH, FIFTH, SIXTH, SEVENTH MouseButtonFlag = 0, 1, 2, 4, 8, 16, 32, 64
|
||||||
|
const WHEEL_UP, WHEEL_DOWN, WHEEL_LEFT, WHEEL_RIGHT MouseButtonFlag = -1, -2, -4, -8
|
||||||
|
|
||||||
|
var bmap = [...]MouseButtonFlag{LEFT, MIDDLE, RIGHT}
|
||||||
|
var ebmap = [...]MouseButtonFlag{FOURTH, FIFTH, SIXTH, SEVENTH}
|
||||||
|
var wbmap = [...]MouseButtonFlag{WHEEL_UP, WHEEL_DOWN, WHEEL_LEFT, WHEEL_RIGHT}
|
||||||
|
|
||||||
|
type MouseEvent struct {
|
||||||
|
Event_type MouseEventType
|
||||||
|
Buttons MouseButtonFlag
|
||||||
|
Mods KeyModifiers
|
||||||
|
Cell, Pixel struct{ x, y int }
|
||||||
|
}
|
||||||
|
|
||||||
|
func pixel_to_cell(px, length, cell_length int) int {
|
||||||
|
px = utils.Max(0, utils.Min(px, length-1))
|
||||||
|
return px / cell_length
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent {
|
||||||
|
parts := strings.Split(text, ";")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cb, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ans := MouseEvent{}
|
||||||
|
ans.Pixel.x, err = strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(parts[2]) < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ans.Pixel.y, err = strconv.Atoi(parts[2][:len(parts[2])-1])
|
||||||
|
m := parts[2][len(parts[2])-1]
|
||||||
|
if m == 'm' {
|
||||||
|
ans.Event_type = MOUSE_RELEASE
|
||||||
|
} else if cb&MOTION_INDICATOR != 0 {
|
||||||
|
ans.Event_type = MOUSE_MOVE
|
||||||
|
}
|
||||||
|
cb3 := cb & 3
|
||||||
|
if cb >= 128 {
|
||||||
|
ans.Buttons |= ebmap[cb3]
|
||||||
|
} else if cb >= 64 {
|
||||||
|
ans.Buttons |= wbmap[cb3]
|
||||||
|
} else if cb3 < 3 {
|
||||||
|
ans.Buttons |= bmap[cb3]
|
||||||
|
}
|
||||||
|
if cb&SHIFT_INDICATOR != 0 {
|
||||||
|
ans.Mods |= SHIFT
|
||||||
|
}
|
||||||
|
if cb&ALT_INDICATOR != 0 {
|
||||||
|
ans.Mods |= ALT
|
||||||
|
}
|
||||||
|
if cb&CTRL_INDICATOR != 0 {
|
||||||
|
ans.Mods |= CTRL
|
||||||
|
}
|
||||||
|
ans.Cell.x = pixel_to_cell(ans.Pixel.x, int(screen_size.WidthPx), int(screen_size.CellWidth))
|
||||||
|
ans.Cell.y = pixel_to_cell(ans.Pixel.y, int(screen_size.HeightPx), int(screen_size.CellHeight))
|
||||||
|
|
||||||
|
return &ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func MouseEventFromCSI(csi string, screen_size ScreenSize) *MouseEvent {
|
||||||
|
if len(csi) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
last_char := csi[len(csi)-1]
|
||||||
|
if last_char != 'm' && last_char != 'M' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
csi = csi[:len(csi)-1]
|
||||||
|
if !strings.HasPrefix(csi, "<") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return decode_sgr_mouse(csi[1:], screen_size)
|
||||||
|
}
|
||||||
@ -73,12 +73,26 @@ func (self *Loop) handle_csi(raw []byte) error {
|
|||||||
if ke != nil {
|
if ke != nil {
|
||||||
return self.handle_key_event(ke)
|
return self.handle_key_event(ke)
|
||||||
}
|
}
|
||||||
|
sz, err := self.ScreenSize()
|
||||||
|
if err == nil {
|
||||||
|
me := MouseEventFromCSI(csi, sz)
|
||||||
|
if me != nil {
|
||||||
|
return self.handle_mouse_event(me)
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.OnEscapeCode != nil {
|
if self.OnEscapeCode != nil {
|
||||||
return self.OnEscapeCode(CSI, raw)
|
return self.OnEscapeCode(CSI, raw)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Loop) handle_mouse_event(ev *MouseEvent) error {
|
||||||
|
if self.OnMouseEvent != nil {
|
||||||
|
return self.OnMouseEvent(ev)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Loop) handle_key_event(ev *KeyEvent) error {
|
func (self *Loop) handle_key_event(ev *KeyEvent) error {
|
||||||
if self.OnKeyEvent != nil {
|
if self.OnKeyEvent != nil {
|
||||||
err := self.OnKeyEvent(ev)
|
err := self.OnKeyEvent(ev)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user