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
diff --git a/Cargo.lock b/Cargo.lock index d0fdf5616..c0fe3d280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index b5a2565ca..bf546efde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 ed03edfc6..c4e0bc3eb 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 30f67a3e6..a5850eddc 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. |