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 0c92dd0966
commit c81375e6f1
19 files changed
+6170 -195

No files matched your search

+120
View File
@@ -0,0 +1,120 @@
//! Backend implementations (`termio/backend.zig`). A backend is responsible
//! for owning the pty behavior and providing read/write capabilities.
use std::sync::Arc;
use ghostty_renderer::size::{GridSize, ScreenSize};
use ghostty_terminal::terminal::Terminal;
use crate::exec::{self, Exec};
use crate::sinks::SurfaceMailbox;
use crate::state::RendererState;
use crate::termio::Output;
/// The kinds of backends.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Exec,
}
/// Configuration for the various backend types.
pub enum Config {
/// Exec uses posix exec to run a command with a pty.
Exec(exec::Config),
}
/// Backend implementations.
pub enum Backend {
Exec(Exec),
}
impl Backend {
pub fn new(config: Config) -> Result<Backend, exec::Error> {
match config {
Config::Exec(cfg) => Ok(Backend::Exec(Exec::new(cfg)?)),
}
}
pub fn kind(&self) -> Kind {
match self {
Backend::Exec(_) => Kind::Exec,
}
}
pub fn init_terminal(&mut self, t: &mut Terminal) {
match self {
Backend::Exec(e) => e.init_terminal(t),
}
}
pub fn thread_enter(
&mut self,
output: Arc<Output>,
surface_mailbox: Arc<dyn SurfaceMailbox>,
renderer_state: Arc<RendererState>,
) -> Result<ThreadData, exec::Error> {
match self {
Backend::Exec(e) => Ok(ThreadData::Exec(e.thread_enter(
output,
surface_mailbox,
renderer_state,
)?)),
}
}
pub fn thread_exit(&mut self, td: &mut ThreadData) {
match (self, td) {
(Backend::Exec(e), ThreadData::Exec(td)) => e.thread_exit(td),
}
}
pub fn focus_gained(&mut self, td: &mut ThreadData, focused: bool) {
match (self, td) {
(Backend::Exec(e), ThreadData::Exec(td)) => e.focus_gained(td, focused),
}
}
pub fn resize(
&mut self,
grid_size: GridSize,
screen_size: ScreenSize,
) -> Result<(), exec::Error> {
match self {
Backend::Exec(e) => e.resize(grid_size, screen_size),
}
}
pub fn queue_write(&mut self, td: &mut ThreadData, data: &[u8], linefeed: bool) {
match (self, td) {
(Backend::Exec(e), ThreadData::Exec(td)) => e.queue_write(td, data, linefeed),
}
}
}
/// Termio thread data. See `termio::ThreadData` for docs.
pub enum ThreadData {
Exec(exec::ThreadData),
}
impl ThreadData {
pub fn kind(&self) -> Kind {
match self {
ThreadData::Exec(_) => Kind::Exec,
}
}
pub fn as_exec(&self) -> &exec::ThreadData {
match self {
ThreadData::Exec(e) => e,
}
}
pub fn as_exec_mut(&mut self) -> &mut exec::ThreadData {
match self {
ThreadData::Exec(e) => e,
}
}
/// `ThreadData.changeConfig`: nothing to do for the exec backend.
pub fn change_config(&mut self, _config: &crate::termio::DerivedConfig) {}
}