#[cfg(feature = "crossterm")]
use crossterm::{
event::{
self, DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
Event,
},
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
#[cfg(feature = "crossterm")]
use std::{io, time::Duration};
#[cfg(feature = "crossterm")]
#[derive(Debug, Clone, Copy)]
pub struct CrosstermInputOptions {
pub raw_mode: bool,
pub alternate_screen: bool,
pub mouse_capture: bool,
pub bracketed_paste: bool,
}
#[cfg(feature = "crossterm")]
impl Default for CrosstermInputOptions {
fn default() -> Self {
Self {
raw_mode: true,
alternate_screen: false,
mouse_capture: false,
bracketed_paste: true,
}
}
}
#[cfg(feature = "crossterm")]
impl CrosstermInputOptions {
pub fn tui_defaults() -> Self {
Self {
alternate_screen: true,
mouse_capture: true,
..Self::default()
}
}
pub fn with_raw_mode(mut self, enabled: bool) -> Self {
self.raw_mode = enabled;
self
}
pub fn with_alternate_screen(mut self, enabled: bool) -> Self {
self.alternate_screen = enabled;
self
}
pub fn with_mouse_capture(mut self, enabled: bool) -> Self {
self.mouse_capture = enabled;
self
}
pub fn with_bracketed_paste(mut self, enabled: bool) -> Self {
self.bracketed_paste = enabled;
self
}
}
#[cfg(feature = "crossterm")]
#[derive(Debug)]
pub struct CrosstermInputSession {
raw_mode_enabled: bool,
alternate_screen_enabled: bool,
mouse_capture_enabled: bool,
bracketed_paste_enabled: bool,
}
#[cfg(feature = "crossterm")]
impl CrosstermInputSession {
pub fn install() -> io::Result<Self> {
Self::install_with_options(CrosstermInputOptions::default())
}
pub fn install_with_options(options: CrosstermInputOptions) -> io::Result<Self> {
let mut session = Self {
raw_mode_enabled: false,
alternate_screen_enabled: false,
mouse_capture_enabled: false,
bracketed_paste_enabled: false,
};
if options.raw_mode {
enable_raw_mode()?;
session.raw_mode_enabled = true;
}
let install_result = (|| -> io::Result<()> {
if options.alternate_screen {
execute!(io::stdout(), EnterAlternateScreen)?;
session.alternate_screen_enabled = true;
}
if options.mouse_capture {
execute!(io::stdout(), EnableMouseCapture)?;
session.mouse_capture_enabled = true;
}
if options.bracketed_paste {
execute!(io::stdout(), EnableBracketedPaste)?;
session.bracketed_paste_enabled = true;
}
Ok(())
})();
if let Err(err) = install_result {
let _ = session.uninstall();
return Err(err);
}
Ok(session)
}
pub fn read_event(&self) -> io::Result<Event> {
event::read()
}
pub fn poll_event(&self, timeout: Duration) -> io::Result<bool> {
event::poll(timeout)
}
pub fn uninstall(&mut self) -> io::Result<()> {
if self.bracketed_paste_enabled {
execute!(io::stdout(), DisableBracketedPaste)?;
self.bracketed_paste_enabled = false;
}
if self.mouse_capture_enabled {
execute!(io::stdout(), DisableMouseCapture)?;
self.mouse_capture_enabled = false;
}
if self.alternate_screen_enabled {
execute!(io::stdout(), LeaveAlternateScreen)?;
self.alternate_screen_enabled = false;
}
if self.raw_mode_enabled {
disable_raw_mode()?;
self.raw_mode_enabled = false;
}
Ok(())
}
}
#[cfg(feature = "crossterm")]
impl Drop for CrosstermInputSession {
fn drop(&mut self) {
let _ = self.uninstall();
}
}
#[cfg(feature = "crossterm")]
#[derive(Debug)]
pub struct CrosstermInputGuard {
bracketed_paste_enabled: bool,
}
#[cfg(feature = "crossterm")]
impl CrosstermInputGuard {
pub fn install() -> io::Result<Self> {
execute!(io::stdout(), EnableBracketedPaste)?;
Ok(Self {
bracketed_paste_enabled: true,
})
}
pub fn uninstall(&mut self) -> io::Result<()> {
if self.bracketed_paste_enabled {
execute!(io::stdout(), DisableBracketedPaste)?;
self.bracketed_paste_enabled = false;
}
Ok(())
}
}
#[cfg(feature = "crossterm")]
impl Drop for CrosstermInputGuard {
fn drop(&mut self) {
let _ = self.uninstall();
}
}