Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-tui/src/backend/termina.rs')
| -rw-r--r-- | helix-tui/src/backend/termina.rs | 148 |
1 files changed, 82 insertions, 66 deletions
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. |