From e043fef25744605d3987c8f6d8ece6e6d68ae565 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Mar 2023 07:57:14 +0530 Subject: [PATCH] Synthesize click events in the loop --- tools/tui/loop/api.go | 2 ++ tools/tui/loop/mouse.go | 11 ++++++----- tools/tui/loop/run.go | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/tools/tui/loop/api.go b/tools/tui/loop/api.go index f80a53d7e..7f66cb7f7 100644 --- a/tools/tui/loop/api.go +++ b/tools/tui/loop/api.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "fmt" "kitty/tools/tty" + "kitty/tools/utils" "strings" "time" @@ -56,6 +57,7 @@ type Loop struct { timer_id_counter, write_msg_id_counter IdType wakeup_channel chan byte pending_writes []*write_msg + pending_mouse_events *utils.RingBuffer[MouseEvent] on_SIGTSTP func() error // Suspend the loop restoring terminal state. Call the return resume function to restore the loop diff --git a/tools/tui/loop/mouse.go b/tools/tui/loop/mouse.go index b927cb3d3..1a1830ed9 100644 --- a/tools/tui/loop/mouse.go +++ b/tools/tui/loop/mouse.go @@ -19,6 +19,7 @@ const ( MOUSE_PRESS MouseEventType = iota MOUSE_RELEASE MOUSE_MOVE + MOUSE_CLICK ) const ( SHIFT_INDICATOR int = 1 << 2 @@ -38,7 +39,7 @@ type MouseEvent struct { Event_type MouseEventType Buttons MouseButtonFlag Mods KeyModifiers - Cell, Pixel struct{ x, y int } + Cell, Pixel struct{ X, Y 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 } ans := MouseEvent{} - ans.Pixel.x, err = strconv.Atoi(parts[1]) + 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]) + 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 @@ -87,8 +88,8 @@ func decode_sgr_mouse(text string, screen_size ScreenSize) *MouseEvent { 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)) + 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 } diff --git a/tools/tui/loop/run.go b/tools/tui/loop/run.go index 87624d353..a0f2e535b 100644 --- a/tools/tui/loop/run.go +++ b/tools/tui/loop/run.go @@ -86,9 +86,39 @@ func (self *Loop) handle_csi(raw []byte) error { 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 { 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 } @@ -243,6 +273,7 @@ func (self *Loop) run() (err error) { } self.keep_going = true + self.pending_mouse_events = utils.NewRingBuffer[MouseEvent](4) 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 write_done_channel := make(chan IdType)