diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-18 02:52:35 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-18 02:53:31 +0300 |
| commit | 36b4001f4526b60f1c9b67a4a34596e5ad2bcca1 (patch) | |
| tree | 520ab28a0a3f3f798e6e655fc83529e8617720e9 /src/pane | |
| parent | aca35ea76f3df15ec68fd07959d939f5e7a78a6c (diff) | |
feat: sync theme with host appearance
refs #675
Diffstat (limited to 'src/pane')
| -rw-r--r-- | src/pane/osc.rs | 38 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 88 |
2 files changed, 111 insertions, 15 deletions
diff --git a/src/pane/osc.rs b/src/pane/osc.rs index e7b97a79..83cb47ae 100644 --- a/src/pane/osc.rs +++ b/src/pane/osc.rs @@ -885,22 +885,44 @@ pub(super) fn write_host_terminal_theme( terminal: &mut crate::ghostty::Terminal, theme: crate::terminal_theme::TerminalTheme, ) { - if let Some(color) = theme.foreground { - let sequence = crate::terminal_theme::osc_set_default_color_sequence( + write_host_terminal_theme_selective(terminal, theme, true, true); +} + +pub(super) fn write_host_terminal_theme_selective( + terminal: &mut crate::ghostty::Terminal, + theme: crate::terminal_theme::TerminalTheme, + foreground: bool, + background: bool, +) { + if foreground { + write_host_default_color( + terminal, crate::terminal_theme::DefaultColorKind::Foreground, - color, + theme.foreground, ); - terminal.write(sequence.as_bytes()); } - if let Some(color) = theme.background { - let sequence = crate::terminal_theme::osc_set_default_color_sequence( + if background { + write_host_default_color( + terminal, crate::terminal_theme::DefaultColorKind::Background, - color, + theme.background, ); - terminal.write(sequence.as_bytes()); } } +fn write_host_default_color( + terminal: &mut crate::ghostty::Terminal, + kind: crate::terminal_theme::DefaultColorKind, + color: Option<crate::terminal_theme::RgbColor>, +) { + let sequence = if let Some(color) = color { + crate::terminal_theme::osc_set_default_color_sequence(kind, color) + } else { + crate::terminal_theme::osc_reset_default_color_sequence(kind).to_string() + }; + terminal.write(sequence.as_bytes()); +} + pub(super) fn restore_host_terminal_theme_if_needed( core: &mut GhosttyPaneCore, pane_id: PaneId, diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index eca56975..a87b9622 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -24,8 +24,8 @@ use super::{ osc::{ contains_scrollback_clear_sequence, current_transient_default_color_owner, maybe_filter_primary_screen_scrollback_clear, restore_host_terminal_theme_if_needed, - write_host_terminal_theme, AgentOscStateTracker, CwdOscTracker, DefaultColorEvent, - DefaultColorEventTracker, DefaultColorOscTracker, DefaultColorQuery, + write_host_terminal_theme_selective, AgentOscStateTracker, CwdOscTracker, + DefaultColorEvent, DefaultColorEventTracker, DefaultColorOscTracker, DefaultColorQuery, DefaultColorTrackedEvent, Osc52Forwarder, OscDebugTracker, }, xtgettcap::{XtgettcapQueryTracker, XtgettcapResponse}, @@ -399,11 +399,18 @@ impl GhosttyPaneTerminal { pub fn apply_host_terminal_theme(&self, theme: crate::terminal_theme::TerminalTheme) { if let Ok(mut core) = self.core.lock() { + let foreground_unowned = !core.child_default_foreground_changed; + let background_unowned = !core.child_default_background_changed; core.host_terminal_theme = theme; - core.transient_default_color_owner_pgid = None; - core.child_default_foreground_changed = false; - core.child_default_background_changed = false; - write_host_terminal_theme(&mut core.terminal, theme); + if foreground_unowned && background_unowned { + core.transient_default_color_owner_pgid = None; + } + write_host_terminal_theme_selective( + &mut core.terminal, + theme, + foreground_unowned, + background_unowned, + ); } } @@ -646,6 +653,10 @@ impl GhosttyPaneTerminal { core.terminal.write(&bytes[written..]); terminal_responses.extend(self.drain_pending_pty_responses()); } + + if !core.child_default_foreground_changed && !core.child_default_background_changed { + core.transient_default_color_owner_pgid = None; + } } fn drain_pending_pty_responses(&self) -> Vec<Bytes> { @@ -1929,7 +1940,10 @@ fn respond_to_default_color_event( } } DefaultColorEvent::Set(query) => mark_child_default_color_changed(core, query, true), - DefaultColorEvent::Reset(query) => mark_child_default_color_changed(core, query, false), + DefaultColorEvent::Reset(query) => { + mark_child_default_color_changed(core, query, false); + apply_cached_host_default_color(core, query); + } } } @@ -1975,6 +1989,15 @@ fn osc_rgb_response(command: &str, r: u8, g: u8, b: u8) -> Bytes { Bytes::from(format!("\x1b]{command};rgb:{r:04x}/{g:04x}/{b:04x}\x1b\\")) } +fn apply_cached_host_default_color(core: &mut GhosttyPaneCore, query: DefaultColorQuery) { + write_host_terminal_theme_selective( + &mut core.terminal, + core.host_terminal_theme, + matches!(query, DefaultColorQuery::Foreground), + matches!(query, DefaultColorQuery::Background), + ); +} + fn mark_child_default_color_changed( core: &mut GhosttyPaneCore, query: DefaultColorQuery, @@ -3386,6 +3409,57 @@ mod tests { } #[test] + fn host_theme_update_preserves_child_default_color_override() { + let (tx, mut rx) = mpsc::channel(4); + let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(); + let pane_id = PaneId::from_raw(1); + + let result = pane.process_pty_bytes(pane_id, 0, b"\x1b]11;#112233\x07", &tx); + assert!(result.terminal_responses.is_empty()); + + pane.apply_host_terminal_theme(crate::terminal_theme::TerminalTheme { + foreground: None, + background: Some(crate::terminal_theme::RgbColor { + r: 0xaa, + g: 0xbb, + b: 0xcc, + }), + }); + + let result = pane.process_pty_bytes(pane_id, 0, b"\x1b]11;?\x07", &tx); + assert!(result.terminal_responses.is_empty()); + assert!(rx.try_recv().is_err()); + } + + #[test] + fn child_default_color_reset_restores_cached_host_color() { + let (tx, mut rx) = mpsc::channel(4); + let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(); + let pane_id = PaneId::from_raw(1); + + pane.process_pty_bytes(pane_id, 0, b"\x1b]11;#112233\x07", &tx); + pane.apply_host_terminal_theme(crate::terminal_theme::TerminalTheme { + foreground: None, + background: Some(crate::terminal_theme::RgbColor { + r: 0xaa, + g: 0xbb, + b: 0xcc, + }), + }); + pane.process_pty_bytes(pane_id, 0, b"\x1b]111\x07", &tx); + assert!(!pane.has_transient_default_color_override()); + + let result = pane.process_pty_bytes(pane_id, 0, b"\x1b]11;?\x07", &tx); + assert_eq!( + result.terminal_responses, + vec![Bytes::from_static(b"\x1b]11;rgb:aaaa/bbbb/cccc\x1b\\")] + ); + assert!(rx.try_recv().is_err()); + } + + #[test] fn process_pty_bytes_recovers_xtgettcap_after_osc_bel_terminator() { let (tx, mut rx) = mpsc::channel(4); let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap(); |
