macos: ignore alt key with other modifiers set

This enables shifted alt-prefixed keys, such as `shift+alt+.` on
US standard becoming `M->`. To do this, we needed to fix a few bugs:

  (1) translation mods should strip alt even if other mods are set
  (2) AppKit translation event needs to construct new characters with
      the translation mods.
  (3) Alt-prefix handling in KeyEncoder needs to allow ASCII utf8
      translations even for macOS.
This commit is contained in:
Mitchell Hashimoto
2023-11-13 19:08:20 -08:00
parent ae112f48b9
commit 35e78939e5
4 changed files with 35 additions and 28 deletions

View File

@ -758,7 +758,7 @@ extension Ghostty {
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: event.characters ?? "",
characters: event.characters(byApplyingModifiers: translationMods) ?? "",
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? "",
isARepeat: event.isARepeat,
keyCode: event.keyCode

View File

@ -658,15 +658,14 @@ pub const Surface = struct {
// then we strip the alt modifier from the mods for translation.
const translate_mods = translate_mods: {
var translate_mods = mods;
switch (self.app.config.@"macos-option-as-alt") {
.false => {},
.true => translate_mods.alt = false,
.left => if (mods.sides.alt == .left) {
translate_mods.alt = false;
},
.right => if (mods.sides.alt == .right) {
translate_mods.alt = false;
},
if (comptime builtin.target.isDarwin()) {
const strip = switch (self.app.config.@"macos-option-as-alt") {
.false => false,
.true => mods.alt,
.left => mods.sides.alt == .left,
.right => mods.sides.alt == .right,
};
if (strip) translate_mods.alt = false;
}
// On macOS we strip ctrl because UCKeyTranslate

View File

@ -325,15 +325,6 @@ fn legacyAltPrefix(
.right => if (mods.sides.alt == .left) return null,
.true => {},
}
if (self.event.unshifted_codepoint > 0) {
if (std.math.cast(
u8,
self.event.unshifted_codepoint,
)) |byte| {
return byte;
}
}
}
// Otherwise, we require utf8 to already have the byte represented.
@ -1241,6 +1232,25 @@ test "legacy: alt+x macos" {
try testing.expectEqualStrings("\x1Bc", actual);
}
test "legacy: shift+alt+. macos" {
if (comptime !builtin.target.isDarwin()) return error.SkipZigTest;
var buf: [128]u8 = undefined;
var enc: KeyEncoder = .{
.event = .{
.key = .period,
.utf8 = ">",
.unshifted_codepoint = '.',
.mods = .{ .alt = true, .shift = true },
},
.alt_esc_prefix = true,
.macos_option_as_alt = .true,
};
const actual = try enc.legacy(&buf);
try testing.expectEqualStrings("\x1B>", actual);
}
test "legacy: alt+ф" {
var buf: [128]u8 = undefined;
var enc: KeyEncoder = .{

View File

@ -131,15 +131,6 @@ pub const Mods = packed struct(Mods.Backing) {
// 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,
@ -201,6 +192,13 @@ pub const Mods = packed struct(Mods.Backing) {
const result = mods.translation(.right);
try testing.expectEqual(result, mods);
}
// Set with other mods
{
const mods: Mods = .{ .alt = true, .shift = true };
const result = mods.translation(.true);
try testing.expectEqual(Mods{ .shift = true }, result);
}
}
};