From ead8d68c69914c2d391165da43ffd8bb0e35a9a5 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 14 Mar 2020 15:09:10 +0000 Subject: [PATCH] Fix live config reload for window title This enables live config reload for the window title. This includes updating the title after it has been pushed and popped from the title stack. The dynamic title option also isn't disabled automatically anymore when the title is set in the config. If the title is set from CLI, the behavior is unchanged and dynamic title changes are still disabled. If the dynamic title is disabled in the config, the title is still updated when the config title is changed. Dynamic title now only prevents changes to the UI's title. --- CHANGELOG.md | 5 + alacritty/src/cli.rs | 7 +- alacritty_terminal/src/ansi.rs | 8 +- alacritty_terminal/src/term/mod.rs | 150 ++++++++++++++--------------- 4 files changed, 86 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd4c2a0..7574f21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Capitalized the default Alacritty.desktop file - Capitalized the Alacritty.desktop file +### Added + +- Live config reload for `window.title` + ### Changed - Pressing additional modifiers for mouse bindings will no longer trigger them @@ -22,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The default value for `draw_bold_text_with_bright_colors` is now `false` - Mirror OSC query terminators instead of always using BEL - Increased Beam, Underline, and Hollow Block cursors' line widths +- Dynamic title is not disabled anymore when `window.title` is set in config ### Fixed diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 30f0938..2137c9c 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -272,6 +272,7 @@ impl Options { config.hold = self.hold; + config.set_dynamic_title(config.dynamic_title() && self.title.is_none()); config.window.dimensions = self.dimensions.unwrap_or(config.window.dimensions); config.window.title = self.title.unwrap_or(config.window.title); config.window.position = self.position.or(config.window.position); @@ -285,8 +286,6 @@ impl Options { } } - config.set_dynamic_title(config.dynamic_title() && config.window.title == DEFAULT_NAME); - config.debug.print_events = self.print_events || config.debug.print_events; config.debug.log_level = max(config.debug.log_level, self.log_level); config.debug.ref_test = self.ref_test || config.debug.ref_test; @@ -328,12 +327,12 @@ mod tests { } #[test] - fn dynamic_title_overridden_by_config() { + fn dynamic_title_not_overridden_by_config() { let mut config = Config::default(); config.window.title = "foo".to_owned(); let config = Options::default().into_config(config); - assert!(!config.dynamic_title()); + assert!(config.dynamic_title()); } } diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 1c1c7cd..a5f8564 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -154,7 +154,7 @@ pub trait TermInfo { /// writing specific handler impls for tests far easier. pub trait Handler { /// OSC to set window title - fn set_title(&mut self, _: &str) {} + fn set_title(&mut self, _: Option) {} /// Set the cursor style fn set_cursor_style(&mut self, _: Option) {} @@ -771,8 +771,10 @@ where .iter() .flat_map(|x| str::from_utf8(x)) .collect::>() - .join(";"); - self.handler.set_title(&title); + .join(";") + .trim() + .to_owned(); + self.handler.set_title(Some(title)); return; } unhandled(params); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 660aeb7..ac5e56b 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -26,7 +26,7 @@ use crate::ansi::{ self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, TermInfo, }; use crate::clipboard::{Clipboard, ClipboardType}; -use crate::config::{Config, VisualBellAnimation, DEFAULT_NAME}; +use crate::config::{Config, VisualBellAnimation}; use crate::event::{Event, EventListener}; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, @@ -35,8 +35,6 @@ use crate::index::{self, Column, IndexRange, Line, Point}; use crate::selection::{Selection, SelectionRange}; use crate::term::cell::{Cell, Flags, LineLength}; use crate::term::color::Rgb; -#[cfg(windows)] -use crate::tty; pub mod cell; pub mod color; @@ -797,21 +795,24 @@ pub struct Term { /// Default style for resetting the cursor default_cursor_style: CursorStyle, - /// Whether to permit updating the terminal title - dynamic_title: bool, - /// Clipboard access coupled to the active window clipboard: Clipboard, /// Proxy for sending events to the event loop event_proxy: T, - /// Current title of the window - title: String, + /// Current title of the window. + title: Option, + + /// Default title for resetting it. + default_title: String, + + /// Whether to permit updating the terminal title. + dynamic_title: bool, /// Stack of saved window titles. When a title is popped from this stack, the `title` for the /// term is set, and the Glutin window's title attribute is changed through the event listener. - title_stack: Vec, + title_stack: Vec>, } /// Terminal size info @@ -934,12 +935,16 @@ impl Term { clipboard, event_proxy, is_focused: true, - title: config.window.title.clone(), + title: None, + default_title: config.window.title.clone(), title_stack: Vec::new(), } } - pub fn update_config(&mut self, config: &Config) { + pub fn update_config(&mut self, config: &Config) + where + T: EventListener, + { self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned(); self.original_colors.fill_named(&config.colors); self.original_colors.fill_cube(&config.colors); @@ -954,8 +959,16 @@ impl Term { self.mode.remove(TermMode::ALTERNATE_SCROLL); } self.default_cursor_style = config.cursor.style; + + self.default_title = config.window.title.clone(); self.dynamic_title = config.dynamic_title(); + if self.dynamic_title { + self.set_title(self.title.clone()); + } else { + self.event_proxy.send_event(Event::Title(self.default_title.clone())); + } + if self.alt { self.alt_grid.update_history(config.scrolling.history() as usize); } else { @@ -1299,40 +1312,6 @@ impl TermInfo for Term { } impl Handler for Term { - #[inline] - #[cfg(not(windows))] - fn set_title(&mut self, title: &str) { - if self.dynamic_title { - trace!("Setting window title to '{}'", title); - - self.title = title.into(); - self.event_proxy.send_event(Event::Title(title.to_owned())); - } - } - - #[inline] - #[cfg(windows)] - fn set_title(&mut self, title: &str) { - if self.dynamic_title { - // cmd.exe in winpty: winpty incorrectly sets the title to ' ' instead of - // 'Alacritty' - thus we have to substitute this back to get equivalent - // behaviour as conpty. - // - // The starts_with check is necessary because other shells e.g. bash set a - // different title and don't need Alacritty prepended. - trace!("Setting window title to '{}'", title); - - let title = if !tty::is_conpty() && title.starts_with(' ') { - format!("Alacritty {}", title.trim()) - } else { - title.to_owned() - }; - - self.title = title.clone(); - self.event_proxy.send_event(Event::Title(title)); - } - } - /// A character to be displayed #[inline] fn input(&mut self, c: char) { @@ -1916,8 +1895,8 @@ impl Handler for Term { self.grid.reset(&Cell::default()); self.alt_grid.reset(&Cell::default()); self.scroll_region = Line(0)..self.grid.num_lines(); - self.title = DEFAULT_NAME.to_string(); - self.title_stack.clear(); + self.title_stack = Vec::new(); + self.title = None; } #[inline] @@ -2109,14 +2088,26 @@ impl Handler for Term { self.cursor_style = style; } + #[inline] + fn set_title(&mut self, title: Option) { + trace!("Setting title to '{:?}'", title); + + self.title = title.clone(); + + if self.dynamic_title { + let title = title.unwrap_or_else(|| self.default_title.clone()); + self.event_proxy.send_event(Event::Title(title)); + } + } + #[inline] fn push_title(&mut self) { - trace!("Pushing '{}' onto title stack", self.title); + trace!("Pushing '{:?}' onto title stack", self.title); if self.title_stack.len() >= TITLE_STACK_MAX_DEPTH { let removed = self.title_stack.remove(0); trace!( - "Removing '{}' from bottom of title stack that exceeds its maximum depth", + "Removing '{:?}' from bottom of title stack that exceeds its maximum depth", removed ); } @@ -2129,8 +2120,8 @@ impl Handler for Term { trace!("Attempting to pop title from stack..."); if let Some(popped) = self.title_stack.pop() { - trace!("Title '{}' popped from stack", popped); - self.set_title(&popped); + trace!("Title '{:?}' popped from stack", popped); + self.set_title(popped); } } } @@ -2377,42 +2368,47 @@ mod tests { }; let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); + // Title None by default + assert_eq!(term.title, None); + // Title can be set - { - term.title = "Test".to_string(); - assert_eq!(term.title, "Test"); - } + term.set_title(Some("Test".into())); + assert_eq!(term.title, Some("Test".into())); // Title can be pushed onto stack - { - term.push_title(); - term.title = "Next".to_string(); - assert_eq!(term.title, "Next"); - assert_eq!(term.title_stack.get(0).unwrap(), "Test"); - } + term.push_title(); + term.set_title(Some("Next".into())); + assert_eq!(term.title, Some("Next".into())); + assert_eq!(term.title_stack.get(0).unwrap(), &Some("Test".into())); // Title can be popped from stack and set as the window title - { - term.pop_title(); - assert_eq!(term.title, "Test"); - assert!(term.title_stack.is_empty()); - } + term.pop_title(); + assert_eq!(term.title, Some("Test".into())); + assert!(term.title_stack.is_empty()); // Title stack doesn't grow infinitely - { - for _ in 0..4097 { - term.push_title(); - } - assert_eq!(term.title_stack.len(), 4096); + for _ in 0..4097 { + term.push_title(); } + assert_eq!(term.title_stack.len(), 4096); // Title and title stack reset when terminal state is reset - { - term.push_title(); - term.reset_state(); - assert_eq!(term.title, "Alacritty"); - assert!(term.title_stack.is_empty()); - } + term.push_title(); + term.reset_state(); + assert_eq!(term.title, None); + assert!(term.title_stack.is_empty()); + + // Title stack pops back to default + term.title = None; + term.push_title(); + term.set_title(Some("Test".into())); + term.pop_title(); + assert_eq!(term.title, None); + + // Title can be reset to default + term.title = Some("Test".into()); + term.set_title(None); + assert_eq!(term.title, None); } }