Unnamed repository; edit this file 'description' to name the repository.
Restore original terminal background color (#15527)
We were previously emitting OSC111 to reset the terminal background
color when restoring. This breaks setups where you customize the
terminal background with OSC11 outside of Helix. We can use OSC111 when
there is no detected background color or when restoring during a panic,
but otherwise we should first query with `OSC 11 ; ? ST` and set that
color when restoring.
| -rw-r--r-- | Cargo.lock | 4 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | helix-term/src/application.rs | 2 | ||||
| -rw-r--r-- | helix-tui/src/backend/termina.rs | 148 |
4 files changed, 87 insertions, 69 deletions
@@ -2796,9 +2796,9 @@ dependencies = [ [[package]] name = "termina" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9747435fb6e4b6db463ce5a7ec39354e60b555a1083211d976421384beb5b8db" +checksum = "9a49b3d294d994319c4cd8c30f2172aab3217db673bb057912dc383677372830" dependencies = [ "bitflags", "futures-core", @@ -52,7 +52,7 @@ futures-executor = "0.3" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } tokio-stream = "0.1.18" toml = "1.1" -termina = "0.2" +termina = "0.3" sonic-rs = "0.5" globset = "0.4" etcetera = "0.11" diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ed03edfc..c4e0bc3e 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -751,6 +751,8 @@ impl Application { kind: crossterm::event::KeyEventKind::Release, .. }) => false, + #[cfg(not(windows))] + event if event.is_escape() => false, event => self.compositor.handle_event(&event.into(), &mut cx), }; diff --git a/helix-tui/src/backend/termina.rs b/helix-tui/src/backend/termina.rs index 30f67a3e..a5850edd 100644 --- a/helix-tui/src/backend/termina.rs +++ b/helix-tui/src/backend/termina.rs @@ -79,64 +79,26 @@ pub struct TerminaBackend { capabilities: Capabilities, reset_cursor_command: String, is_synchronized_output_set: bool, + /// The requested terminal background color, set in `Self::set_background_color`. background_color: Option<RgbColor>, + /// The terminal emulator's background color. This is queried when claiming the terminal so + /// that custom colors set outside of Helix with OSC11 are restored when Helix exits. + original_background_color: Option<RgbColor>, } impl TerminaBackend { pub fn new(config: Config) -> io::Result<Self> { - let mut terminal = PlatformTerminal::new()?; - let (capabilities, reset_cursor_command) = - Self::detect_capabilities(&mut terminal, &config)?; - - // In the case of a panic, reset the terminal eagerly. If we didn't do this and instead - // relied on `Drop`, the backtrace would be lost because it is printed before we would - // clear and exit the alternate screen. - let hook_reset_cursor_command = reset_cursor_command.clone(); - terminal.set_panic_hook(move |term| { - let _ = write!( - term, - "{}{}{}{}{}{}{}{}{}{}{}{}", - Csi::Keyboard(csi::Keyboard::PopFlags(1)), - decreset!(MouseTracking), - decreset!(ButtonEventMouse), - decreset!(AnyEventMouse), - decreset!(RXVTMouse), - decreset!(SGRMouse), - &hook_reset_cursor_command, - decreset!(BracketedPaste), - decreset!(FocusTracking), - Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor), - Csi::Edit(csi::Edit::EraseInDisplay(csi::EraseInDisplay::EraseDisplay)), - decreset!(ClearAndEnableAlternateScreen), - ); - }); - - Ok(Self { - terminal, - config, - capabilities, - reset_cursor_command, - is_synchronized_output_set: false, - background_color: None, - }) - } - - pub fn terminal(&self) -> &PlatformTerminal { - &self.terminal - } - - fn detect_capabilities( - terminal: &mut PlatformTerminal, - config: &Config, - ) -> io::Result<(Capabilities, String)> { use std::time::{Duration, Instant}; + let mut terminal = PlatformTerminal::new()?; + // Colibri "midnight" const TEST_COLOR: RgbColor = RgbColor::new(59, 34, 76); terminal.enter_raw_mode()?; let mut capabilities = Capabilities::default(); + let mut original_background_color = None; let start = Instant::now(); // HACK: emitting OSC11 / OSC111 seems to break SGR and cause flickering in tmux. @@ -158,7 +120,7 @@ impl TerminaBackend { // If we only receive the device attributes then we know it is not. write!( terminal, - "{}{}{}{}{}{}{}", + "{}{}{}{}{}{}{}{}", // Synchronized output Csi::Mode(csi::Mode::QueryDecPrivateMode(csi::DecPrivateMode::Code( csi::DecPrivateModeCode::SynchronizedOutput @@ -171,6 +133,10 @@ impl TerminaBackend { Csi::Sgr(csi::Sgr::UnderlineColor(TEST_COLOR.into())), Dcs::Request(dcs::DcsRequest::GraphicRendition), Csi::Sgr(csi::Sgr::Reset), + Osc::ChangeDynamicColors( + osc::DynamicColorNumber::TextBackgroundColor, + vec![osc::ColorOrQuery::Query] + ), // Finally request the primary device attributes Csi::Device(csi::Device::RequestPrimaryDeviceAttributes), )?; @@ -199,6 +165,14 @@ impl TerminaBackend { Event::Csi(Csi::Mode(csi::Mode::ReportTheme(mode))) => { capabilities.theme_mode = Some(mode.into()); } + Event::Osc(Osc::ChangeDynamicColors( + osc::DynamicColorNumber::TextBackgroundColor, + colors, + )) => { + if let Some(osc::ColorOrQuery::Color(color)) = colors.first() { + original_background_color = Some(*color); + } + } Event::Dcs(dcs::Dcs::Response { value: dcs::DcsResponse::GraphicRendition(sgrs), .. @@ -250,7 +224,42 @@ impl TerminaBackend { terminal.enter_cooked_mode()?; - Ok((capabilities, reset_cursor_command)) + // In the case of a panic, reset the terminal eagerly. If we didn't do this and instead + // relied on `Drop`, the backtrace would be lost because it is printed before we would + // clear and exit the alternate screen. + let hook_reset_cursor_command = reset_cursor_command.clone(); + terminal.set_panic_hook(move |term| { + let _ = write!( + term, + "{}{}{}{}{}{}{}{}{}{}{}{}", + Csi::Keyboard(csi::Keyboard::PopFlags(1)), + decreset!(MouseTracking), + decreset!(ButtonEventMouse), + decreset!(AnyEventMouse), + decreset!(RXVTMouse), + decreset!(SGRMouse), + &hook_reset_cursor_command, + decreset!(BracketedPaste), + decreset!(FocusTracking), + Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor), + Csi::Edit(csi::Edit::EraseInDisplay(csi::EraseInDisplay::EraseDisplay)), + decreset!(ClearAndEnableAlternateScreen), + ); + }); + + Ok(Self { + terminal, + config, + capabilities, + reset_cursor_command, + is_synchronized_output_set: false, + background_color: None, + original_background_color, + }) + } + + pub fn terminal(&self) -> &PlatformTerminal { + &self.terminal } fn enable_mouse_capture(&mut self) -> io::Result<()> { @@ -283,6 +292,20 @@ impl TerminaBackend { Ok(()) } + fn reset_background_color(&mut self) -> io::Result<()> { + write!( + self.terminal, + "{}", + match self.original_background_color { + Some(color) => Osc::ChangeDynamicColors( + osc::DynamicColorNumber::TextBackgroundColor, + vec![color.into()] + ), + None => Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor), + } + ) + } + fn enable_extensions(&mut self) -> io::Result<()> { const KEYBOARD_FLAGS: csi::KittyKeyboardFlags = csi::KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES @@ -440,11 +463,7 @@ impl Backend for TerminaBackend { decreset!(ClearAndEnableAlternateScreen), )?; if self.background_color.is_some() { - write!( - self.terminal, - "{}", - Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor) - )?; + self.reset_background_color()?; } self.terminal.flush()?; self.terminal.enter_cooked_mode()?; @@ -606,17 +625,18 @@ impl Backend for TerminaBackend { Some(Color::Rgb(r, g, b)) => Some(RgbColor::new(r, g, b)), _ => None, }; - write!( - self.terminal, - "{}", - match self.background_color { - Some(color) => Osc::ChangeDynamicColors( + if let Some(color) = self.background_color { + write!( + self.terminal, + "{}", + Osc::ChangeDynamicColors( osc::DynamicColorNumber::TextBackgroundColor, vec![color.into()] - ), - _ => Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor), - } - ) + ) + ) + } else { + self.reset_background_color() + } } } @@ -636,11 +656,7 @@ impl Drop for TerminaBackend { decreset!(ClearAndEnableAlternateScreen), ); if self.background_color.is_some() { - let _ = write!( - self.terminal, - "{}", - Osc::ResetDynamicColor(osc::DynamicColorNumber::TextBackgroundColor) - ); + let _ = self.reset_background_color(); } // NOTE: Drop for Platform terminal resets the mode and flushes the buffer when not // panicking. |