From ab4acf0d008ff3f57e176502dc74d563f8fd96a5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 17 Sep 2023 11:45:53 -0700 Subject: [PATCH 1/2] core: send mouse motion events if motion tracking and button pressed --- src/Surface.zig | 34 +++++++++++++++++++++++----------- src/terminal/Terminal.zig | 5 +++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index b0c2d3c51..97890b828 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1283,12 +1283,6 @@ fn mouseReport( mods: input.Mods, pos: apprt.CursorPos, ) !void { - // The maximum pos values so we can determine if we're outside the window. - // If we're outside the window, we do not report mouse events. - const max_x: f32 = @floatFromInt(self.screen_size.width); - const max_y: f32 = @floatFromInt(self.screen_size.height); - if (pos.x < 0 or pos.y < 0 or pos.x > max_x or pos.y > max_y) return; - // Depending on the event, we may do nothing at all. switch (self.io.terminal.flags.mouse_event) { .none => return, @@ -1311,6 +1305,27 @@ fn mouseReport( .any => {}, } + // The maximum pos values so we can determine if we're outside the window. + // If we're outside the window, we do not report mouse events. + const pos_out_viewport = pos_out_viewport: { + const max_x: f32 = @floatFromInt(self.screen_size.width); + const max_y: f32 = @floatFromInt(self.screen_size.height); + break :pos_out_viewport pos.x < 0 or pos.y < 0 or + pos.x > max_x or pos.y > max_y; + }; + if (pos_out_viewport) outside_viewport: { + // If we don't have a motion-tracking event mode, do nothing. + if (!self.io.terminal.flags.mouse_event.motion()) return; + + // If any button is pressed, we still do the report. Otherwise, + // we do not do the report. + for (self.mouse.click_state) |state| { + if (state != .release) break :outside_viewport; + } + + return; + } + // This format reports X/Y const viewport_point = self.posToViewport(pos.x, pos.y); @@ -1450,9 +1465,6 @@ fn mouseReport( }, .sgr_pixels => { - assert(pos.x >= 0); - assert(pos.y >= 0); - // Final character to send in the CSI const final: u8 = if (action == .release) 'm' else 'M'; @@ -1461,8 +1473,8 @@ fn mouseReport( var data: termio.Message.WriteReq.Small.Array = undefined; const resp = try std.fmt.bufPrint(&data, "\x1B[<{d};{d};{d}{c}", .{ button_code, - @as(u32, @intFromFloat(@round(pos.x))), - @as(u32, @intFromFloat(@round(pos.y))), + @as(i32, @intFromFloat(@round(pos.x))), + @as(i32, @intFromFloat(@round(pos.y))), final, }); diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index bd32dbb83..ba53ffbfe 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -132,6 +132,11 @@ pub const MouseEvents = enum(u3) { normal = 2, // 1000 button = 3, // 1002 any = 4, // 1003 + + /// Returns true if this event sends motion events. + pub fn motion(self: MouseEvents) bool { + return self == .button or self == .any; + } }; /// The format of mouse events when enabled. From 36c9d607b226a4aa5c1fb9ee2536522a4daca96e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 17 Sep 2023 11:49:25 -0700 Subject: [PATCH 2/2] core: do not send repeat mouse events if cell didn't change --- src/Surface.zig | 10 ++++++++-- src/terminal/point.zig | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 97890b828..e3d84acd1 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -117,7 +117,7 @@ const Mouse = struct { left_click_time: std.time.Instant = undefined, /// The last x/y sent for mouse reports. - event_point: terminal.point.Viewport = .{}, + event_point: ?terminal.point.Viewport = null, /// Pending scroll amounts for high-precision scrolls pending_scroll_x: f64 = 0, @@ -1329,7 +1329,13 @@ fn mouseReport( // This format reports X/Y const viewport_point = self.posToViewport(pos.x, pos.y); - // Record our new point + // Record our new point. We only want to send a mouse event if the + // cell changed, unless we're tracking raw pixels. + if (self.io.terminal.flags.mouse_format != .sgr_pixels) { + if (self.mouse.event_point) |last_point| { + if (last_point.eql(viewport_point)) return; + } + } self.mouse.event_point = viewport_point; // Get the code we'll actually write diff --git a/src/terminal/point.zig b/src/terminal/point.zig index df35f2cd0..b0f851acd 100644 --- a/src/terminal/point.zig +++ b/src/terminal/point.zig @@ -20,6 +20,10 @@ pub const Viewport = struct { }; } + pub fn eql(self: Viewport, other: Viewport) bool { + return self.x == other.x and self.y == other.y; + } + test "toScreen with no scrollback" { const testing = std.testing; const alloc = testing.allocator;