aboutsummaryrefslogtreecommitdiff
path: root/src/pane
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-08-03 02:05:27 +0300
committerGitHub <noreply@github.com>2026-08-03 02:05:27 +0300
commit10eb0bc744165ae91dfce9fd731fe170410b34ec (patch)
tree8622d429d9453c2e8d06630322804a702973d94a /src/pane
parentcbc31d34a1fb3da64415737de57ad10fe6af5ba5 (diff)
fix: propagate host color scheme to pane apps (#2214)
refs #714
Diffstat (limited to 'src/pane')
-rw-r--r--src/pane/terminal.rs84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index e464554c..438c3037 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -120,6 +120,8 @@ pub struct InputState {
pub mouse_alternate_scroll: bool,
#[serde(default)]
pub modify_other_keys: bool,
+ #[serde(default)]
+ pub color_scheme_reporting: bool,
}
impl InputState {
@@ -458,6 +460,13 @@ impl PaneTerminal {
self.ghostty.apply_host_terminal_theme(theme);
}
+ pub fn apply_host_terminal_appearance(
+ &self,
+ appearance: Option<crate::terminal_theme::HostAppearance>,
+ ) -> Option<Bytes> {
+ self.ghostty.apply_host_terminal_appearance(appearance)
+ }
+
pub fn has_transient_default_color_override(&self) -> bool {
self.ghostty.has_transient_default_color_override()
}
@@ -988,6 +997,32 @@ impl GhosttyPaneTerminal {
}
}
+ pub fn apply_host_terminal_appearance(
+ &self,
+ appearance: Option<crate::terminal_theme::HostAppearance>,
+ ) -> Option<Bytes> {
+ let mut core = self.core.lock().ok()?;
+ let color_scheme = appearance.map(|appearance| match appearance {
+ crate::terminal_theme::HostAppearance::Dark => crate::ghostty::ColorScheme::Dark,
+ crate::terminal_theme::HostAppearance::Light => crate::ghostty::ColorScheme::Light,
+ });
+ let previous = core.terminal.set_color_scheme(color_scheme);
+
+ let transitioned = matches!(
+ (previous, color_scheme),
+ (Some(previous), Some(current)) if previous != current
+ );
+ if !transitioned
+ || !core
+ .terminal
+ .mode_get(crate::ghostty::MODE_COLOR_SCHEME_REPORT)
+ .unwrap_or(false)
+ {
+ return None;
+ }
+ appearance.map(|appearance| Bytes::from_static(appearance.color_scheme_report()))
+ }
+
pub fn has_transient_default_color_override(&self) -> bool {
self.core
.lock()
@@ -1331,6 +1366,10 @@ impl GhosttyPaneTerminal {
crate::ghostty::MODE_MOUSE_ALTERNATE_SCROLL,
input_state.mouse_alternate_scroll,
);
+ let _ = core.terminal.mode_set(
+ crate::ghostty::MODE_COLOR_SCHEME_REPORT,
+ input_state.color_scheme_reporting,
+ );
for mode in [
MODE_MOUSE_X10,
@@ -1585,6 +1624,10 @@ impl GhosttyPaneTerminal {
.keyboard_state_ansi()
.ok()
.is_some_and(|ansi| !ansi.is_empty()),
+ color_scheme_reporting: core
+ .terminal
+ .mode_get(crate::ghostty::MODE_COLOR_SCHEME_REPORT)
+ .ok()?,
})
}
@@ -3907,6 +3950,7 @@ mod tests {
mouse_protocol_encoding: crate::input::MouseProtocolEncoding::Sgr,
mouse_alternate_scroll: true,
modify_other_keys: true,
+ color_scheme_reporting: true,
});
assert_eq!(
@@ -3920,6 +3964,7 @@ mod tests {
mouse_protocol_encoding: crate::input::MouseProtocolEncoding::Sgr,
mouse_alternate_scroll: true,
modify_other_keys: true,
+ color_scheme_reporting: true,
})
);
@@ -5010,6 +5055,45 @@ mod tests {
}
#[test]
+ fn color_scheme_queries_and_live_updates_follow_terminal_mode() {
+ 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);
+
+ assert!(pane
+ .apply_host_terminal_appearance(Some(crate::terminal_theme::HostAppearance::Dark))
+ .is_none());
+ let query = pane.process_pty_bytes(pane_id, 0, b"\x1b[?996n", &tx);
+ assert_eq!(
+ query.terminal_responses,
+ vec![Bytes::from_static(b"\x1b[?997;1n")]
+ );
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1b[?2031h", &tx);
+ assert!(pane
+ .apply_host_terminal_appearance(Some(crate::terminal_theme::HostAppearance::Dark))
+ .is_none());
+ assert_eq!(
+ pane.apply_host_terminal_appearance(Some(crate::terminal_theme::HostAppearance::Light)),
+ Some(Bytes::from_static(b"\x1b[?997;2n"))
+ );
+
+ assert!(pane.apply_host_terminal_appearance(None).is_none());
+ let unknown_query = pane.process_pty_bytes(pane_id, 0, b"\x1b[?996n", &tx);
+ assert!(unknown_query.terminal_responses.is_empty());
+ assert!(pane
+ .apply_host_terminal_appearance(Some(crate::terminal_theme::HostAppearance::Dark))
+ .is_none());
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1bc", &tx);
+ assert!(pane
+ .apply_host_terminal_appearance(Some(crate::terminal_theme::HostAppearance::Light))
+ .is_none());
+ assert!(rx.try_recv().is_err());
+ }
+
+ #[test]
fn process_pty_bytes_returns_xtgettcap_truecolor_query_responses_without_queuing_input() {
let (tx, mut rx) = mpsc::channel(8);
let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();