From 6fdfa9d4915573137561609f1f9718dfde5e1750 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 5 Oct 2024 06:46:34 -1000 Subject: [PATCH] Make the function/globe key available as a modifier on macOS --- include/ghostty.h | 13 +++++++------ macos/Sources/App/macOS/AppDelegate.swift | 5 +++++ macos/Sources/Ghostty/Ghostty.Input.swift | 2 ++ macos/Sources/Ghostty/SurfaceView_AppKit.swift | 1 + src/config/Config.zig | 16 +++++++++++++--- src/input/Binding.zig | 8 ++++++-- src/input/key.zig | 3 ++- 7 files changed, 36 insertions(+), 12 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index c64ce0160..b0e5c3fd6 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -88,12 +88,13 @@ typedef enum { GHOSTTY_MODS_CTRL = 1 << 1, GHOSTTY_MODS_ALT = 1 << 2, GHOSTTY_MODS_SUPER = 1 << 3, - GHOSTTY_MODS_CAPS = 1 << 4, - GHOSTTY_MODS_NUM = 1 << 5, - GHOSTTY_MODS_SHIFT_RIGHT = 1 << 6, - GHOSTTY_MODS_CTRL_RIGHT = 1 << 7, - GHOSTTY_MODS_ALT_RIGHT = 1 << 8, - GHOSTTY_MODS_SUPER_RIGHT = 1 << 9, + GHOSTTY_MODS_FN = 1 << 4, + GHOSTTY_MODS_CAPS = 1 << 5, + GHOSTTY_MODS_NUM = 1 << 6, + GHOSTTY_MODS_SHIFT_RIGHT = 1 << 7, + GHOSTTY_MODS_CTRL_RIGHT = 1 << 8, + GHOSTTY_MODS_ALT_RIGHT = 1 << 9, + GHOSTTY_MODS_SUPER_RIGHT = 1 << 10, } ghostty_input_mods_e; typedef enum { diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 9fdc8c7a2..a68e67a98 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -348,6 +348,11 @@ class AppDelegate: NSObject, return } + if (equiv.modifiers.contains(.function)) { + // NSMenuItem key equivalent cannot contain function modifiers. + return + } + menu.keyEquivalent = equiv.key menu.keyEquivalentModifierMask = equiv.modifiers } diff --git a/macos/Sources/Ghostty/Ghostty.Input.swift b/macos/Sources/Ghostty/Ghostty.Input.swift index d7fd96f12..89013cab8 100644 --- a/macos/Sources/Ghostty/Ghostty.Input.swift +++ b/macos/Sources/Ghostty/Ghostty.Input.swift @@ -14,6 +14,7 @@ extension Ghostty { if (mods.rawValue & GHOSTTY_MODS_CTRL.rawValue != 0) { flags.insert(.control) } if (mods.rawValue & GHOSTTY_MODS_ALT.rawValue != 0) { flags.insert(.option) } if (mods.rawValue & GHOSTTY_MODS_SUPER.rawValue != 0) { flags.insert(.command) } + if (mods.rawValue & GHOSTTY_MODS_FN.rawValue != 0) { flags.insert(.function) } return flags } @@ -25,6 +26,7 @@ extension Ghostty { if (flags.contains(.control)) { mods |= GHOSTTY_MODS_CTRL.rawValue } if (flags.contains(.option)) { mods |= GHOSTTY_MODS_ALT.rawValue } if (flags.contains(.command)) { mods |= GHOSTTY_MODS_SUPER.rawValue } + if (flags.contains(.function)) { mods |= GHOSTTY_MODS_FN.rawValue } if (flags.contains(.capsLock)) { mods |= GHOSTTY_MODS_CAPS.rawValue } // Handle sided input. We can't tell that both are pressed in the diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index ead898bdd..592611f32 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -726,6 +726,7 @@ extension Ghostty { case 0x3B, 0x3E: mod = GHOSTTY_MODS_CTRL.rawValue case 0x3A, 0x3D: mod = GHOSTTY_MODS_ALT.rawValue case 0x37, 0x36: mod = GHOSTTY_MODS_SUPER.rawValue + case 0x3F: mod = GHOSTTY_MODS_FN.rawValue default: return } diff --git a/src/config/Config.zig b/src/config/Config.zig index c0b1b9daf..8273553f7 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -670,9 +670,19 @@ class: ?[:0]const u8 = null, /// translated by any system keyboard layouts. Example: "ctrl+physical:a" /// /// Valid modifiers are `shift`, `ctrl` (alias: `control`), `alt` (alias: `opt`, -/// `option`), and `super` (alias: `cmd`, `command`). You may use the modifier -/// or the alias. When debugging keybinds, the non-aliased modifier will always -/// be used in output. +/// `option`), `super` (alias: `cmd`, `command`), and `function` (alias: `fn`, +/// `globe`). You may use the modifier or the alias. When debugging keybinds, +/// the non-aliased modifier will always be used in output. +/// +/// Some notes about the `function` modifier: +/// +/// * It is only available on macOS. +/// * It is used by many system shortcuts and Ghostty is not able to +/// override these shortcuts. If a system shortcut is triggered, the +/// system shortcut will take precedence. +/// * Menu items on macOS cannot be bound to the `function` modifier, +/// so this modifier will work with Ghostty but will not be visible +/// in the menu. /// /// You may also specify multiple triggers separated by `>` to require a /// sequence of triggers to activate the action. For example, diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 9f4ebd626..1d0e26b9e 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -896,9 +896,13 @@ pub const Trigger = struct { // Alias modifiers const alias_mods = .{ - .{ "cmd", "super" }, .{ "command", "super" }, - .{ "opt", "alt" }, .{ "option", "alt" }, + .{ "cmd", "super" }, + .{ "command", "super" }, + .{ "opt", "alt" }, + .{ "option", "alt" }, .{ "control", "ctrl" }, + .{ "fn", "function" }, + .{ "globe", "function" }, }; inline for (alias_mods) |pair| { if (std.mem.eql(u8, part, pair[0])) { diff --git a/src/input/key.zig b/src/input/key.zig index 8fc7c6f20..3aa85bf01 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -89,10 +89,11 @@ pub const Mods = packed struct(Mods.Backing) { ctrl: bool = false, alt: bool = false, super: bool = false, + function: bool = false, caps_lock: bool = false, num_lock: bool = false, sides: side = .{}, - _padding: u6 = 0, + _padding: u5 = 0, /// Tracks the side that is active for any given modifier. Note /// that this doesn't confirm a modifier is pressed; you must check