mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-20 12:01:34 +00:00
macos: filter option in AppKit when option-as-alt set
Fixes #872 In #867 we fixed macos-option-as-alt, but unfortunately AppKit ALSO does some translation so some behaviors were not working correctly. Specifically, when you had macos-option-as-alt set, option+e would properly send `esc+e` to the pty but it would ALSO set the dead key state for "`" since AppKit was still translating the option key. This commit introduces a function to strip alt when necessary from the translation modifiers used at the AppKit layer, preventing this behavior.
This commit is contained in:
@@ -1026,6 +1026,8 @@ fn resize(self: *Surface, size: renderer.ScreenSize) !void {
|
||||
/// keyCallback and we rely completely on the apprt implementation to track
|
||||
/// the preedit state correctly.
|
||||
pub fn preeditCallback(self: *Surface, preedit_: ?u21) !void {
|
||||
// log.debug("preedit cp={any}", .{preedit_});
|
||||
|
||||
const preedit: ?renderer.State.Preedit = if (preedit_) |cp| preedit: {
|
||||
const width = ziglyph.display_width.codePointWidth(cp, .half);
|
||||
|
||||
|
||||
@@ -1348,6 +1348,24 @@ pub const CAPI = struct {
|
||||
surface.focusCallback(focused);
|
||||
}
|
||||
|
||||
/// Filter the mods if necessary. This handles settings such as
|
||||
/// `macos-option-as-alt`. The filtered mods should be used for
|
||||
/// key translation but should NOT be sent back via the `_key`
|
||||
/// function -- the original mods should be used for that.
|
||||
export fn ghostty_surface_key_translation_mods(
|
||||
surface: *Surface,
|
||||
mods_raw: c_int,
|
||||
) c_int {
|
||||
const mods: input.Mods = @bitCast(@as(
|
||||
input.Mods.Backing,
|
||||
@truncate(@as(c_uint, @bitCast(mods_raw))),
|
||||
));
|
||||
const result = mods.translation(
|
||||
surface.core_surface.config.macos_option_as_alt,
|
||||
);
|
||||
return @intCast(@as(input.Mods.Backing, @bitCast(result)));
|
||||
}
|
||||
|
||||
/// Send this for raw keypresses (i.e. the keyDown event on macOS).
|
||||
/// This will handle the keymap translation and send the appropriate
|
||||
/// key and char events.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const cimgui = @import("cimgui");
|
||||
const config = @import("../config.zig");
|
||||
|
||||
/// A generic key input event. This is the information that is necessary
|
||||
/// regardless of apprt in order to generate the proper terminal
|
||||
@@ -121,6 +123,37 @@ pub const Mods = packed struct(Mods.Backing) {
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// Return the mods to use for key translation. This handles settings
|
||||
/// like macos-option-as-alt. The translation mods should be used for
|
||||
/// translation but never sent back in for the key callback.
|
||||
pub fn translation(self: Mods, option_as_alt: config.OptionAsAlt) Mods {
|
||||
// We currently only process macos-option-as-alt so other
|
||||
// platforms don't need to do anything.
|
||||
if (comptime !builtin.target.isDarwin()) return self;
|
||||
|
||||
// We care if only alt is set.
|
||||
const alt_only: bool = alt_only: {
|
||||
const alt_mods: Mods = .{ .alt = true };
|
||||
var compare = self;
|
||||
compare.sides = .{};
|
||||
break :alt_only alt_mods.equal(compare);
|
||||
};
|
||||
if (!alt_only) return self;
|
||||
|
||||
// Alt has to be set only on the correct side
|
||||
switch (option_as_alt) {
|
||||
.false => return self,
|
||||
.true => {},
|
||||
.left => if (self.sides.alt == .right) return self,
|
||||
.right => if (self.sides.alt == .left) return self,
|
||||
}
|
||||
|
||||
// Unset alt
|
||||
var result = self;
|
||||
result.alt = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// For our own understanding
|
||||
test {
|
||||
const testing = std.testing;
|
||||
@@ -130,6 +163,45 @@ pub const Mods = packed struct(Mods.Backing) {
|
||||
@as(Backing, 0b0000_0001),
|
||||
);
|
||||
}
|
||||
|
||||
test "translation macos-option-as-alt" {
|
||||
if (comptime !builtin.target.isDarwin()) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
// Unset
|
||||
{
|
||||
const mods: Mods = .{};
|
||||
const result = mods.translation(.true);
|
||||
try testing.expectEqual(result, mods);
|
||||
}
|
||||
|
||||
// Set
|
||||
{
|
||||
const mods: Mods = .{ .alt = true };
|
||||
const result = mods.translation(.true);
|
||||
try testing.expectEqual(Mods{}, result);
|
||||
}
|
||||
|
||||
// Set but disabled
|
||||
{
|
||||
const mods: Mods = .{ .alt = true };
|
||||
const result = mods.translation(.false);
|
||||
try testing.expectEqual(result, mods);
|
||||
}
|
||||
|
||||
// Set wrong side
|
||||
{
|
||||
const mods: Mods = .{ .alt = true, .sides = .{ .alt = .right } };
|
||||
const result = mods.translation(.left);
|
||||
try testing.expectEqual(result, mods);
|
||||
}
|
||||
{
|
||||
const mods: Mods = .{ .alt = true, .sides = .{ .alt = .left } };
|
||||
const result = mods.translation(.right);
|
||||
try testing.expectEqual(result, mods);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// The action associated with an input event. This is backed by a c_int
|
||||
|
||||
Reference in New Issue
Block a user