aboutsummaryrefslogtreecommitdiff
path: root/src/pane
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-18 02:52:35 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-18 02:53:31 +0300
commit36b4001f4526b60f1c9b67a4a34596e5ad2bcca1 (patch)
tree520ab28a0a3f3f798e6e655fc83529e8617720e9 /src/pane
parentaca35ea76f3df15ec68fd07959d939f5e7a78a6c (diff)
feat: sync theme with host appearance
refs #675
Diffstat (limited to 'src/pane')
-rw-r--r--src/pane/osc.rs38
-rw-r--r--src/pane/terminal.rs88
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();