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.rs148
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.