input: legacy encoding never encodes text for command mods on macOS (#6057)

Fixes #5929
Replaces #5984

On macOS, native applications typically never encode any text for key
events that use the command key. This is because the command key is used
for key equivalents and "commands" and should not be used for text
input.

This can be verified with apps like TextEdit but also terminals like
Terminal.app officially but also iTerm2 unofficially. Anything such as
`Cmd+b` or `Cmd+Shift+b` will not produce any text input.

Cross-platform terminals generally don't follow this, for example Kitty
performs CSI-u encoding and Alacritty and WezTerm encode the text as-is
(i.e. `Cmd+b` will produce `b`).

On Linux, the super key (command-equivalent) does produce text input.
For example, `Super+b` will produce `b` in Gnome Console, Foot, and all
the cross-platform terminals mentioned above.

In the interest of matching the behavior of native macOS applications,
we should not encode text for command key events on macOS. We continue
to encode text for the super key on non-macOS platforms. This matches
the behaviors appropriately on each platform.
This commit is contained in:
Tim Culverhouse
2025-03-02 16:21:02 -06:00
committed by GitHub

View File

@ -35,6 +35,7 @@ pub fn encode(
self: *const KeyEncoder,
buf: []u8,
) ![]const u8 {
// log.warn("KEYENCODER self={}", .{self.j});
if (self.kitty_flags.int() != 0) return try self.kitty(buf);
return try self.legacy(buf);
}
@ -413,6 +414,19 @@ fn legacy(
return try std.fmt.bufPrint(buf, "\x1B{c}", .{byte});
}
// If we are on macOS, command+keys do not encode text. It isn't
// typical for command+keys on macOS to ever encode text. They
// don't in native text inputs (i.e. TextEdit) and they also don't
// in other native terminals (Terminal.app officially but also
// iTerm2).
//
// For Linux, we continue to encode text because it is typical.
// For example on Gnome Console Super+b will encode a "b" character
// with legacy encoding.
if ((comptime builtin.os.tag == .macos) and all_mods.super) {
return "";
}
return try copyToBuf(buf, utf8);
}
@ -2163,6 +2177,38 @@ test "legacy: hu layout ctrl+ő sends proper codepoint" {
try testing.expectEqualStrings("[337;5u", actual[1..]);
}
test "legacy: super-only on macOS with text" {
if (comptime builtin.os.tag != .macos) return error.SkipZigTest;
var buf: [128]u8 = undefined;
var enc: KeyEncoder = .{
.event = .{
.key = .b,
.utf8 = "b",
.mods = .{ .super = true },
},
};
const actual = try enc.legacy(&buf);
try testing.expectEqualStrings("", actual);
}
test "legacy: super and other mods on macOS with text" {
if (comptime builtin.os.tag != .macos) return error.SkipZigTest;
var buf: [128]u8 = undefined;
var enc: KeyEncoder = .{
.event = .{
.key = .b,
.utf8 = "B",
.mods = .{ .super = true, .shift = true },
},
};
const actual = try enc.legacy(&buf);
try testing.expectEqualStrings("", actual);
}
test "ctrlseq: normal ctrl c" {
const seq = ctrlSeq(.invalid, "c", 'c', .{ .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);