From d177b20bab4863c56c8d96fa86ab3666dd0de99b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 27 Jan 2024 08:13:53 -0800 Subject: [PATCH] macos: do not trust AppKit's text translation with ctrl only Normally, when `ctrl+` is pressed, such as `ctrl+z` or `ctrl+c`, macOS (AppKit) doesn't do any key translation because that doesn't map to any printable text on its own. Ghostty does the translation to correctly determine the character is "z" or "c" or whatever. For some reason when the keyboard layout is "Dvorak - QWERTY Cmd" specifically (_not_ plain "Dvorak") on a US layout keyboard, AppKit decides that "ctrl+z" ("/" on a qwerty keyboard) translates to "/"... I can't find any explanation for this. To workaround this, this commit makes it so that if the following conditions are true, then we IGNORE AppKit's text translation and manually do it using UCKeyTranslate: (1) We're on macOS specifically (not iOS, etc.) (2) We have a key event with ONLY control pressed This fixes `ctrl+z` on this unique Dvorak keyboard layout. --- src/apprt/embedded.zig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index d8e915550..8b6d7c51e 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -765,6 +765,24 @@ pub const Surface = struct { break :translate_mods translate_mods; }; + const event_text: ?[]const u8 = event_text: { + // This logic only applies to macOS. + if (comptime builtin.os.tag != .macos) break :event_text event.text; + + // If the modifiers are ONLY "control" then we never process + // the event text because we want to do our own translation so + // we can handle ctrl+c, ctrl+z, etc. + // + // This is specifically because on macOS using the + // "Dvorak - QWERTY ⌘" keyboard layout, ctrl+z is translated as + // "/" (the physical key that is z on a qwerty keyboard). But on + // other layouts, ctrl+ is not translated by AppKit. So, + // we just avoid this by never allowing AppKit to translate + // ctrl+ and instead do it ourselves. + const ctrl_only = comptime (input.Mods{ .ctrl = true }).int(); + break :event_text if (mods.int() == ctrl_only) null else event.text; + }; + // Translate our key using the keymap for our localized keyboard layout. // We only translate for keydown events. Otherwise, we only care about // the raw keycode. @@ -772,7 +790,7 @@ pub const Surface = struct { const result: input.Keymap.Translation = if (is_down) translate: { // If the event provided us with text, then we use this as a result // and do not do manual translation. - const result: input.Keymap.Translation = if (event.text) |text| .{ + const result: input.Keymap.Translation = if (event_text) |text| .{ .text = text, .composing = event.composing, } else try self.app.keymap.translate(