mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-26 02:02:14 +02:00
Synthesize click events in the loop
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"kitty/tools/tty"
|
"kitty/tools/tty"
|
||||||
|
"kitty/tools/utils"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ type Loop struct {
|
|||||||
timer_id_counter, write_msg_id_counter IdType
|
timer_id_counter, write_msg_id_counter IdType
|
||||||
wakeup_channel chan byte
|
wakeup_channel chan byte
|
||||||
pending_writes []*write_msg
|
pending_writes []*write_msg
|
||||||
|
pending_mouse_events *utils.RingBuffer[MouseEvent]
|
||||||
on_SIGTSTP func() error
|
on_SIGTSTP func() error
|
||||||
|
|
||||||
// Suspend the loop restoring terminal state. Call the return resume function to restore the loop
|
// Suspend the loop restoring terminal state. Call the return resume function to restore the loop
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const (
|
|||||||
MOUSE_PRESS MouseEventType = iota
|
MOUSE_PRESS MouseEventType = iota
|
||||||
MOUSE_RELEASE
|
MOUSE_RELEASE
|
||||||
MOUSE_MOVE
|
MOUSE_MOVE
|
||||||
|
MOUSE_CLICK
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
SHIFT_INDICATOR int = 1 << 2
|
SHIFT_INDICATOR int = 1 << 2
|
||||||
@@ -38,7 +39,7 @@ type MouseEvent struct {
|
|||||||
Event_type MouseEventType
|
Event_type MouseEventType
|
||||||
Buttons MouseButtonFlag
|
Buttons MouseButtonFlag
|
||||||
Mods KeyModifiers
|
Mods KeyModifiers
|
||||||
Cell, Pixel struct{ x, y int }
|
Cell, Pixel struct{ X, Y int }
|
||||||
}
|
}
|
||||||
|
|
||||||
func pixel_to_cell(px, length, cell_length int) int {
|
func pixel_to_cell(px, length, cell_length int) int {
|
||||||
@@ -56,14 +57,14 @@ func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ans := MouseEvent{}
|
ans := MouseEvent{}
|
||||||
ans.Pixel.x, err = strconv.Atoi(parts[1])
|
ans.Pixel.X, err = strconv.Atoi(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(parts[2]) < 1 {
|
if len(parts[2]) < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ans.Pixel.y, err = strconv.Atoi(parts[2][:len(parts[2])-1])
|
ans.Pixel.Y, err = strconv.Atoi(parts[2][:len(parts[2])-1])
|
||||||
m := parts[2][len(parts[2])-1]
|
m := parts[2][len(parts[2])-1]
|
||||||
if m == 'm' {
|
if m == 'm' {
|
||||||
ans.Event_type = MOUSE_RELEASE
|
ans.Event_type = MOUSE_RELEASE
|
||||||
@@ -87,8 +88,8 @@ func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent {
|
|||||||
if cb&CTRL_INDICATOR != 0 {
|
if cb&CTRL_INDICATOR != 0 {
|
||||||
ans.Mods |= CTRL
|
ans.Mods |= CTRL
|
||||||
}
|
}
|
||||||
ans.Cell.x = pixel_to_cell(ans.Pixel.x, int(screen_size.WidthPx), int(screen_size.CellWidth))
|
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))
|
ans.Cell.Y = pixel_to_cell(ans.Pixel.Y, int(screen_size.HeightPx), int(screen_size.CellHeight))
|
||||||
|
|
||||||
return &ans
|
return &ans
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,9 +86,39 @@ func (self *Loop) handle_csi(raw []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func is_click(a, b *MouseEvent) bool {
|
||||||
|
if a.Event_type != MOUSE_PRESS || b.Event_type != MOUSE_RELEASE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
x := a.Cell.X - b.Cell.X
|
||||||
|
y := a.Cell.Y - b.Cell.Y
|
||||||
|
return x*x+y*y <= 4
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Loop) handle_mouse_event(ev *MouseEvent) error {
|
func (self *Loop) handle_mouse_event(ev *MouseEvent) error {
|
||||||
if self.OnMouseEvent != nil {
|
if self.OnMouseEvent != nil {
|
||||||
return self.OnMouseEvent(ev)
|
err := self.OnMouseEvent(ev)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch ev.Event_type {
|
||||||
|
case MOUSE_PRESS:
|
||||||
|
self.pending_mouse_events.WriteAllAndDiscardOld(*ev)
|
||||||
|
case MOUSE_RELEASE:
|
||||||
|
self.pending_mouse_events.WriteAllAndDiscardOld(*ev)
|
||||||
|
if self.pending_mouse_events.Len() > 1 {
|
||||||
|
events := self.pending_mouse_events.ReadAll()
|
||||||
|
if is_click(&events[len(events)-2], &events[len(events)-1]) {
|
||||||
|
e := events[len(events)-1]
|
||||||
|
e.Event_type = MOUSE_CLICK
|
||||||
|
err = self.OnMouseEvent(&e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -243,6 +273,7 @@ func (self *Loop) run() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.keep_going = true
|
self.keep_going = true
|
||||||
|
self.pending_mouse_events = utils.NewRingBuffer[MouseEvent](4)
|
||||||
tty_read_channel := make(chan []byte)
|
tty_read_channel := make(chan []byte)
|
||||||
tty_write_channel := make(chan *write_msg, 1) // buffered so there is no race between initial queueing and startup of writer thread
|
tty_write_channel := make(chan *write_msg, 1) // buffered so there is no race between initial queueing and startup of writer thread
|
||||||
write_done_channel := make(chan IdType)
|
write_done_channel := make(chan IdType)
|
||||||
|
|||||||
Reference in New Issue
Block a user