aboutsummaryrefslogtreecommitdiff
path: root/src/pane/terminal.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-22 19:37:34 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-22 19:37:34 +0300
commit1b9a165f9e9fe9e8082d38e15942cfe0962344e2 (patch)
tree4467b1a94ddd2fe9b4b2f59a75a4a0ca7fbef26e /src/pane/terminal.rs
parentc25f116c5365b2a9dcb621432a7f96b3f5160d43 (diff)
fix: answer pane default color queries
refs #253
Diffstat (limited to 'src/pane/terminal.rs')
-rw-r--r--src/pane/terminal.rs149
1 files changed, 147 insertions, 2 deletions
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index 6e29774a..e1a57e89 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -20,7 +20,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, DefaultColorOscTracker, Osc52Forwarder,
+ write_host_terminal_theme, DefaultColorEvent, DefaultColorEventTracker,
+ DefaultColorOscTracker, DefaultColorQuery, Osc52Forwarder,
},
};
@@ -93,6 +94,9 @@ pub(crate) struct GhosttyPaneCore {
pub host_terminal_theme: crate::terminal_theme::TerminalTheme,
pub transient_default_color_owner_pgid: Option<u32>,
pub default_color_tracker: DefaultColorOscTracker,
+ pub default_color_event_tracker: DefaultColorEventTracker,
+ pub child_default_foreground_changed: bool,
+ pub child_default_background_changed: bool,
pub osc52_forwarder: Osc52Forwarder,
}
@@ -282,6 +286,9 @@ impl GhosttyPaneTerminal {
host_terminal_theme: crate::terminal_theme::TerminalTheme::default(),
transient_default_color_owner_pgid: None,
default_color_tracker: DefaultColorOscTracker::default(),
+ default_color_event_tracker: DefaultColorEventTracker::default(),
+ child_default_foreground_changed: false,
+ child_default_background_changed: false,
osc52_forwarder: Osc52Forwarder::default(),
}),
key_encoder: Mutex::new(key_encoder),
@@ -292,6 +299,8 @@ impl GhosttyPaneTerminal {
if let Ok(mut core) = self.core.lock() {
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);
}
}
@@ -387,6 +396,10 @@ impl GhosttyPaneTerminal {
}
core.terminal.write(filtered_bytes.as_ref());
+ core.default_color_event_tracker
+ .observe(filtered_bytes.as_ref());
+ respond_to_default_color_events(&mut core, response_writer);
+
let has_kitty_graphics_sequence = crate::kitty_graphics::is_enabled()
&& contains_kitty_graphics_sequence(filtered_bytes.as_ref());
if has_kitty_graphics_sequence {
@@ -399,7 +412,6 @@ impl GhosttyPaneTerminal {
.terminal
.mode_get(crate::ghostty::MODE_SYNCHRONIZED_OUTPUT)
.unwrap_or(false);
- let _ = response_writer;
ProcessBytesResult {
request_render: !synchronized_output && !has_kitty_graphics_sequence,
render_delay: (!synchronized_output && has_kitty_graphics_sequence)
@@ -1132,6 +1144,53 @@ fn ghostty_cell_style(
style.add_modifier(modifiers)
}
+fn respond_to_default_color_events(
+ core: &mut GhosttyPaneCore,
+ response_writer: &mpsc::Sender<Bytes>,
+) {
+ for event in core.default_color_event_tracker.drain_pending() {
+ match event {
+ DefaultColorEvent::Query(query) => {
+ if let Some(response) = default_color_query_response(query, core) {
+ let _ = response_writer.try_send(response);
+ }
+ }
+ DefaultColorEvent::Set(query) => mark_child_default_color_changed(core, query, true),
+ DefaultColorEvent::Reset(query) => mark_child_default_color_changed(core, query, false),
+ }
+ }
+}
+
+fn default_color_query_response(query: DefaultColorQuery, core: &GhosttyPaneCore) -> Option<Bytes> {
+ let color = match query {
+ DefaultColorQuery::Foreground if !core.child_default_foreground_changed => {
+ core.host_terminal_theme.foreground
+ }
+ DefaultColorQuery::Background if !core.child_default_background_changed => {
+ core.host_terminal_theme.background
+ }
+ _ => None,
+ }?;
+ let r = u16::from(color.r) * 257;
+ let g = u16::from(color.g) * 257;
+ let b = u16::from(color.b) * 257;
+ Some(Bytes::from(format!(
+ "\x1b]{};rgb:{r:04x}/{g:04x}/{b:04x}\x1b\\",
+ query.osc_number()
+ )))
+}
+
+fn mark_child_default_color_changed(
+ core: &mut GhosttyPaneCore,
+ query: DefaultColorQuery,
+ changed: bool,
+) {
+ match query {
+ DefaultColorQuery::Foreground => core.child_default_foreground_changed = changed,
+ DefaultColorQuery::Background => core.child_default_background_changed = changed,
+ }
+}
+
fn ghostty_default_fg(
color: crate::ghostty::RgbColor,
host_theme: crate::terminal_theme::TerminalTheme,
@@ -1962,6 +2021,92 @@ mod tests {
}
#[test]
+ fn process_pty_bytes_responds_to_default_color_queries() {
+ 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.apply_host_terminal_theme(crate::terminal_theme::TerminalTheme {
+ foreground: Some(crate::terminal_theme::RgbColor {
+ r: 0x65,
+ g: 0x7b,
+ b: 0x83,
+ }),
+ background: Some(crate::terminal_theme::RgbColor {
+ r: 0xfd,
+ g: 0xf6,
+ b: 0xe3,
+ }),
+ });
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1b]10;?\x07\x1b]11;?\x07", &tx);
+
+ assert_eq!(
+ rx.try_recv().unwrap().as_ref(),
+ b"\x1b]10;rgb:6565/7b7b/8383\x1b\\"
+ );
+ assert_eq!(
+ rx.try_recv().unwrap().as_ref(),
+ b"\x1b]11;rgb:fdfd/f6f6/e3e3\x1b\\"
+ );
+ assert!(rx.try_recv().is_err());
+ }
+
+ #[test]
+ fn process_pty_bytes_responds_to_split_default_color_query() {
+ 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.apply_host_terminal_theme(crate::terminal_theme::TerminalTheme {
+ foreground: None,
+ background: Some(crate::terminal_theme::RgbColor {
+ r: 0xfd,
+ g: 0xf6,
+ b: 0xe3,
+ }),
+ });
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1b]11", &tx);
+ assert!(rx.try_recv().is_err());
+ pane.process_pty_bytes(pane_id, 0, b";?\x1b", &tx);
+ assert!(rx.try_recv().is_err());
+ pane.process_pty_bytes(pane_id, 0, b"\\", &tx);
+
+ assert_eq!(
+ rx.try_recv().unwrap().as_ref(),
+ b"\x1b]11;rgb:fdfd/f6f6/e3e3\x1b\\"
+ );
+ assert!(rx.try_recv().is_err());
+ }
+
+ #[test]
+ fn process_pty_bytes_stops_replying_after_child_default_color_set() {
+ 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.apply_host_terminal_theme(crate::terminal_theme::TerminalTheme {
+ foreground: None,
+ background: Some(crate::terminal_theme::RgbColor {
+ r: 0xfd,
+ g: 0xf6,
+ b: 0xe3,
+ }),
+ });
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1b]11;rgb:11/22/33\x07\x1b]11;?\x07", &tx);
+ assert!(rx.try_recv().is_err());
+
+ pane.process_pty_bytes(pane_id, 0, b"\x1b]111\x07\x1b]11;?\x07", &tx);
+ assert_eq!(
+ rx.try_recv().unwrap().as_ref(),
+ b"\x1b]11;rgb:fdfd/f6f6/e3e3\x1b\\"
+ );
+ assert!(rx.try_recv().is_err());
+ }
+
+ #[test]
fn render_leaves_host_default_background_transparent() {
let (tx, _rx) = mpsc::channel(4);
let terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();