vt: add setopt_from_terminal to C API

Expose the key encoder Options.fromTerminal function to the C API as
ghostty_key_encoder_setopt_from_terminal. This lets C callers sync all
terminal-derived encoding options (cursor key application mode, keypad
mode, alt escape prefix, modifyOtherKeys, and Kitty flags) in a single
call instead of setting each option individually.
This commit is contained in:
Mitchell Hashimoto
2026-03-15 07:03:44 -07:00
parent c9236558b1
commit 943d3d2e89
6 changed files with 130 additions and 3 deletions

View File

@@ -124,6 +124,7 @@ comptime {
@export(&c.key_encoder_new, .{ .name = "ghostty_key_encoder_new" });
@export(&c.key_encoder_free, .{ .name = "ghostty_key_encoder_free" });
@export(&c.key_encoder_setopt, .{ .name = "ghostty_key_encoder_setopt" });
@export(&c.key_encoder_setopt_from_terminal, .{ .name = "ghostty_key_encoder_setopt_from_terminal" });
@export(&c.key_encoder_encode, .{ .name = "ghostty_key_encoder_encode" });
@export(&c.osc_new, .{ .name = "ghostty_osc_new" });
@export(&c.osc_free, .{ .name = "ghostty_osc_free" });

View File

@@ -8,6 +8,7 @@ const KittyFlags = @import("../../terminal/kitty/key.zig").Flags;
const OptionAsAlt = @import("../../input/config.zig").OptionAsAlt;
const Result = @import("result.zig").Result;
const KeyEvent = @import("key_event.zig").Event;
const Terminal = @import("terminal.zig").Terminal;
const log = std.log.scoped(.key_encode);
@@ -115,6 +116,15 @@ fn setoptTyped(
}
}
pub fn setopt_from_terminal(
encoder_: Encoder,
terminal_: Terminal,
) callconv(.c) void {
const wrapper = encoder_ orelse return;
const t = terminal_ orelse return;
wrapper.opts = .fromTerminal(t);
}
pub fn encode(
encoder_: Encoder,
event_: KeyEvent,
@@ -222,6 +232,64 @@ test "setopt macos option as alt" {
try testing.expectEqual(OptionAsAlt.true, e.?.opts.macos_option_as_alt);
}
test "setopt_from_terminal" {
const testing = std.testing;
const terminal_c = @import("terminal.zig");
// Create encoder
var e: Encoder = undefined;
try testing.expectEqual(Result.success, new(
&lib_alloc.test_allocator,
&e,
));
defer free(e);
// Create terminal
var t: Terminal = undefined;
try testing.expectEqual(Result.success, terminal_c.new(
&lib_alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
// Apply terminal state to encoder
setopt_from_terminal(e, t);
// Options should reflect defaults from a fresh terminal
try testing.expect(!e.?.opts.cursor_key_application);
try testing.expect(!e.?.opts.alt_esc_prefix);
try testing.expectEqual(KittyFlags.disabled, e.?.opts.kitty_flags);
try testing.expectEqual(OptionAsAlt.false, e.?.opts.macos_option_as_alt);
}
test "setopt_from_terminal null" {
// Both null should be no-ops
setopt_from_terminal(null, null);
const testing = std.testing;
// Encoder null with valid terminal
const terminal_c = @import("terminal.zig");
var t: Terminal = undefined;
try testing.expectEqual(Result.success, terminal_c.new(
&lib_alloc.test_allocator,
&t,
.{ .cols = 80, .rows = 24, .max_scrollback = 0 },
));
defer terminal_c.free(t);
setopt_from_terminal(null, t);
// Valid encoder with null terminal
var e: Encoder = undefined;
try testing.expectEqual(Result.success, new(
&lib_alloc.test_allocator,
&e,
));
defer free(e);
setopt_from_terminal(e, null);
}
test "encode: kitty ctrl release with ctrl mod set" {
const testing = std.testing;

View File

@@ -55,6 +55,7 @@ pub const key_event_get_unshifted_codepoint = key_event.get_unshifted_codepoint;
pub const key_encoder_new = key_encode.new;
pub const key_encoder_free = key_encode.free;
pub const key_encoder_setopt = key_encode.setopt;
pub const key_encoder_setopt_from_terminal = key_encode.setopt_from_terminal;
pub const key_encoder_encode = key_encode.encode;
pub const paste_is_safe = paste.is_safe;