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
@@ -8,7 +8,7 @@ use ghostty_terminal::terminal::Terminal;
|
||||
|
||||
use crate::exec::{self, Exec};
|
||||
use crate::sinks::SurfaceMailbox;
|
||||
use crate::state::RendererState;
|
||||
use crate::state::SharedState;
|
||||
use crate::termio::Output;
|
||||
|
||||
/// The kinds of backends.
|
||||
@@ -51,7 +51,7 @@ impl Backend {
|
||||
&mut self,
|
||||
output: Arc<Output>,
|
||||
surface_mailbox: Arc<dyn SurfaceMailbox>,
|
||||
renderer_state: Arc<RendererState>,
|
||||
renderer_state: SharedState,
|
||||
) -> Result<ThreadData, exec::Error> {
|
||||
match self {
|
||||
Backend::Exec(e) => Ok(ThreadData::Exec(e.thread_enter(
|
||||
|
||||
@@ -40,7 +40,7 @@ use crate::command::{
|
||||
use crate::pty::{self, Mode, Pty, Winsize};
|
||||
use crate::shell_integration::{self, EnvMap, Shell};
|
||||
use crate::sinks::SurfaceMailbox;
|
||||
use crate::state::RendererState;
|
||||
use crate::state::{SharedState, SharedStateExt};
|
||||
use crate::termio::Output;
|
||||
use crate::wakeup::Wakeup;
|
||||
use ghostty_apprt::surface::{ChildExited, Message as SurfaceMessage};
|
||||
@@ -192,7 +192,7 @@ impl Exec {
|
||||
&mut self,
|
||||
output: Arc<Output>,
|
||||
surface_mailbox: Arc<dyn SurfaceMailbox>,
|
||||
renderer_state: Arc<RendererState>,
|
||||
renderer_state: SharedState,
|
||||
) -> Result<ThreadData, Error> {
|
||||
// Start our subprocess
|
||||
let (read_fd, write_fd) = self.subprocess.start()?;
|
||||
@@ -382,7 +382,7 @@ pub struct ThreadData {
|
||||
termios_mode: Mode,
|
||||
|
||||
surface_mailbox: Arc<dyn SurfaceMailbox>,
|
||||
renderer_state: Arc<RendererState>,
|
||||
renderer_state: SharedState,
|
||||
}
|
||||
|
||||
impl ThreadData {
|
||||
@@ -579,7 +579,7 @@ impl ThreadData {
|
||||
// If our password input state changed on the terminal then
|
||||
// we notify the surface.
|
||||
let changed = {
|
||||
let t = self.renderer_state.lock();
|
||||
let t = self.renderer_state.lock_terminal();
|
||||
t.flags.password_input != password_input
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ pub use message::Message;
|
||||
pub use options::Options;
|
||||
pub use pty::{Pty, Winsize};
|
||||
pub use sinks::{RendererMailbox, SurfaceMailbox};
|
||||
pub use state::RendererState;
|
||||
pub use state::{SharedState, SharedStateExt, State, TerminalGuard};
|
||||
pub use stream_handler::StreamHandler;
|
||||
pub use termio::{DerivedConfig, Termio};
|
||||
pub use thread::Thread;
|
||||
|
||||
@@ -1,37 +1,49 @@
|
||||
//! The shared render state (`renderer/State.zig`, the part termio needs).
|
||||
//! The shared render state (`renderer/State.zig`).
|
||||
//!
|
||||
//! In Zig the terminal is owned by `Termio` and `renderer.State` holds a
|
||||
//! mutex plus a pointer to it. In Rust the terminal lives inside the mutex,
|
||||
//! shared via `Arc` between the IO thread, the pty read thread, the
|
||||
//! surface (core) thread and the renderer thread.
|
||||
//!
|
||||
//! TODO(integration): `renderer/State.zig` is deferred in ghostty-renderer;
|
||||
//! when it lands, this type should move there (and gain `inspector`,
|
||||
//! `preedit`, `mouse`).
|
||||
//! The canonical type lives in `ghostty-renderer`
|
||||
//! ([`ghostty_renderer::state::State`] behind a mutex,
|
||||
//! [`SharedState`]). This module only provides a convenience for termio:
|
||||
//! locking the state and borrowing just the [`Terminal`].
|
||||
|
||||
use std::sync::{Mutex, MutexGuard, PoisonError};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{MutexGuard, PoisonError};
|
||||
|
||||
pub use ghostty_renderer::state::{SharedState, State};
|
||||
use ghostty_terminal::terminal::Terminal;
|
||||
|
||||
/// The state shared with the renderer.
|
||||
#[derive(Debug)]
|
||||
pub struct RendererState {
|
||||
/// The terminal emulator state. Hold the lock for as short as possible;
|
||||
/// it is contended by the pty read thread.
|
||||
pub terminal: Mutex<Terminal>,
|
||||
}
|
||||
/// A locked [`SharedState`] that dereferences to its [`Terminal`].
|
||||
pub struct TerminalGuard<'a>(MutexGuard<'a, State>);
|
||||
|
||||
impl RendererState {
|
||||
pub fn new(terminal: Terminal) -> Self {
|
||||
Self {
|
||||
terminal: Mutex::new(terminal),
|
||||
}
|
||||
impl TerminalGuard<'_> {
|
||||
/// The whole locked state (preedit, mouse, ...).
|
||||
pub fn state(&mut self) -> &mut State {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Lock the terminal. Lock poisoning is ignored: a panic on another
|
||||
/// thread must not take the whole terminal down with it (Zig mutexes
|
||||
/// don't poison).
|
||||
pub fn lock(&self) -> MutexGuard<'_, Terminal> {
|
||||
self.terminal.lock().unwrap_or_else(PoisonError::into_inner)
|
||||
impl Deref for TerminalGuard<'_> {
|
||||
type Target = Terminal;
|
||||
fn deref(&self) -> &Terminal {
|
||||
&self.0.terminal
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TerminalGuard<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Terminal {
|
||||
&mut self.0.terminal
|
||||
}
|
||||
}
|
||||
|
||||
/// Lock helpers for [`SharedState`].
|
||||
pub trait SharedStateExt {
|
||||
/// Lock the state and borrow the terminal. Lock poisoning is ignored: a
|
||||
/// panic on another thread must not take the whole terminal down with
|
||||
/// it (Zig mutexes don't poison).
|
||||
fn lock_terminal(&self) -> TerminalGuard<'_>;
|
||||
}
|
||||
|
||||
impl SharedStateExt for SharedState {
|
||||
fn lock_terminal(&self) -> TerminalGuard<'_> {
|
||||
TerminalGuard(self.lock().unwrap_or_else(PoisonError::into_inner))
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@
|
||||
//! * [`Termio`] is owned by the IO thread (`Thread::thread_main` takes it by
|
||||
//! value) and holds the backend, derived config and cached size.
|
||||
//! * [`Output`] (an `Arc`, reachable through [`Termio::output`]) is the part
|
||||
//! the pty read thread needs: the shared [`RendererState`], the VT stream and
|
||||
//! the pty read thread needs: the shared [`SharedState`], the VT stream and
|
||||
//! the renderer/termio notification handles. `Termio::process_output` is
|
||||
//! just `output.process_output`.
|
||||
//! * The `Terminal` itself lives in [`RendererState`]
|
||||
//! * The `Terminal` itself lives in [`SharedState`]
|
||||
//! (`Arc<Mutex<Terminal>>`), shared with the renderer like Zig's
|
||||
//! `renderer.State`.
|
||||
//!
|
||||
@@ -34,12 +34,14 @@ use ghostty_config::types_b::{ClipboardAccess, GraphemeWidthMethod, OSCColorRepo
|
||||
use ghostty_datastruct::blocking_queue::Timeout;
|
||||
use ghostty_renderer::message::Message as RendererMessage;
|
||||
use ghostty_renderer::size::Size;
|
||||
use ghostty_terminal::kitty::graphics_storage::TerminalInfo;
|
||||
use ghostty_terminal::point::{Coordinate, Point};
|
||||
use ghostty_terminal::screen::Scroll;
|
||||
use ghostty_terminal::screen_set::Key as ScreenKey;
|
||||
use ghostty_terminal::terminal::{Colors, Options as TerminalOptions, ScrollViewport, Terminal};
|
||||
use ghostty_vt::color::{DynamicPalette, DynamicRgb, Palette};
|
||||
use ghostty_vt::csi;
|
||||
use ghostty_vt::kitty::graphics_command::Delete as KittyDelete;
|
||||
use ghostty_vt::modes::{Mode, ModePacked};
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
@@ -49,7 +51,7 @@ use crate::mailbox::Mailbox;
|
||||
use crate::message::{Message, SizeReport};
|
||||
use crate::options::Options;
|
||||
use crate::sinks::{RendererMailbox, SurfaceMailbox};
|
||||
use crate::state::RendererState;
|
||||
use crate::state::{SharedState, SharedStateExt, State};
|
||||
use crate::stream_handler::{
|
||||
HandlerOptions, HandlerState, TerminalStream, cursor_style_from_config,
|
||||
};
|
||||
@@ -201,7 +203,7 @@ struct OutputInner {
|
||||
/// The part of termio used from the pty read thread: parses the pty output
|
||||
/// and applies it to the shared terminal.
|
||||
pub struct Output {
|
||||
state: Arc<RendererState>,
|
||||
state: SharedState,
|
||||
inner: Mutex<OutputInner>,
|
||||
renderer_mailbox: Arc<dyn RendererMailbox>,
|
||||
mailbox: Mailbox,
|
||||
@@ -220,7 +222,7 @@ impl Output {
|
||||
// We are modifying terminal state from here on out and we need
|
||||
// the lock to grab our read data.
|
||||
let outbox = {
|
||||
let mut term = self.state.lock();
|
||||
let mut term = self.state.lock_terminal();
|
||||
let mut inner = self.inner();
|
||||
self.process_output_locked(&mut term, &mut inner, buf);
|
||||
|
||||
@@ -275,7 +277,7 @@ pub struct Termio {
|
||||
config: DerivedConfig,
|
||||
|
||||
/// The shared render state (contains the terminal).
|
||||
renderer_state: Arc<RendererState>,
|
||||
renderer_state: SharedState,
|
||||
|
||||
/// The part shared with the pty read thread.
|
||||
output: Arc<Output>,
|
||||
@@ -309,7 +311,7 @@ pub struct Termio {
|
||||
/// the termio thread. As such, a lock is not necessary.
|
||||
pub struct ThreadData {
|
||||
/// The shared render state
|
||||
pub renderer_state: Arc<RendererState>,
|
||||
pub renderer_state: SharedState,
|
||||
|
||||
/// Mailboxes for different threads
|
||||
pub surface_mailbox: Arc<dyn SurfaceMailbox>,
|
||||
@@ -371,8 +373,7 @@ impl Termio {
|
||||
})?;
|
||||
|
||||
// Set the image size limits
|
||||
// TODO(integration): `screen.kitty_images.setLimit(config.image_storage_limit)`
|
||||
// once ghostty-terminal ports kitty `ImageStorage` (currently a stub).
|
||||
set_image_limits(&mut term, config.image_storage_limit);
|
||||
|
||||
// Set our default cursor style
|
||||
term.screen_mut().cursor.cursor_style = cursor_style_from_config(config.cursor_style);
|
||||
@@ -399,7 +400,7 @@ impl Termio {
|
||||
|
||||
let thread_enter_state = ThreadEnterState::create(full_config);
|
||||
|
||||
let renderer_state = Arc::new(RendererState::new(term));
|
||||
let renderer_state = State::new(term).into_shared();
|
||||
let output = Arc::new(Output {
|
||||
state: Arc::clone(&renderer_state),
|
||||
inner: Mutex::new(OutputInner {
|
||||
@@ -427,7 +428,7 @@ impl Termio {
|
||||
|
||||
/// The shared render state (it holds the terminal). Hand this to the
|
||||
/// renderer.
|
||||
pub fn renderer_state(&self) -> &Arc<RendererState> {
|
||||
pub fn renderer_state(&self) -> &SharedState {
|
||||
&self.renderer_state
|
||||
}
|
||||
|
||||
@@ -522,7 +523,7 @@ impl Termio {
|
||||
// The remainder of this function is modifying terminal state or
|
||||
// the read thread data, all of which requires holding the renderer
|
||||
// state lock.
|
||||
let mut term = self.renderer_state.lock();
|
||||
let mut term = self.renderer_state.lock_terminal();
|
||||
|
||||
// Update our stream handler. The stream handler uses the same
|
||||
// renderer mutex so this is safe to do despite being executed
|
||||
@@ -546,7 +547,7 @@ impl Termio {
|
||||
term.colors.cursor.default = config.cursor_color.and_then(TerminalColor::to_terminal_rgb);
|
||||
|
||||
// Set the image size limits
|
||||
// TODO(integration): kitty_images.setLimit (see `init`).
|
||||
set_image_limits(&mut term, config.image_storage_limit);
|
||||
|
||||
self.config = config;
|
||||
}
|
||||
@@ -562,7 +563,7 @@ impl Termio {
|
||||
// Enter the critical area that we want to keep small
|
||||
{
|
||||
let state = Arc::clone(&self.renderer_state);
|
||||
let mut term = state.lock();
|
||||
let mut term = state.lock_terminal();
|
||||
self.output().inner().stream.state.size = size;
|
||||
|
||||
// Update the size of our terminal state
|
||||
@@ -594,7 +595,7 @@ impl Termio {
|
||||
/// Make a size report.
|
||||
pub fn size_report(&mut self, td: &mut ThreadData, style: SizeReport) {
|
||||
let state = Arc::clone(&self.renderer_state);
|
||||
let _term = state.lock();
|
||||
let _term = state.lock_terminal();
|
||||
self.size_report_locked(td, style);
|
||||
}
|
||||
|
||||
@@ -625,7 +626,7 @@ impl Termio {
|
||||
/// Reset the synchronized output mode. This is usually called by timer
|
||||
/// expiration from the termio thread.
|
||||
pub fn reset_synchronized_output(&mut self) {
|
||||
let mut term = self.renderer_state.lock();
|
||||
let mut term = self.renderer_state.lock_terminal();
|
||||
term.modes.set(Mode::SynchronizedOutput, false);
|
||||
if let Err(err) = self.renderer_wakeup.notify() {
|
||||
log::warn!("failed to notify renderer err={err}");
|
||||
@@ -635,7 +636,7 @@ impl Termio {
|
||||
/// Clear the screen.
|
||||
pub fn clear_screen(&mut self, td: &mut ThreadData, history: bool) {
|
||||
{
|
||||
let mut term = self.renderer_state.lock();
|
||||
let mut term = self.renderer_state.lock_terminal();
|
||||
|
||||
// If we're on the alternate screen, we do not clear. Since this is an
|
||||
// emulator-level screen clear, this messes up the running programs
|
||||
@@ -672,9 +673,11 @@ impl Termio {
|
||||
// graphics that are placed above the cursor or if it deletes
|
||||
// all of them. We delete all of them for now but if this behavior
|
||||
// isn't fully correct we should fix this later.
|
||||
// TODO(integration): `kitty_images.delete(.{ .all = true })`
|
||||
// once kitty image storage is ported; only mark dirty for now.
|
||||
term.screen_mut().kitty_images.dirty = true;
|
||||
let info = TerminalInfo::from_terminal(&term);
|
||||
let screen = term.screen_mut();
|
||||
screen
|
||||
.kitty_images
|
||||
.delete(&mut screen.pages, &info, KittyDelete::All(true));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -690,13 +693,13 @@ impl Termio {
|
||||
|
||||
/// Scroll the viewport
|
||||
pub fn scroll_viewport(&mut self, scroll: ScrollViewport) {
|
||||
self.renderer_state.lock().scroll_viewport(scroll);
|
||||
self.renderer_state.lock_terminal().scroll_viewport(scroll);
|
||||
}
|
||||
|
||||
/// Jump the viewport to the prompt.
|
||||
pub fn jump_to_prompt(&mut self, delta: isize) {
|
||||
{
|
||||
let mut term = self.renderer_state.lock();
|
||||
let mut term = self.renderer_state.lock_terminal();
|
||||
term.screen_mut().scroll(Scroll::DeltaPrompt(delta));
|
||||
}
|
||||
|
||||
@@ -707,7 +710,11 @@ impl Termio {
|
||||
|
||||
/// Called when focus is gained or lost (when focus events are enabled)
|
||||
pub fn focus_gained(&mut self, td: &mut ThreadData, focused: bool) {
|
||||
let focus_event = self.renderer_state.lock().modes.get(Mode::FocusEvent);
|
||||
let focus_event = self
|
||||
.renderer_state
|
||||
.lock_terminal()
|
||||
.modes
|
||||
.get(Mode::FocusEvent);
|
||||
|
||||
// If we have focus events enabled, we send the focus event.
|
||||
if focus_event {
|
||||
@@ -727,7 +734,7 @@ impl Termio {
|
||||
/// Sends a DSR response for the current color scheme to the pty.
|
||||
pub fn color_scheme_report(&mut self, td: &mut ThreadData, force: bool) {
|
||||
let state = Arc::clone(&self.renderer_state);
|
||||
let term = state.lock();
|
||||
let term = state.lock_terminal();
|
||||
self.color_scheme_report_locked(&term, td, force);
|
||||
}
|
||||
|
||||
@@ -747,3 +754,14 @@ impl Termio {
|
||||
self.queue_write(td, output, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply `image-storage-limit` to every initialized screen.
|
||||
fn set_image_limits(term: &mut Terminal, limit: usize) {
|
||||
for key in [ScreenKey::Primary, ScreenKey::Alternate] {
|
||||
if let Some(screen) = term.screens.get_mut(key)
|
||||
&& let Err(err) = screen.kitty_images.set_limit(&mut screen.pages, limit)
|
||||
{
|
||||
log::warn!("failed to set kitty image storage limit: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ use rustix::event::{PollFd, PollFlags};
|
||||
use crate::backend::ThreadData as BackendThreadData;
|
||||
use crate::exec;
|
||||
use crate::message::Message;
|
||||
use crate::state::SharedStateExt;
|
||||
use crate::termio::{Error, Termio, ThreadData};
|
||||
use crate::wakeup::Wakeup;
|
||||
|
||||
@@ -376,7 +377,7 @@ impl Thread {
|
||||
/// in some way but this works for now. Without this, the user would
|
||||
/// just see a blank terminal window.
|
||||
fn report_start_error(io: &Termio, err: &Error) {
|
||||
let mut t = io.renderer_state().lock();
|
||||
let mut t = io.renderer_state().lock_terminal();
|
||||
|
||||
// Hide the cursor
|
||||
t.modes.set(Mode::CursorVisible, false);
|
||||
|
||||
@@ -12,17 +12,35 @@ use std::sync::Arc;
|
||||
use rustix::event::{EventfdFlags, eventfd};
|
||||
|
||||
/// See the module docs. Cloning shares the same underlying eventfd.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Wakeup(Arc<OwnedFd>);
|
||||
#[derive(Clone)]
|
||||
pub struct Wakeup(Arc<OwnedFd>, Option<Arc<dyn Fn() + Send + Sync>>);
|
||||
|
||||
impl std::fmt::Debug for Wakeup {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Wakeup").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Wakeup {
|
||||
pub fn new() -> io::Result<Wakeup> {
|
||||
let fd = eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK)?;
|
||||
Ok(Wakeup(Arc::new(fd)))
|
||||
Ok(Wakeup(Arc::new(fd), None))
|
||||
}
|
||||
|
||||
/// A wakeup that calls `hook` instead of signalling an eventfd. Used to
|
||||
/// bridge into waiters that are not `poll(2)`-based (the renderer
|
||||
/// thread's channel). `as_fd`/`drain` are meaningless for these.
|
||||
pub fn with_hook(hook: Arc<dyn Fn() + Send + Sync>) -> io::Result<Wakeup> {
|
||||
let fd = eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK)?;
|
||||
Ok(Wakeup(Arc::new(fd), Some(hook)))
|
||||
}
|
||||
|
||||
/// Wake up the waiter. Multiple notifications coalesce.
|
||||
pub fn notify(&self) -> io::Result<()> {
|
||||
if let Some(hook) = &self.1 {
|
||||
hook();
|
||||
return Ok(());
|
||||
}
|
||||
loop {
|
||||
match rustix::io::write(&*self.0, &1u64.to_ne_bytes()) {
|
||||
Ok(_) => return Ok(()),
|
||||
|
||||
@@ -18,12 +18,13 @@ use ghostty_config::types_b::{LinuxCgroup, ShellIntegration};
|
||||
use ghostty_datastruct::blocking_queue::Timeout;
|
||||
use ghostty_renderer::message::Message as RendererMessage;
|
||||
use ghostty_renderer::size::{CellSize, Padding, ScreenSize, Size};
|
||||
use ghostty_termio::SharedStateExt;
|
||||
use ghostty_termio::backend::{self, Backend};
|
||||
use ghostty_termio::exec;
|
||||
use ghostty_termio::message::Message;
|
||||
use ghostty_termio::termio::DerivedConfig;
|
||||
use ghostty_termio::{
|
||||
Mailbox, Options, RendererMailbox, RendererState, SurfaceMailbox, Termio, Thread, Wakeup,
|
||||
Mailbox, Options, RendererMailbox, SharedState, SurfaceMailbox, Termio, Thread, Wakeup,
|
||||
};
|
||||
|
||||
struct SurfaceRec(Sender<SurfaceMessage>);
|
||||
@@ -69,7 +70,7 @@ fn size(cols: u32, rows: u32) -> Size {
|
||||
}
|
||||
|
||||
struct Fixture {
|
||||
state: Arc<RendererState>,
|
||||
state: SharedState,
|
||||
mailbox: Mailbox,
|
||||
stop: Wakeup,
|
||||
thread: Option<JoinHandle<()>>,
|
||||
@@ -136,7 +137,7 @@ fn sh(script: &str) -> ConfigCommand {
|
||||
|
||||
impl Fixture {
|
||||
fn text(&self) -> String {
|
||||
self.state.lock().plain_string().unwrap()
|
||||
self.state.lock_terminal().plain_string().unwrap()
|
||||
}
|
||||
|
||||
fn send(&self, msg: Message) {
|
||||
@@ -218,7 +219,7 @@ fn printf_output_reaches_terminal_and_exit_is_reported() {
|
||||
|
||||
// The SGR was applied: the cursor pen is red (palette 1).
|
||||
{
|
||||
let t = f.state.lock();
|
||||
let t = f.state.lock_terminal();
|
||||
let style = t.screen().cursor.style;
|
||||
assert!(
|
||||
!matches!(style.fg_color, ghostty_terminal::style::Color::None),
|
||||
@@ -308,7 +309,7 @@ fn linefeed_mode_translates_cr() {
|
||||
f.wait_text("a^M");
|
||||
// cat -v got "a\r\n", so it printed a newline after the ^M.
|
||||
f.wait_for("cursor on second row", |f| {
|
||||
(f.state.lock().screen().cursor.y == 1).then_some(())
|
||||
(f.state.lock_terminal().screen().cursor.y == 1).then_some(())
|
||||
});
|
||||
f.stop();
|
||||
}
|
||||
@@ -346,7 +347,7 @@ fn missing_input_file_shows_error_and_thread_keeps_draining() {
|
||||
f.wait_text("A configured `input` path was not found");
|
||||
assert!(
|
||||
!f.state
|
||||
.lock()
|
||||
.lock_terminal()
|
||||
.modes
|
||||
.get(ghostty_vt::modes::Mode::CursorVisible)
|
||||
);
|
||||
@@ -366,7 +367,7 @@ fn resize_messages_are_coalesced_and_applied_to_the_pty() {
|
||||
f.send(Message::Resize(size(100, 40)));
|
||||
f.wait_text("40 100");
|
||||
{
|
||||
let t = f.state.lock();
|
||||
let t = f.state.lock_terminal();
|
||||
assert_eq!((t.cols, t.rows), (100, 40));
|
||||
assert_eq!(t.width_px, 1000);
|
||||
assert_eq!(t.height_px, 800);
|
||||
@@ -385,7 +386,7 @@ fn synchronized_output_is_force_reset_by_timer() {
|
||||
let mut f = start(sh("printf '\\033[?2026h'; sleep 5"), |_| {});
|
||||
f.wait_for("sync output on", |f| {
|
||||
f.state
|
||||
.lock()
|
||||
.lock_terminal()
|
||||
.modes
|
||||
.get(ghostty_vt::modes::Mode::SynchronizedOutput)
|
||||
.then_some(())
|
||||
@@ -393,7 +394,7 @@ fn synchronized_output_is_force_reset_by_timer() {
|
||||
let started = Instant::now();
|
||||
f.wait_for("sync output reset", |f| {
|
||||
(!f.state
|
||||
.lock()
|
||||
.lock_terminal()
|
||||
.modes
|
||||
.get(ghostty_vt::modes::Mode::SynchronizedOutput))
|
||||
.then_some(())
|
||||
@@ -462,7 +463,7 @@ fn focus_events_are_sent_when_enabled() {
|
||||
);
|
||||
f.wait_for("focus mode", |f| {
|
||||
f.state
|
||||
.lock()
|
||||
.lock_terminal()
|
||||
.modes
|
||||
.get(ghostty_vt::modes::Mode::FocusEvent)
|
||||
.then_some(())
|
||||
@@ -529,7 +530,7 @@ fn writes_after_exit_are_ignored_and_thread_stays_alive_until_stopped() {
|
||||
f.send(Message::Focused(true));
|
||||
f.send(Message::Resize(size(90, 25)));
|
||||
f.wait_for("resize after exit", |f| {
|
||||
(f.state.lock().cols == 90).then_some(())
|
||||
(f.state.lock_terminal().cols == 90).then_some(())
|
||||
});
|
||||
f.stop();
|
||||
}
|
||||
@@ -542,11 +543,11 @@ fn change_config_message_updates_palette_and_colors() {
|
||||
cfg.enquiry_response = "x".into();
|
||||
f.send(Message::ChangeConfig(Box::new(cfg)));
|
||||
f.wait_for("background change", |f| {
|
||||
let t = f.state.lock();
|
||||
let t = f.state.lock_terminal();
|
||||
(t.colors.background.default == Some(ghostty_vt::color::Rgb { r: 1, g: 2, b: 3 }))
|
||||
.then_some(())
|
||||
});
|
||||
assert!(f.state.lock().flags.dirty.palette);
|
||||
assert!(f.state.lock_terminal().flags.dirty.palette);
|
||||
f.stop();
|
||||
}
|
||||
|
||||
@@ -571,7 +572,11 @@ fn process_output_manual_api() {
|
||||
termio.process_output(b"manual\x1b[31m!");
|
||||
assert_eq!(
|
||||
"manual!",
|
||||
termio.renderer_state().lock().plain_string().unwrap()
|
||||
termio
|
||||
.renderer_state()
|
||||
.lock_terminal()
|
||||
.plain_string()
|
||||
.unwrap()
|
||||
);
|
||||
termio.process_output(b"\x1b[5n");
|
||||
let m = termio.mailbox().pop().unwrap();
|
||||
|
||||
Reference in new issue
Block a user