aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-21 17:37:41 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-21 17:37:41 +0300
commitb1c9833b828d12e7e0196c5607ae63372998e25a (patch)
treec8b1ff84cea1cf4367c5163e4721d028cf7856cf /src
parent8a4571198d013451743d1238f80a6bd69d67b32d (diff)
fix: stop headless idle spin and skip unnecessary macos probes
Diffstat (limited to 'src')
-rw-r--r--src/app/mod.rs29
-rw-r--r--src/app/runtime.rs19
-rw-r--r--src/pane/terminal.rs98
-rw-r--r--src/server/headless.rs2
4 files changed, 146 insertions, 2 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 51a6678e..a598100a 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -1148,6 +1148,35 @@ mod tests {
}
#[test]
+ fn headless_next_loop_deadline_ignores_resize_poll() {
+ let mut app = test_app();
+ let now = Instant::now();
+ app.next_resize_poll = now + Duration::from_millis(100);
+ app.session_save_deadline = Some(now + Duration::from_secs(2));
+ app.next_auto_update_check = Some(now + Duration::from_secs(6));
+
+ assert_eq!(
+ app.next_headless_loop_deadline(now, false),
+ app.session_save_deadline
+ );
+ }
+
+ #[test]
+ fn headless_next_loop_deadline_returns_none_when_resize_poll_is_only_deadline() {
+ let mut app = test_app();
+ let now = Instant::now();
+ app.next_resize_poll = now - Duration::from_millis(1);
+ app.config_diagnostic_deadline = None;
+ app.toast_deadline = None;
+ app.next_animation_tick = None;
+ app.next_auto_update_check = None;
+ app.session_save_deadline = None;
+ app.state.workspaces.clear();
+
+ assert_eq!(app.next_headless_loop_deadline(now, false), None);
+ }
+
+ #[test]
fn due_session_save_deadline_is_cleared() {
let mut app = test_app();
app.session_save_deadline = Some(Instant::now() - Duration::from_secs(1));
diff --git a/src/app/runtime.rs b/src/app/runtime.rs
index d82f6440..b6ad806b 100644
--- a/src/app/runtime.rs
+++ b/src/app/runtime.rs
@@ -227,6 +227,23 @@ impl App {
}
pub(crate) fn next_loop_deadline(&self, now: Instant, needs_render: bool) -> Option<Instant> {
+ self.next_loop_deadline_with_resize_poll(now, needs_render, true)
+ }
+
+ pub(crate) fn next_headless_loop_deadline(
+ &self,
+ now: Instant,
+ needs_render: bool,
+ ) -> Option<Instant> {
+ self.next_loop_deadline_with_resize_poll(now, needs_render, false)
+ }
+
+ fn next_loop_deadline_with_resize_poll(
+ &self,
+ now: Instant,
+ needs_render: bool,
+ include_resize_poll: bool,
+ ) -> Option<Instant> {
let render_deadline = if needs_render {
self.last_render_at
.map(|last_render_at| last_render_at + MIN_RENDER_INTERVAL)
@@ -236,7 +253,7 @@ impl App {
};
[
- Some(self.next_resize_poll),
+ include_resize_poll.then_some(self.next_resize_poll),
self.config_diagnostic_deadline,
self.toast_deadline,
self.next_animation_tick,
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index 796220b9..28b4186e 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -237,6 +237,15 @@ impl GhosttyPaneTerminal {
}
pub fn maybe_restore_host_terminal_theme(&self, pane_id: PaneId, shell_pid: u32) -> bool {
+ {
+ let Ok(core) = self.core.lock() else {
+ return false;
+ };
+ if !should_probe_host_terminal_theme_restore(&core) {
+ return false;
+ }
+ }
+
let foreground_job = crate::detect::foreground_job(shell_pid);
let Ok(mut core) = self.core.lock() else {
return false;
@@ -883,6 +892,18 @@ fn recent_text_from_rows(rows: &[String], lines: usize) -> String {
}
}
+fn should_probe_host_terminal_theme_restore(core: &GhosttyPaneCore) -> bool {
+ if core.transient_default_color_owner_pgid.is_none() || core.host_terminal_theme.is_empty() {
+ return false;
+ }
+
+ !core
+ .terminal
+ .active_screen()
+ .map(|screen| screen == crate::ghostty::ActiveScreen::Alternate)
+ .unwrap_or(false)
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -896,6 +917,83 @@ mod tests {
}
#[test]
+ fn host_terminal_theme_restore_probe_skips_when_no_transient_override() {
+ let (tx, _rx) = mpsc::channel(4);
+ let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
+ let core = pane.core.lock().unwrap();
+
+ assert!(!should_probe_host_terminal_theme_restore(&core));
+ }
+
+ #[test]
+ fn host_terminal_theme_restore_probe_skips_when_host_theme_unknown() {
+ let (tx, _rx) = mpsc::channel(4);
+ let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
+ {
+ let mut core = pane.core.lock().unwrap();
+ core.transient_default_color_owner_pgid = Some(42);
+ }
+ let core = pane.core.lock().unwrap();
+
+ assert!(!should_probe_host_terminal_theme_restore(&core));
+ }
+
+ #[test]
+ fn host_terminal_theme_restore_probe_skips_on_alternate_screen() {
+ let (tx, _rx) = mpsc::channel(4);
+ let mut terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
+ terminal.write(b"\x1b[?1049h");
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
+ {
+ let mut core = pane.core.lock().unwrap();
+ core.transient_default_color_owner_pgid = Some(42);
+ core.host_terminal_theme = crate::terminal_theme::TerminalTheme {
+ foreground: Some(crate::terminal_theme::RgbColor {
+ r: 0xaa,
+ g: 0xbb,
+ b: 0xcc,
+ }),
+ background: Some(crate::terminal_theme::RgbColor {
+ r: 0x11,
+ g: 0x22,
+ b: 0x33,
+ }),
+ };
+ }
+ let core = pane.core.lock().unwrap();
+
+ assert!(!should_probe_host_terminal_theme_restore(&core));
+ }
+
+ #[test]
+ fn host_terminal_theme_restore_probe_runs_when_restore_is_pending() {
+ let (tx, _rx) = mpsc::channel(4);
+ let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
+ {
+ let mut core = pane.core.lock().unwrap();
+ core.transient_default_color_owner_pgid = Some(42);
+ core.host_terminal_theme = crate::terminal_theme::TerminalTheme {
+ foreground: Some(crate::terminal_theme::RgbColor {
+ r: 0xaa,
+ g: 0xbb,
+ b: 0xcc,
+ }),
+ background: Some(crate::terminal_theme::RgbColor {
+ r: 0x11,
+ g: 0x22,
+ b: 0x33,
+ }),
+ };
+ }
+ let core = pane.core.lock().unwrap();
+
+ assert!(should_probe_host_terminal_theme_restore(&core));
+ }
+
+ #[test]
fn ghostty_render_can_suppress_cursor_position() {
let (tx, _rx) = mpsc::channel(4);
let mut first_terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
diff --git a/src/server/headless.rs b/src/server/headless.rs
index ba6098df..d120d21b 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -477,7 +477,7 @@ impl HeadlessServer {
}
// 8. Wait for next event.
- let next_deadline = self.app.next_loop_deadline(now, needs_render);
+ let next_deadline = self.app.next_headless_loop_deadline(now, needs_render);
let event = {
tokio::select! {
maybe_api = self.app.api_rx.recv() => match maybe_api {