Files
ghostty-rs/crates/ghostty-termio/src/backend.rs
T
ddidderr ad76ae2301 feat(core): port App/Surface and wire up the runnable ghostty binary
This is the integration step that turns the ported crates into a working
terminal: `cargo run` now opens a GTK window running the user's shell.

ghostty-core ports App.zig, Surface.zig (split into keyboard, mouse,
binding, clipboard and config submodules), surface_mouse.zig and
global.zig, and implements the runtime traits. perform_binding_action
handles every binding action.

Cross-crate integration:
- The core<->runtime traits (CoreApp, CoreSurface, RtApp, RtSurface,
  Waker) move from apprt-gtk into ghostty_apprt::runtime_api so the core
  stays runtime-agnostic; Scrollbar/RendererHealth stand-ins are replaced
  by the real terminal/renderer types.
- termio now uses ghostty_renderer::state::{State, SharedState}; the
  renderer calls kitty_update; image-storage-limit is applied at init
  and on config change.
- The search thread locks the terminal through a TerminalLock trait so
  it can share the render state mutex; termio's Wakeup gained a hook to
  wake the renderer thread.
- apprt-gtk: GL proc addresses are resolved through libepoxy's
  epoxy_glFoo pointer variables (plain glFoo symbols resolved to null),
  default DPI fixed to 96, libadwaita version read without GTK init so
  --version works headless.

The root binary dispatches +actions to ghostty-cli (list-fonts,
show-face and new-window are now wired) and otherwise runs the GTK app
with the real core.

Verified at runtime on Wayland and X11: shell prompt and colored output
render, child exit closes the window cleanly. Not yet verified: keyboard
and mouse input, clipboard, IME, tabs and splits (no key injection was
possible in this environment). GHOSTTY_DEBUG_SCREENSHOT=<png> dumps the
renderer's last frame for headless checks.

Test Plan:
- cargo test --workspace (2890 passed)
- cargo clippy --workspace --all-targets -- -D warnings
- GSETTINGS_BACKEND=memory GHOSTTY_RESOURCES_DIR=/usr/share/ghostty \
  cargo run
2026-10-12 01:33:44 +02:00

121 lines
3.0 KiB
Rust

//! 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::SharedState;
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: SharedState,
) -> 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) {}
}