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
This commit is contained in:
1 parent
9c3eeeba50
commit
3266108c10
42 files changed
+9301
-179
No files matched your search
@@ -13,7 +13,8 @@
|
||||
//! based refresh timer. The terminal and its mutex are combined into an
|
||||
//! `Arc<Mutex<Terminal>>`.
|
||||
|
||||
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Mutex, PoisonError};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_channel::{Receiver, Sender, TrySendError};
|
||||
@@ -50,7 +51,7 @@ pub type EventCallback = Arc<dyn Fn(Event) + Send + Sync>;
|
||||
pub struct Options {
|
||||
/// The terminal data to search. The mutex must be held while
|
||||
/// reading/writing the terminal.
|
||||
pub terminal: Arc<Mutex<Terminal>>,
|
||||
pub terminal: Arc<dyn TerminalLock>,
|
||||
|
||||
/// The callback for events from the search thread. This can be `None`
|
||||
/// if you don't want to receive events, which could be useful for a
|
||||
@@ -146,8 +147,26 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
fn lock(m: &Mutex<Terminal>) -> MutexGuard<'_, Terminal> {
|
||||
m.lock().unwrap_or_else(PoisonError::into_inner)
|
||||
/// A locked terminal (any guard that derefs to a [`Terminal`]).
|
||||
pub trait TerminalRef: DerefMut<Target = Terminal> {}
|
||||
impl<T: DerefMut<Target = Terminal>> TerminalRef for T {}
|
||||
|
||||
/// Something that owns a terminal behind a lock. Implemented for
|
||||
/// `Mutex<Terminal>`; the core implements it for the shared renderer state
|
||||
/// (where the terminal lives next to preedit/mouse state).
|
||||
pub trait TerminalLock: Send + Sync {
|
||||
/// Lock and borrow the terminal (poisoning is ignored).
|
||||
fn lock_terminal(&self) -> Box<dyn TerminalRef + '_>;
|
||||
}
|
||||
|
||||
impl TerminalLock for Mutex<Terminal> {
|
||||
fn lock_terminal(&self) -> Box<dyn TerminalRef + '_> {
|
||||
Box::new(self.lock().unwrap_or_else(PoisonError::into_inner))
|
||||
}
|
||||
}
|
||||
|
||||
fn lock(m: &Arc<dyn TerminalLock>) -> Box<dyn TerminalRef + '_> {
|
||||
m.lock_terminal()
|
||||
}
|
||||
|
||||
/// The search thread state.
|
||||
@@ -677,7 +696,7 @@ impl Search {
|
||||
/// This doesn't require any locking as it only reads internal state
|
||||
/// (the terminal mutex is only passed through for API symmetry and is
|
||||
/// not locked).
|
||||
fn notify(&mut self, _terminal: &Arc<Mutex<Terminal>>, cb: &EventCallback) {
|
||||
fn notify(&mut self, _terminal: &Arc<dyn TerminalLock>, cb: &EventCallback) {
|
||||
let Some(screen_search) = self.screens[key_index(self.last_screen.key)].as_ref() else {
|
||||
return;
|
||||
};
|
||||
@@ -787,7 +806,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let (handle, join) = Thread::spawn(Options {
|
||||
terminal: Arc::clone(&terminal),
|
||||
terminal: terminal.clone(),
|
||||
event_cb: Some(cb),
|
||||
})
|
||||
.unwrap();
|
||||
@@ -845,7 +864,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let (handle, join) = Thread::spawn(Options {
|
||||
terminal: Arc::clone(&terminal),
|
||||
terminal: terminal.clone(),
|
||||
event_cb: Some(cb),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Reference in new issue
Block a user