From e29918ebb849f8502ac490b6f87490d7fce5050f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 26 Sep 2024 10:20:40 -0700 Subject: [PATCH] core: more actions --- src/Surface.zig | 51 +++++++++++++--------- src/apprt/action.zig | 35 +++++++++++++++ src/apprt/embedded.zig | 96 +++++++++++++++++++++++++++--------------- src/apprt/glfw.zig | 15 ++++--- src/input/Binding.zig | 2 +- 5 files changed, 140 insertions(+), 59 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 029a1b221..298bb6a0a 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3735,11 +3735,15 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool {}, ), - .toggle_fullscreen => { - if (@hasDecl(apprt.Surface, "toggleFullscreen")) { - self.rt_surface.toggleFullscreen(self.config.macos_non_native_fullscreen); - } else log.warn("runtime doesn't implement toggleFullscreen", .{}); - }, + .toggle_fullscreen => try self.rt_app.performAction( + .{ .surface = self }, + .toggle_fullscreen, + switch (self.config.macos_non_native_fullscreen) { + .false => .native, + .true => .macos_non_native, + .@"visible-menu" => .macos_non_native_visible_menu, + }, + ), .toggle_window_decorations => try self.rt_app.performAction( .{ .surface = self }, @@ -3761,11 +3765,16 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool } }, - .inspector => |mode| { - if (@hasDecl(apprt.Surface, "controlInspector")) { - self.rt_surface.controlInspector(mode); - } else log.warn("runtime doesn't implement controlInspector", .{}); - }, + .inspector => |mode| try self.rt_app.performAction( + .{ .surface = self }, + .inspector, + switch (mode) { + inline else => |tag| @field( + apprt.action.Inspector, + @tagName(tag), + ), + }, + ), .close_surface => self.close(), @@ -4152,11 +4161,6 @@ fn completeClipboardReadOSC52( } fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const u8) !void { - if (comptime !@hasDecl(apprt.Surface, "showDesktopNotification")) { - log.warn("runtime doesn't support desktop notifications", .{}); - return; - } - // Wyhash is used to hash the contents of the desktop notification to limit // how fast identical notifications can be sent sequentially. const hash_algorithm = std.hash.Wyhash; @@ -4192,7 +4196,14 @@ fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const self.app.last_notification_time = now; self.app.last_notification_digest = new_digest; - try self.rt_surface.showDesktopNotification(title, body); + try self.rt_app.performAction( + .{ .surface = self }, + .desktop_notification, + .{ + .title = title, + .body = body, + }, + ); } fn crashThreadState(self: *Surface) crash.sentry.ThreadState { @@ -4205,9 +4216,11 @@ fn crashThreadState(self: *Surface) crash.sentry.ThreadState { /// Tell the surface to present itself to the user. This may involve raising the /// window and switching tabs. fn presentSurface(self: *Surface) !void { - if (@hasDecl(apprt.Surface, "presentSurface")) { - self.rt_surface.presentSurface(); - } else log.warn("runtime doesn't support presentSurface", .{}); + try self.rt_app.performAction( + .{ .surface = self }, + .present_terminal, + {}, + ); } pub const face_ttf = @embedFile("font/res/JetBrainsMono-Regular.ttf"); diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 72f0aadd5..35bdcb3c2 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -28,6 +28,9 @@ pub const Action = union(enum) { /// Close all open windows. close_all_windows, + /// Toggle fullscreen mode. + toggle_fullscreen: Fullscreen, + /// Toggle whether window directions are shown. toggle_window_decorations, @@ -48,6 +51,15 @@ pub const Action = union(enum) { /// to take up the entire window. toggle_split_zoom, + /// Present the target terminal whether its a tab, split, or window. + present_terminal, + + /// Control whether the inspector is shown or hidden. + inspector: Inspector, + + /// Show a desktop notification. + desktop_notification: DesktopNotification, + /// Open the Ghostty configuration. This is platform-specific about /// what it means; it can mean opening a dedicated UI or just opening /// a file in a text editor. @@ -130,8 +142,31 @@ pub const GotoTab = enum(c_int) { _, }; +/// The fullscreen mode to toggle to if we're moving to fullscreen. +pub const Fullscreen = enum(c_int) { + native, + + /// macOS has a non-native fullscreen mode that is more like a maximized + /// window. This is much faster to enter and exit than the native mode. + macos_non_native, + macos_non_native_visible_menu, +}; + pub const SecureInput = enum(c_int) { on, off, toggle, }; + +/// The inspector mode to toggle to if we're toggling the inspector. +pub const Inspector = enum(c_int) { + toggle, + show, + hide, +}; + +/// The desktop notification to show. +pub const DesktopNotification = struct { + title: [:0]const u8, + body: [:0]const u8, +}; diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index a0e4230be..ca53f137e 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -92,7 +92,7 @@ pub const App = struct { new_window: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null, /// Control the inspector visibility - control_inspector: ?*const fn (SurfaceUD, input.InspectorMode) callconv(.C) void = null, + control_inspector: ?*const fn (SurfaceUD, apprt.action.Inspector) callconv(.C) void = null, /// Close the current surface given by this function. close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, @@ -125,7 +125,8 @@ pub const App = struct { /// Called when the cell size changes. set_cell_size: ?*const fn (SurfaceUD, u32, u32) callconv(.C) void = null, - /// Show a desktop notification to the user. + /// Show a desktop notification to the user. The surface may be null + /// if the notification is global. show_desktop_notification: ?*const fn (SurfaceUD, [*:0]const u8, [*:0]const u8) void = null, /// Called when the health of the renderer changes. @@ -507,6 +508,29 @@ pub const App = struct { func(null, .{}); } + fn toggleFullscreen( + self: *App, + target: apprt.Target, + fullscreen: apprt.action.Fullscreen, + ) void { + const func = self.opts.toggle_fullscreen orelse { + log.info("runtime embedder does not toggle_fullscreen", .{}); + return; + }; + + switch (target) { + .app => {}, + .surface => |v| func( + v.rt_surface.userdata, + switch (fullscreen) { + .native => .false, + .macos_non_native => .true, + .macos_non_native_visible_menu => .@"visible-menu", + }, + ), + } + } + fn newTab(self: *const App, target: apprt.Target) void { const func = self.opts.new_tab orelse { log.info("runtime embedder does not support new_tab", .{}); @@ -614,6 +638,38 @@ pub const App = struct { } } + fn controlInspector( + self: *const App, + target: apprt.Target, + value: apprt.action.Inspector, + ) void { + const func = self.opts.control_inspector orelse { + log.info("runtime embedder does not support the terminal inspector", .{}); + return; + }; + + switch (target) { + .app => {}, + .surface => |v| func(v.rt_surface.userdata, value), + } + } + + fn showDesktopNotification( + self: *const App, + target: apprt.Target, + notification: apprt.action.DesktopNotification, + ) void { + const func = self.opts.show_desktop_notification orelse { + log.info("runtime embedder does not support show_desktop_notification", .{}); + return; + }; + + func(switch (target) { + .app => null, + .surface => |v| v.rt_surface.userdata, + }, notification.title, notification.body); + } + fn setPasswordInput(self: *App, target: apprt.Target, v: apprt.action.SecureInput) void { switch (v) { inline .on, .off => |tag| { @@ -655,6 +711,7 @@ pub const App = struct { .app => null, .surface => |v| v, }), + .toggle_fullscreen => self.toggleFullscreen(target, value), .new_tab => self.newTab(target), .goto_tab => self.gotoTab(target, value), @@ -664,9 +721,12 @@ pub const App = struct { .toggle_split_zoom => self.toggleSplitZoom(target), .goto_split => self.gotoSplit(target, value), .open_config => try configpkg.edit.open(self.core_app.alloc), + .inspector => self.controlInspector(target, value), + .desktop_notification => self.showDesktopNotification(target, value), .secure_input => self.setPasswordInput(target, value), // Unimplemented + .present_terminal, .close_all_windows, .toggle_window_decorations, .quit_timer, @@ -889,15 +949,6 @@ pub const Surface = struct { } } - pub fn controlInspector(self: *const Surface, mode: input.InspectorMode) void { - const func = self.app.opts.control_inspector orelse { - log.info("runtime embedder does not support the terminal inspector", .{}); - return; - }; - - func(self.userdata, mode); - } - pub fn close(self: *const Surface, process_alive: bool) void { const func = self.app.opts.close_surface orelse { log.info("runtime embedder does not support closing a surface", .{}); @@ -1185,15 +1236,6 @@ pub const Surface = struct { }; } - pub fn toggleFullscreen(self: *Surface, nonNativeFullscreen: configpkg.NonNativeFullscreen) void { - const func = self.app.opts.toggle_fullscreen orelse { - log.info("runtime embedder does not toggle_fullscreen", .{}); - return; - }; - - func(self.userdata, nonNativeFullscreen); - } - fn newWindow(self: *const Surface) !void { const func = self.app.opts.new_window orelse { log.info("runtime embedder does not support new_window", .{}); @@ -1249,20 +1291,6 @@ pub const Surface = struct { return .{ .x = pos.x * scale.x, .y = pos.y * scale.y }; } - /// Show a desktop notification. - pub fn showDesktopNotification( - self: *const Surface, - title: [:0]const u8, - body: [:0]const u8, - ) !void { - const func = self.app.opts.show_desktop_notification orelse { - log.info("runtime embedder does not support show_desktop_notification", .{}); - return; - }; - - func(self.userdata, title, body); - } - /// Update the health of the renderer. pub fn updateRendererHealth(self: *const Surface, health: renderer.Health) void { const func = self.app.opts.update_renderer_health orelse { diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index dabf63c2b..4be6aaf89 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -147,6 +147,8 @@ pub const App = struct { .surface => |v| v, }), + .toggle_fullscreen => self.toggleFullscreen(target), + .open_config => try configpkg.edit.open(self.app.alloc), // Unimplemented @@ -155,11 +157,14 @@ pub const App = struct { .resize_split, .equalize_splits, .toggle_split_zoom, + .present_terminal, .close_all_windows, .toggle_window_decorations, .goto_tab, + .inspector, .quit_timer, .secure_input, + .desktop_notification, => log.info("unimplemented action={}", .{action}), } } @@ -182,8 +187,12 @@ pub const App = struct { } /// Toggle the window to fullscreen mode. - pub fn toggleFullscreen(self: *App, surface: *Surface) void { + fn toggleFullscreen(self: *App, target: apprt.Target) void { _ = self; + const surface: *Surface = switch (target) { + .app => return, + .surface => |v| v.rt_surface, + }; const win = surface.window; if (surface.isFullscreen()) { @@ -562,10 +571,6 @@ pub const Surface = struct { return self.window.getMonitor() != null; } - pub fn toggleFullscreen(self: *Surface, _: Config.NonNativeFullscreen) void { - self.app.toggleFullscreen(self); - } - /// Close this surface. pub fn close(self: *Surface, processActive: bool) void { _ = processActive; diff --git a/src/input/Binding.zig b/src/input/Binding.zig index f39510554..45ec24126 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -438,7 +438,7 @@ pub const Action = union(enum) { }; // Extern because it is used in the embedded runtime ABI. - pub const InspectorMode = enum(c_int) { + pub const InspectorMode = enum { toggle, show, hide,