From 261ce00552d3275d9f2790883ecbe9cff0be6187 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Sep 2024 15:11:28 -0700 Subject: [PATCH] apprt/macos,gtk: unfocused splits now highlight hovered links Fixes #1547 The core change to make this work is to make the cursor position callback support taking updated modifiers. On both macOS and GTK, cursor position events also provide the pressed modifiers so we can pass those in. --- include/ghostty.h | 5 +++- .../Sources/Ghostty/SurfaceView_AppKit.swift | 3 ++- src/Surface.zig | 16 ++++++++++-- src/apprt/embedded.zig | 25 ++++++++++++++++--- src/apprt/glfw.zig | 2 +- src/apprt/gtk/Surface.zig | 9 +++++-- 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index c82411820..072a8536a 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -556,7 +556,10 @@ bool ghostty_surface_mouse_button(ghostty_surface_t, ghostty_input_mouse_state_e, ghostty_input_mouse_button_e, ghostty_input_mods_e); -void ghostty_surface_mouse_pos(ghostty_surface_t, double, double); +void ghostty_surface_mouse_pos(ghostty_surface_t, + double, + double, + ghostty_input_mods_e); void ghostty_surface_mouse_scroll(ghostty_surface_t, double, double, diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 90d259d22..82bc547de 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -505,7 +505,8 @@ extension Ghostty { // Convert window position to view position. Note (0, 0) is bottom left. let pos = self.convert(event.locationInWindow, from: nil) - ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y) + let mods = Ghostty.ghosttyMods(event.modifierFlags) + ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y, mods) // If focus follows mouse is enabled then move focus to this surface. if let window = self.window as? TerminalWindow, diff --git a/src/Surface.zig b/src/Surface.zig index 3700df7d9..a9b2c17d6 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1459,7 +1459,7 @@ pub fn keyCallback( // mod changes can affect link highlighting. self.mouse.link_point = null; const pos = self.rt_surface.getCursorPos() catch break :mouse_mods; - self.cursorPosCallback(pos) catch {}; + self.cursorPosCallback(pos, null) catch {}; if (rehide) self.mouse.hidden = true; } @@ -2421,7 +2421,7 @@ pub fn mouseButtonCallback( // expensive because it could block all our threads. if (self.hasSelection()) { const pos = try self.rt_surface.getCursorPos(); - try self.cursorPosCallback(pos); + try self.cursorPosCallback(pos, null); return true; } } @@ -2887,9 +2887,18 @@ pub fn mousePressureCallback( } } +/// Cursor position callback. +/// +/// The mods parameter is optional because some apprts do not provide +/// modifier information on cursor position events. If mods is null then +/// we'll use the last known mods. This is usually accurate since mod events +/// will trigger key press events but on some platforms we don't get them. +/// For example, on macOS, unfocused surfaces don't receive key events but +/// do receive mouse events so we have to rely on updated mods. pub fn cursorPosCallback( self: *Surface, pos: apprt.CursorPos, + mods: ?input.Mods, ) !void { // Crash metadata in case we crash in here crash.sentry.thread_state = self.crashThreadState(); @@ -2898,6 +2907,9 @@ pub fn cursorPosCallback( // Always show the mouse again if it is hidden if (self.mouse.hidden) self.showMouse(); + // Update our modifiers if they changed + if (mods) |v| self.modsChanged(v); + // The mouse position in the viewport const pos_vp = self.posToViewport(pos.x, pos.y); diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index f57b16272..b59ab1c9d 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -763,7 +763,12 @@ pub const Surface = struct { }; } - pub fn cursorPosCallback(self: *Surface, x: f64, y: f64) void { + pub fn cursorPosCallback( + self: *Surface, + x: f64, + y: f64, + mods: input.Mods, + ) void { // Convert our unscaled x/y to scaled. self.cursor_pos = self.cursorPosToPixels(.{ .x = @floatCast(x), @@ -776,7 +781,7 @@ pub const Surface = struct { return; }; - self.core_surface.cursorPosCallback(self.cursor_pos) catch |err| { + self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| { log.err("error in cursor pos callback err={}", .{err}); return; }; @@ -1716,8 +1721,20 @@ pub const CAPI = struct { } /// Update the mouse position within the view. - export fn ghostty_surface_mouse_pos(surface: *Surface, x: f64, y: f64) void { - surface.cursorPosCallback(x, y); + export fn ghostty_surface_mouse_pos( + surface: *Surface, + x: f64, + y: f64, + mods: c_int, + ) void { + surface.cursorPosCallback( + x, + y, + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods))), + )), + ); } export fn ghostty_surface_mouse_scroll( diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index f38214e32..57cb257b4 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -1040,7 +1040,7 @@ pub const Surface = struct { core_win.cursorPosCallback(.{ .x = @floatCast(pos.xpos), .y = @floatCast(pos.ypos), - }) catch |err| { + }, null) catch |err| { log.err("error in cursor pos callback err={}", .{err}); return; }; diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 7d337fbe0..c1146d348 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1386,7 +1386,7 @@ fn gtkMouseUp( } fn gtkMouseMotion( - _: *c.GtkEventControllerMotion, + ec: *c.GtkEventControllerMotion, x: c.gdouble, y: c.gdouble, ud: ?*anyopaque, @@ -1415,7 +1415,12 @@ fn gtkMouseMotion( self.grabFocus(); } - self.core_surface.cursorPosCallback(self.cursor_pos) catch |err| { + // Get our modifiers + const event = c.gtk_event_controller_get_current_event(@ptrCast(ec)); + const gtk_mods = c.gdk_event_get_modifier_state(event); + const mods = translateMods(gtk_mods); + + self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| { log.err("error in cursor pos callback err={}", .{err}); return; };