diff --git a/Cargo.lock b/Cargo.lock index 090b5f027492..50c827c8d25c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1216,6 +1216,8 @@ dependencies = [ "crossterm", "helix-core", "helix-view", + "log", + "once_cell", "serde", "termini", "unicode-segmentation", diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index df6d9da6cd32..2487a02f831d 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -30,22 +30,14 @@ use crate::{ use log::{debug, error, warn}; use std::{ - io::{stdin, stdout, Write}, + io::{stdin, stdout}, sync::Arc, time::{Duration, Instant}, }; use anyhow::{Context, Error}; -use crossterm::{ - event::{ - DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, - EnableFocusChange, EnableMouseCapture, Event as CrosstermEvent, KeyboardEnhancementFlags, - PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, - }, - execute, terminal, - tty::IsTty, -}; +use crossterm::{event::Event as CrosstermEvent, tty::IsTty}; #[cfg(not(windows))] use { signal_hook::{consts::signal, low_level}, @@ -63,10 +55,12 @@ use tui::backend::CrosstermBackend; use tui::backend::TestBackend; #[cfg(not(feature = "integration"))] -type Terminal = tui::terminal::Terminal>; +type TerminalBackend = CrosstermBackend; #[cfg(feature = "integration")] -type Terminal = tui::terminal::Terminal; +type TerminalBackend = TestBackend; + +type Terminal = tui::terminal::Terminal; pub struct Application { compositor: Compositor, @@ -108,26 +102,6 @@ fn setup_integration_logging() { .apply(); } -fn restore_term() -> Result<(), Error> { - let mut stdout = stdout(); - // reset cursor shape - write!(stdout, "\x1B[0 q")?; - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { - execute!(stdout, PopKeyboardEnhancementFlags)?; - } - // Ignore errors on disabling, this might trigger on windows if we call - // disable without calling enable previously - let _ = execute!(stdout, DisableMouseCapture); - execute!( - stdout, - DisableBracketedPaste, - DisableFocusChange, - terminal::LeaveAlternateScreen - )?; - terminal::disable_raw_mode()?; - Ok(()) -} - impl Application { pub fn new( args: Args, @@ -472,13 +446,7 @@ impl Application { pub async fn handle_signals(&mut self, signal: i32) { match signal { signal::SIGTSTP => { - // restore cursor - use helix_view::graphics::CursorKind; - self.terminal - .backend_mut() - .show_cursor(CursorKind::Block) - .ok(); - restore_term().unwrap(); + self.restore_term().unwrap(); low_level::emulate_default_handler(signal::SIGTSTP).unwrap(); } signal::SIGCONT => { @@ -1054,37 +1022,19 @@ impl Application { } } - async fn claim_term(&mut self) -> Result<(), Error> { - use helix_view::graphics::CursorKind; - terminal::enable_raw_mode()?; - if self.terminal.cursor_kind() == CursorKind::Hidden { - self.terminal.backend_mut().hide_cursor().ok(); - } - let mut stdout = stdout(); - execute!( - stdout, - terminal::EnterAlternateScreen, - EnableBracketedPaste, - EnableFocusChange - )?; - execute!(stdout, terminal::Clear(terminal::ClearType::All))?; - if self.config.load().editor.mouse { - execute!(stdout, EnableMouseCapture)?; - } - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { - log::debug!("The enhanced keyboard protocol is supported on this terminal"); - execute!( - stdout, - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS - ) - )?; - } else { - log::debug!("The enhanced keyboard protocol is not supported on this terminal"); - } + async fn claim_term(&mut self) -> std::io::Result<()> { + let terminal_config = self.config.load().editor.clone().into(); + self.terminal.claim(terminal_config) + } - Ok(()) + fn restore_term(&mut self) -> std::io::Result<()> { + let terminal_config = self.config.load().editor.clone().into(); + use helix_view::graphics::CursorKind; + self.terminal + .backend_mut() + .show_cursor(CursorKind::Block) + .ok(); + self.terminal.restore(terminal_config) } pub async fn run(&mut self, input_stream: &mut S) -> Result @@ -1099,7 +1049,7 @@ impl Application { // We can't handle errors properly inside this closure. And it's // probably not a good idea to `unwrap()` inside a panic handler. // So we just ignore the `Result`. - let _ = restore_term(); + let _ = TerminalBackend::force_restore(); hook(info); })); @@ -1107,13 +1057,7 @@ impl Application { let close_errs = self.close().await; - // restore cursor - use helix_view::graphics::CursorKind; - self.terminal - .backend_mut() - .show_cursor(CursorKind::Block) - .ok(); - restore_term()?; + self.restore_term()?; for err in close_errs { self.editor.exit_code = 1; diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index ccd016f50b9c..8a6d5367d82e 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -22,5 +22,7 @@ unicode-segmentation = "1.10" crossterm = { version = "0.26", optional = true } termini = "0.1" serde = { version = "1", "optional" = true, features = ["derive"]} +once_cell = "1.17" +log = "~0.4" helix-view = { version = "0.6", path = "../helix-view", features = ["term"] } helix-core = { version = "0.6", path = "../helix-core" } diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index 5305640cb15a..fba1f029c244 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -1,6 +1,11 @@ -use crate::{backend::Backend, buffer::Cell}; +use crate::{backend::Backend, buffer::Cell, terminal::Config}; use crossterm::{ cursor::{Hide, MoveTo, SetCursorStyle, Show}, + event::{ + DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, + EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags, + PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, + }, execute, queue, style::{ Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor, @@ -10,6 +15,7 @@ use crossterm::{ Command, }; use helix_view::graphics::{Color, CursorKind, Modifier, Rect, UnderlineStyle}; +use once_cell::sync::OnceCell; use std::{ fmt, io::{self, Write}, @@ -52,6 +58,7 @@ impl Capabilities { pub struct CrosstermBackend { buffer: W, capabilities: Capabilities, + supports_keyboard_enhancement_protocol: OnceCell, } impl CrosstermBackend @@ -62,8 +69,27 @@ where CrosstermBackend { buffer, capabilities: Capabilities::from_env_or_default(), + supports_keyboard_enhancement_protocol: OnceCell::new(), } } + + #[inline] + fn supports_keyboard_enhancement_protocol(&self) -> io::Result { + self.supports_keyboard_enhancement_protocol + .get_or_try_init(|| { + use std::time::Instant; + + let now = Instant::now(); + let support = terminal::supports_keyboard_enhancement(); + log::debug!( + "The keyboard enhancement protocol is {}supported in this terminal (checked in {:?})", + if matches!(support, Ok(true)) { "" } else { "not " }, + Instant::now().duration_since(now) + ); + support + }) + .copied() + } } impl Write for CrosstermBackend @@ -83,6 +109,66 @@ impl Backend for CrosstermBackend where W: Write, { + fn claim(&mut self, config: Config) -> io::Result<()> { + terminal::enable_raw_mode()?; + execute!( + self.buffer, + terminal::EnterAlternateScreen, + EnableBracketedPaste, + EnableFocusChange + )?; + execute!(self.buffer, terminal::Clear(terminal::ClearType::All))?; + if config.enable_mouse_capture { + execute!(self.buffer, EnableMouseCapture)?; + } + if self.supports_keyboard_enhancement_protocol()? { + execute!( + self.buffer, + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) + )?; + } + Ok(()) + } + + fn restore(&mut self, config: Config) -> io::Result<()> { + // reset cursor shape + write!(self.buffer, "\x1B[0 q")?; + if config.enable_mouse_capture { + execute!(self.buffer, DisableMouseCapture)?; + } + if self.supports_keyboard_enhancement_protocol()? { + execute!(self.buffer, PopKeyboardEnhancementFlags)?; + } + execute!( + self.buffer, + DisableBracketedPaste, + DisableFocusChange, + terminal::LeaveAlternateScreen + )?; + terminal::disable_raw_mode() + } + + fn force_restore() -> io::Result<()> { + let mut stdout = io::stdout(); + + // reset cursor shape + write!(stdout, "\x1B[0 q")?; + // Ignore errors on disabling, this might trigger on windows if we call + // disable without calling enable previously + let _ = execute!(stdout, DisableMouseCapture); + let _ = execute!(stdout, PopKeyboardEnhancementFlags); + execute!( + stdout, + DisableBracketedPaste, + DisableFocusChange, + terminal::LeaveAlternateScreen + )?; + terminal::disable_raw_mode() + } + fn draw<'a, I>(&mut self, content: I) -> io::Result<()> where I: Iterator, diff --git a/helix-tui/src/backend/mod.rs b/helix-tui/src/backend/mod.rs index c6c11019de8c..6d7c3894258b 100644 --- a/helix-tui/src/backend/mod.rs +++ b/helix-tui/src/backend/mod.rs @@ -1,6 +1,6 @@ use std::io; -use crate::buffer::Cell; +use crate::{buffer::Cell, terminal::Config}; use helix_view::graphics::{CursorKind, Rect}; @@ -13,6 +13,9 @@ mod test; pub use self::test::TestBackend; pub trait Backend { + fn claim(&mut self, config: Config) -> Result<(), io::Error>; + fn restore(&mut self, config: Config) -> Result<(), io::Error>; + fn force_restore() -> Result<(), io::Error>; fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error> where I: Iterator; diff --git a/helix-tui/src/backend/test.rs b/helix-tui/src/backend/test.rs index 52474148e7bc..ff133ff3e846 100644 --- a/helix-tui/src/backend/test.rs +++ b/helix-tui/src/backend/test.rs @@ -1,6 +1,7 @@ use crate::{ backend::Backend, buffer::{Buffer, Cell}, + terminal::Config, }; use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::{CursorKind, Rect}; @@ -106,6 +107,18 @@ impl TestBackend { } impl Backend for TestBackend { + fn claim(&mut self, _config: Config) -> Result<(), io::Error> { + Ok(()) + } + + fn restore(&mut self, _config: Config) -> Result<(), io::Error> { + Ok(()) + } + + fn force_restore() -> Result<(), io::Error> { + Ok(()) + } + fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error> where I: Iterator, diff --git a/helix-tui/src/terminal.rs b/helix-tui/src/terminal.rs index 22e9232f3f86..802a8c1d9f53 100644 --- a/helix-tui/src/terminal.rs +++ b/helix-tui/src/terminal.rs @@ -1,4 +1,5 @@ use crate::{backend::Backend, buffer::Buffer}; +use helix_view::editor::Config as EditorConfig; use helix_view::graphics::{CursorKind, Rect}; use std::io; @@ -16,6 +17,19 @@ pub struct Viewport { resize_behavior: ResizeBehavior, } +#[derive(Debug)] +pub struct Config { + pub enable_mouse_capture: bool, +} + +impl From for Config { + fn from(config: EditorConfig) -> Self { + Self { + enable_mouse_capture: config.mouse, + } + } +} + impl Viewport { /// UNSTABLE pub fn fixed(area: Rect) -> Viewport { @@ -98,6 +112,14 @@ where }) } + pub fn claim(&mut self, config: Config) -> io::Result<()> { + self.backend.claim(config) + } + + pub fn restore(&mut self, config: Config) -> io::Result<()> { + self.backend.restore(config) + } + // /// Get a Frame object which provides a consistent view into the terminal state for rendering. // pub fn get_frame(&mut self) -> Frame { // Frame {