feat(termio): port the IO thread, Exec backend and stream handler

Ports the remainder of ZIG/src/termio: Options, backend, message,
mailbox, Termio, Thread, Exec and stream_handler.

Threading model (replacing libxev): the IO thread owns Termio and runs a
single poll(2) loop over the mailbox eventfd, a stop eventfd, the child
pidfd and pty POLLOUT, driving Ghostty's timers (25 ms resize
coalescing, 1 s synchronized-output, 15 ms selection scroll, 200 ms
termios polling). A separate pty read thread locks the shared renderer
state and feeds the terminal. Renderer and surface interaction go
through RendererMailbox/SurfaceMailbox traits.

The stream handler answers all queries (DA, DSR, XTVERSION, DECRQM,
OSC color queries, XTGETTCAP, DECRQSS, ...) and forwards titles, pwd,
OSC 52, notifications, progress and bell as surface messages. Zig
unlocks the renderer mutex when a mailbox is full; here overflow goes to
an ordered outbox flushed after the locks drop.

IO1's local stand-ins are replaced with the real config types.

Open: RendererState lives in termio for now (renderer State.zig is
being ported); kitty graphics replies and tmux viewer are not hooked up;
wait-after-command lives in Surface (core); Flatpak stop path needs a
ghostty-os change.

Test Plan:
- cargo test -p ghostty-termio (102 unit + 21 end-to-end tests on a
  real pty, incl. backpressure, DA1 replies, resize coalescing, exits)
This commit is contained in:
ddidderr committed 2026-10-11 22:58:48 +02:00
1 parent 4a40470746
commit 6629fb0822
19 files changed
+6170 -195

No files matched your search

+51 -22
View File
@@ -89,39 +89,43 @@ impl Exit {
}
}
/// Mirrors `config.Command`'s relevant enum `LinuxCgroup`.
/// TODO(integration): replace with the ghostty-config type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LinuxCgroup {
Never,
Always,
#[default]
SingleInstance,
}
/// TODO(integration): replace with the ghostty-config type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GtkSingleInstance {
False,
True,
#[default]
Detect,
}
pub use ghostty_config::types_b::{GtkSingleInstance, LinuxCgroup};
/// Configuration information needed by the apprt pre exec function
/// (`apprt/gtk/pre_exec.zig` `PreExecInfo`). Trivially copyable.
/// TODO(integration): build from `ghostty-config::Config` (`init(cfg)`).
#[derive(Debug, Clone, Copy, Default)]
#[derive(Debug, Clone, Copy)]
pub struct RtPreExecInfo {
pub gtk_single_instance: GtkSingleInstance,
pub linux_cgroup: LinuxCgroup,
pub linux_cgroup_hard_fail: bool,
}
impl Default for RtPreExecInfo {
/// Config defaults (`linux-cgroup = single-instance`,
/// `gtk-single-instance = detect`).
fn default() -> Self {
Self {
gtk_single_instance: GtkSingleInstance::Detect,
linux_cgroup: LinuxCgroup::SingleInstance,
linux_cgroup_hard_fail: false,
}
}
}
impl RtPreExecInfo {
/// `PreExecInfo.init(cfg)`.
pub fn init(cfg: &ghostty_config::config::Config) -> Self {
Self {
gtk_single_instance: cfg.gtk_single_instance,
linux_cgroup: cfg.linux_cgroup,
linux_cgroup_hard_fail: cfg.linux_cgroup_hard_fail,
}
}
}
/// Configuration information needed by the apprt post fork function
/// (`apprt/gtk/post_fork.zig` `PostForkInfo`).
/// TODO(integration): build from `ghostty-config::Config` (`init(cfg)`).
#[derive(Debug, Clone, Copy, Default)]
#[derive(Debug, Clone, Copy)]
pub struct RtPostForkInfo {
pub gtk_single_instance: GtkSingleInstance,
pub linux_cgroup: LinuxCgroup,
@@ -130,6 +134,31 @@ pub struct RtPostForkInfo {
pub linux_cgroup_processes_limit: Option<u64>,
}
impl Default for RtPostForkInfo {
fn default() -> Self {
Self {
gtk_single_instance: GtkSingleInstance::Detect,
linux_cgroup: LinuxCgroup::SingleInstance,
linux_cgroup_hard_fail: false,
linux_cgroup_memory_limit: None,
linux_cgroup_processes_limit: None,
}
}
}
impl RtPostForkInfo {
/// `PostForkInfo.init(cfg)`.
pub fn init(cfg: &ghostty_config::config::Config) -> Self {
Self {
gtk_single_instance: cfg.gtk_single_instance,
linux_cgroup: cfg.linux_cgroup,
linux_cgroup_hard_fail: cfg.linux_cgroup_hard_fail,
linux_cgroup_memory_limit: cfg.linux_cgroup_memory_limit,
linux_cgroup_processes_limit: cfg.linux_cgroup_processes_limit,
}
}
}
/// A command to launch.
#[derive(Default)]
pub struct Command {