native: encode embedded terminal Alt as Alt on macOS

Ghostty's key encoder treats macOS Option as a text modifier unless
macos_option_as_alt is set, and Options.fromTerminal always clears it.
On macOS the embedded terminal therefore dropped the ESC prefix for
legacy Alt keys (Alt+b sent b), cleared Alt for modifyOtherKeys, and let
Alt keep associated text in Kitty report-associated mode. The host
terminal has already applied its own Option setting before a key reaches
OpenTUI, so an Alt bit in an embedded terminal key means Alt. Set
macos_option_as_alt to true; on every other OS the field is compiled out
of these branches, so only macOS changes, and it now matches Linux.

Source +3: export KeyEncodeOptions so the encoder options can be
overridden after fromTerminal; one line to set the field; one comment.
The core Alt conformance rows are the check; they reach this branch only
on the macOS CI runners.

Issue: 066
This commit is contained in:
Simon Klee committed 2026-10-09 13:16:42 +02:00
1 parent 15eb073f81
commit f0d7d1d3be
2 files changed
+5 -1

No files matched your search

@@ -50,6 +50,7 @@ pub const Mouse = struct {
}
};
pub const KeyEncodeOptions = vt.input.KeyEncodeOptions;
pub const MouseEncodeOptions = vt.input.MouseEncodeOptions;
pub const encodeKey = vt.input.encodeKey;
pub const encodeMouse = vt.input.encodeMouse;
@@ -212,7 +212,10 @@ pub const EmbeddedTerminal = struct {
pub fn encodeKey(self: *EmbeddedTerminal, key: ghostty.Key) Error![]u8 {
var output: std.Io.Writer.Allocating = .init(self.allocator);
errdefer output.deinit();
ghostty.encodeKey(&output.writer, key.event(), .fromTerminal(&self.terminal)) catch return error.OutOfMemory;
var options: ghostty.KeyEncodeOptions = .fromTerminal(&self.terminal);
// The host terminal has already applied its Option setting, so an Alt bit here means Alt.
options.macos_option_as_alt = .true;
ghostty.encodeKey(&output.writer, key.event(), options) catch return error.OutOfMemory;
return output.toOwnedSlice();
}