diff --git a/CHANGELOG.md b/CHANGELOG.md index cd4c2a0f..7574f210 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 30f0938c..2137c9c8 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 1c1c7cd5..a5f8564b 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 660aeb79..ac5e56b5 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); } }