From 5c1ffbb64209c9cc75f9a32256b7addf45975ee8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 8 Oct 2024 06:51:25 -1000 Subject: [PATCH] apprt: implement key_sequence action --- include/ghostty.h | 8 ++++++++ macos/Sources/Ghostty/Ghostty.App.swift | 2 ++ src/Surface.zig | 24 ++++++++++++++++++++++++ src/apprt/action.zig | 25 +++++++++++++++++++++++++ src/apprt/glfw.zig | 1 + src/apprt/gtk/App.zig | 1 + src/input.zig | 1 + 7 files changed, 62 insertions(+) diff --git a/include/ghostty.h b/include/ghostty.h index bf13500c8..6cc288b8f 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -500,6 +500,12 @@ typedef enum { GHOSTTY_RENDERER_HEALTH_UNHEALTHY, } ghostty_action_renderer_health_e; +// apprt.action.KeySequence +typedef struct { + bool active; + ghostty_input_trigger_s trigger; +} ghostty_action_key_sequence_s; + // apprt.Action.Key typedef enum { GHOSTTY_ACTION_NEW_WINDOW, @@ -531,6 +537,7 @@ typedef enum { GHOSTTY_ACTION_OPEN_CONFIG, GHOSTTY_ACTION_QUIT_TIMER, GHOSTTY_ACTION_SECURE_INPUT, + GHOSTTY_ACTION_KEY_SEQUENCE, } ghostty_action_tag_e; typedef union { @@ -551,6 +558,7 @@ typedef union { ghostty_action_renderer_health_e renderer_health; ghostty_action_quit_timer_e quit_timer; ghostty_action_secure_input_e secure_input; + ghostty_action_key_sequence_s key_sequence; } ghostty_action_u; typedef struct { diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 5716e9801..82a08f666 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -515,6 +515,8 @@ extension Ghostty { case GHOSTTY_ACTION_TOGGLE_VISIBILITY: toggleVisibility(app, target: target) + case GHOSTTY_ACTION_KEY_SEQUENCE: + fallthrough case GHOSTTY_ACTION_CLOSE_ALL_WINDOWS: fallthrough case GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW: diff --git a/src/Surface.zig b/src/Surface.zig index ac941ef89..aa752419c 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1709,6 +1709,18 @@ fn maybeHandleBinding( try self.keyboard.queued.append(self.alloc, req); } + // Start or continue our key sequence + self.rt_app.performAction( + .{ .surface = self }, + .key_sequence, + .{ .trigger = entry.key_ptr.* }, + ) catch |err| { + log.warn( + "failed to notify app of key sequence err={}", + .{err}, + ); + }; + return .consumed; }, @@ -1795,6 +1807,18 @@ fn endKeySequence( action: KeySequenceQueued, mem: KeySequenceMemory, ) void { + // Notify apprt key sequence ended + self.rt_app.performAction( + .{ .surface = self }, + .key_sequence, + .end, + ) catch |err| { + log.warn( + "failed to notify app of key sequence end err={}", + .{err}, + ); + }; + if (self.keyboard.queued.items.len > 0) { switch (action) { .flush => for (self.keyboard.queued.items) |write_req| { diff --git a/src/apprt/action.zig b/src/apprt/action.zig index a5da51d1d..9fce8502f 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const apprt = @import("../apprt.zig"); +const input = @import("../input.zig"); const renderer = @import("../renderer.zig"); const terminal = @import("../terminal/main.zig"); const CoreSurface = @import("../Surface.zig"); @@ -173,6 +174,11 @@ pub const Action = union(Key) { /// system APIs to not log the input, etc. secure_input: SecureInput, + /// A sequenced key binding has started, continued, or stopped. + /// The UI should show some indication that the user is in a sequenced + /// key mode because other input may be ignored. + key_sequence: KeySequence, + /// Sync with: ghostty_action_tag_e pub const Key = enum(c_int) { new_window, @@ -204,6 +210,7 @@ pub const Action = union(Key) { open_config, quit_timer, secure_input, + key_sequence, }; /// Sync with: ghostty_action_u @@ -411,3 +418,21 @@ pub const DesktopNotification = struct { }; } }; + +pub const KeySequence = union(enum) { + trigger: input.Trigger, + end, + + // Sync with: ghostty_action_key_sequence_s + pub const C = extern struct { + active: bool, + trigger: input.Trigger.C, + }; + + pub fn cval(self: KeySequence) C { + return switch (self) { + .trigger => |t| .{ .active = true, .trigger = t.cval() }, + .end => .{ .active = false, .trigger = .{} }, + }; + } +}; diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 948b38a29..980c2dba3 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -203,6 +203,7 @@ pub const App = struct { .render_inspector, .quit_timer, .secure_input, + .key_sequence, .desktop_notification, .mouse_over_link, .cell_size, diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 3855d27c3..b18753344 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -411,6 +411,7 @@ pub fn performAction( .size_limit, .cell_size, .secure_input, + .key_sequence, .render_inspector, .renderer_health, => log.warn("unimplemented action={}", .{action}), diff --git a/src/input.zig b/src/input.zig index 204adcfbe..9e3997d97 100644 --- a/src/input.zig +++ b/src/input.zig @@ -23,6 +23,7 @@ pub const MousePressureStage = mouse.PressureStage; pub const ScrollMods = mouse.ScrollMods; pub const SplitFocusDirection = Binding.Action.SplitFocusDirection; pub const SplitResizeDirection = Binding.Action.SplitResizeDirection; +pub const Trigger = Binding.Trigger; // Keymap is only available on macOS right now. We could implement it // in theory for XKB too on Linux but we don't need it right now.