mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-13 19:15:48 +00:00
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:
@@ -15,7 +15,9 @@
|
||||
* ## Basic Usage
|
||||
*
|
||||
* 1. Create an encoder instance with ghostty_key_encoder_new()
|
||||
* 2. Configure encoder options with ghostty_key_encoder_setopt().
|
||||
* 2. Configure encoder options with ghostty_key_encoder_setopt()
|
||||
* or ghostty_key_encoder_setopt_from_terminal() if you have a
|
||||
* GhosttyTerminal.
|
||||
* 3. For each key event:
|
||||
* - Create a key event with ghostty_key_event_new()
|
||||
* - Set event properties (action, key, modifiers, etc.)
|
||||
@@ -25,6 +27,9 @@
|
||||
* changing its properties.
|
||||
* 4. Free the encoder with ghostty_key_encoder_free() when done
|
||||
*
|
||||
* For a complete working example, see example/c-vt-key-encode in the
|
||||
* repository.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* @code{.c}
|
||||
@@ -66,8 +71,33 @@
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* For a complete working example, see example/c-vt-key-encode in the
|
||||
* repository.
|
||||
* ## Example: Encoding with Terminal State
|
||||
*
|
||||
* When you have a GhosttyTerminal, you can sync its modes (cursor key
|
||||
* application, Kitty flags, etc.) into the encoder automatically:
|
||||
*
|
||||
* @code{.c}
|
||||
* // Create a terminal and feed it some VT data that changes modes
|
||||
* GhosttyTerminal terminal;
|
||||
* ghostty_terminal_new(NULL, &terminal,
|
||||
* (GhosttyTerminalOptions){.cols = 80, .rows = 24, .max_scrollback = 0});
|
||||
*
|
||||
* // Application might write data that enables Kitty keyboard protocol, etc.
|
||||
* ghostty_terminal_vt_write(terminal, vt_data, vt_len);
|
||||
*
|
||||
* // Create an encoder and sync its options from the terminal
|
||||
* GhosttyKeyEncoder encoder;
|
||||
* ghostty_key_encoder_new(NULL, &encoder);
|
||||
* ghostty_key_encoder_setopt_from_terminal(encoder, terminal);
|
||||
*
|
||||
* // Encode a key event using the terminal-derived options
|
||||
* char buf[128];
|
||||
* size_t written = 0;
|
||||
* ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written);
|
||||
*
|
||||
* ghostty_key_encoder_free(encoder);
|
||||
* ghostty_terminal_free(terminal);
|
||||
* @endcode
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdint.h>
|
||||
#include <ghostty/vt/types.h>
|
||||
#include <ghostty/vt/allocator.h>
|
||||
#include <ghostty/vt/terminal.h>
|
||||
#include <ghostty/vt/key/event.h>
|
||||
|
||||
/**
|
||||
@@ -140,6 +141,10 @@ void ghostty_key_encoder_free(GhosttyKeyEncoder encoder);
|
||||
* protocol selection (Kitty keyboard protocol flags), and platform-specific
|
||||
* behaviors (macOS option-as-alt).
|
||||
*
|
||||
* If you are using a terminal instance, you can set the key encoding
|
||||
* options based on the active terminal state (e.g. legacy vs Kitty mode
|
||||
* and associated flags) with ghostty_key_encoder_setopt_from_terminal().
|
||||
*
|
||||
* A null pointer value does nothing. It does not reset the value to the
|
||||
* default. The setopt call will do nothing.
|
||||
*
|
||||
@@ -151,6 +156,25 @@ void ghostty_key_encoder_free(GhosttyKeyEncoder encoder);
|
||||
*/
|
||||
void ghostty_key_encoder_setopt(GhosttyKeyEncoder encoder, GhosttyKeyEncoderOption option, const void *value);
|
||||
|
||||
/**
|
||||
* Set encoder options from a terminal's current state.
|
||||
*
|
||||
* Reads the terminal's current modes and flags and applies them to the
|
||||
* encoder's options. This sets cursor key application mode, keypad mode,
|
||||
* alt escape prefix, modifyOtherKeys state, and Kitty keyboard protocol
|
||||
* flags from the terminal state.
|
||||
*
|
||||
* Note that the `macos_option_as_alt` option cannot be determined from
|
||||
* terminal state and is reset to `GHOSTTY_OPTION_AS_ALT_FALSE` by this
|
||||
* call. Use ghostty_key_encoder_setopt() to set it afterward if needed.
|
||||
*
|
||||
* @param encoder The encoder handle, must not be NULL
|
||||
* @param terminal The terminal handle, must not be NULL
|
||||
*
|
||||
* @ingroup key
|
||||
*/
|
||||
void ghostty_key_encoder_setopt_from_terminal(GhosttyKeyEncoder encoder, GhosttyTerminal terminal);
|
||||
|
||||
/**
|
||||
* Encode a key event into a terminal escape sequence.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,9 @@ extern "C" {
|
||||
* A terminal instance manages the full emulator state including the screen,
|
||||
* scrollback, cursor, styles, modes, and VT stream processing.
|
||||
*
|
||||
* Once a terminal session is up and running, you can configure a key encoder
|
||||
* to write keyboard input via ghostty_key_encoder_setopt_from_terminal().
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user