From 7ad5483a9258522bcc490a1744e68156f7018974 Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy Date: Mon, 27 Jul 2020 02:11:13 -0600 Subject: [PATCH] Add crossterm support --- Cargo.lock | 169 +++++--------------------------------------- Cargo.toml | 4 +- src/app.rs | 8 +-- src/main.rs | 34 +++------ src/util.rs | 200 ++++++++++++++++++++++++++++++++-------------------- 5 files changed, 158 insertions(+), 257 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7e3c6a..8761e4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -38,7 +38,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -120,22 +120,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "crossterm" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5750773d74a7dc612eac2ded3f55e9cdeeaa072210cd17c0192aedb48adb3618" -dependencies = [ - "bitflags", - "crossterm_winapi 0.5.1", - "lazy_static", - "libc", - "mio 0.6.22", - "parking_lot", - "signal-hook", - "winapi 0.3.9", -] - [[package]] name = "crossterm" version = "0.17.7" @@ -143,22 +127,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" dependencies = [ "bitflags", - "crossterm_winapi 0.6.1", + "crossterm_winapi", "lazy_static", "libc", - "mio 0.7.0", + "mio", "parking_lot", "signal-hook", - "winapi 0.3.9", -] - -[[package]] -name = "crossterm_winapi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8777c700901e2d5b50c406f736ed6b8f9e43645c7e104ddb74f8bc42b8ae62f6" -dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -167,7 +142,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -263,22 +238,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "getrandom" version = "0.1.14" @@ -311,31 +270,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "itoa" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -375,25 +315,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.6.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" -dependencies = [ - "cfg-if", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.1", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.7.0" @@ -403,21 +324,9 @@ dependencies = [ "lazy_static", "libc", "log", - "miow 0.3.5", + "miow", "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "winapi", ] [[package]] @@ -427,18 +336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" dependencies = [ "socket2", - "winapi 0.3.9", -] - -[[package]] -name = "net2" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" -dependencies = [ - "cfg-if", - "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -447,7 +345,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -502,7 +400,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -563,7 +461,7 @@ dependencies = [ "rand_os", "rand_pcg", "rand_xorshift", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -658,7 +556,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ "libc", "rand_core 0.4.2", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -672,7 +570,7 @@ dependencies = [ "libc", "rand_core 0.4.2", "rdrand", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -780,8 +678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" dependencies = [ "libc", - "mio 0.6.22", - "mio 0.7.0", + "mio", "signal-hook-registry", ] @@ -795,12 +692,6 @@ dependencies = [ "libc", ] -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" - [[package]] name = "smallvec" version = "1.4.1" @@ -816,7 +707,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -881,7 +772,7 @@ version = "0.1.0" dependencies = [ "chrono", "clap", - "crossterm 0.14.2", + "crossterm", "rand 0.7.3", "serde", "serde_json", @@ -920,7 +811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -931,7 +822,7 @@ checksum = "a977b0bb2e2033a6fef950f218f13622c3c34e59754b704ce3492dedab1dfe95" dependencies = [ "bitflags", "cassowary", - "crossterm 0.17.7", + "crossterm", "termion", "unicode-segmentation", "unicode-width", @@ -983,12 +874,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -999,12 +884,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1016,13 +895,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] diff --git a/Cargo.toml b/Cargo.toml index 2f88d48..390bb24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["termion-backend"] +default = ["crossterm-backend"] termion-backend = ["tui/termion", "termion"] crossterm-backend = ["tui/crossterm", "crossterm"] @@ -18,7 +18,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" task-hookrs = "0.7.0" termion = { version = "1.5.4", optional = true } -crossterm = { version = "0.14.2", optional = true } +crossterm = { version = "0.17", optional = true } rand = "0.7" shlex = "0.1.1" chrono = "0.4" diff --git a/src/app.rs b/src/app.rs index b5cffe3..8248ebf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -14,7 +14,7 @@ use unicode_width::UnicodeWidthStr; use chrono::{DateTime, Duration, Local, NaiveDateTime, TimeZone}; use tui::{ - backend::{Backend, TermionBackend}, + backend::{Backend}, layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, terminal::Frame, @@ -23,9 +23,9 @@ use tui::{ Terminal, }; -use termion::event::Key; -use crate::util::{Config, Event, Events}; +use crate::util::{Key}; +use crossterm::event::KeyCode; pub fn cmp(t1: &Task, t2: &Task) -> Ordering { let urgency1 = match &t1.uda()["urgency"] { @@ -382,7 +382,7 @@ impl App { self.update(); } - pub fn handle_input(&mut self, event: ::termion::event::Key) { + pub fn handle_input(&mut self, event: Key) { match self.input_mode { InputMode::Normal => match event { Key::Ctrl('c') | Key::Char('q') => self.should_quit = true, diff --git a/src/main.rs b/src/main.rs index def969e..4ff2ae5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,16 +7,12 @@ mod util; #[allow(dead_code)] mod app; -use crate::util::{Config, Event, Events}; -use std::time::Duration; -use std::{error::Error, io}; -use termion::{ - event::Key, - input::MouseTerminal, - raw::{IntoRawMode, RawTerminal}, - screen::AlternateScreen, -}; -use tui::{backend::TermionBackend, Terminal}; +use crate::util::{EventConfig, Event, Events, setup_terminal, destruct_terminal}; +use std::time::{Duration, Instant}; +use std::error::Error; +use std::io::{stdout, Write}; +use std::io; +use tui::backend::Backend; use unicode_width::UnicodeWidthStr; use std::env; use std::process::Command; @@ -24,27 +20,16 @@ use std::process::Command; use app::App; use app::InputMode; -type B = TermionBackend>>>; - -fn setup_terminal() -> Result, io::Error> { - let stdout = io::stdout().into_raw_mode()?; - let stdout = MouseTerminal::from(stdout); - let stdout = AlternateScreen::from(stdout); - let backend = TermionBackend::new(stdout); - Terminal::new(backend) -} - fn main() -> Result<(), Box> { // Terminal initialization - let mut terminal = setup_terminal()?; + let mut terminal = setup_terminal(); + terminal.clear()?; // Setup event handlers - let mut events = Events::with_config(Config { - exit_key: Key::Char('q'), + let events = Events::with_config(EventConfig { tick_rate: Duration::from_millis(250), }); - events.disable_exit_key(); let mut app = App::new(); app.next(); @@ -59,6 +44,7 @@ fn main() -> Result<(), Box> { } if app.should_quit { + destruct_terminal(terminal); break } } diff --git a/src/util.rs b/src/util.rs index 701c999..a9ec723 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,96 +1,142 @@ -#[cfg(feature = "termion")] -use std::io; -use std::sync::mpsc; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, +use std::io::{stdout, Write}; + +#[cfg(feature = "crossterm")] +use crossterm::event; +#[cfg(feature = "crossterm")] +use crossterm::{ + event::{DisableMouseCapture, EnableMouseCapture, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use std::thread; -use std::time::Duration; -use termion::event::Key; -use termion::input::TermRead; +#[cfg(feature = "crossterm")] +use tui::{backend::CrosstermBackend, Terminal}; -pub enum Event { - Input(I), - Tick, -} +use std::{sync::mpsc, thread, time::Duration}; -/// A small event handler that wrap termion input and tick events. Each event -/// type is handled in its own thread and returned to a common `Receiver` -pub struct Events { - rx: mpsc::Receiver>, - input_handle: thread::JoinHandle<()>, - ignore_exit_key: Arc, - tick_handle: thread::JoinHandle<()>, +#[derive(Debug, Clone, Copy)] +pub enum Key { + Backspace, + Left, + Right, + Up, + Down, + Home, + End, + PageUp, + PageDown, + BackTab, + Delete, + Insert, + F(u8), + Char(char), + Alt(char), + Ctrl(char), + Null, + Esc, } #[derive(Debug, Clone, Copy)] -pub struct Config { - pub exit_key: Key, - pub tick_rate: Duration, +pub struct EventConfig { + pub tick_rate: Duration, } -impl Default for Config { - fn default() -> Config { - Config { - exit_key: Key::Char('q'), - tick_rate: Duration::from_millis(250), - } +impl Default for EventConfig { + fn default() -> EventConfig { + EventConfig { + tick_rate: Duration::from_millis(5), } + } +} + +/// An occurred event. +pub enum Event { + /// An input event occurred. + Input(I), + /// An tick event occurred. + Tick, +} + +#[cfg(feature = "crossterm")] +pub fn setup_terminal() -> Terminal> { + enable_raw_mode().unwrap(); + let mut stdout = stdout(); + execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap(); + let backend = CrosstermBackend::new(stdout); + Terminal::new(backend).unwrap() +} + +#[cfg(feature = "crossterm")] +pub fn destruct_terminal(mut terminal: Terminal>) { + disable_raw_mode().unwrap(); + execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture).unwrap(); + terminal.show_cursor().unwrap(); +} + + +pub struct Events { + rx: mpsc::Receiver>, + tx: mpsc::Sender>, } impl Events { - pub fn new() -> Events { - Events::with_config(Config::default()) - } + /// Constructs an new instance of `Events` with the default config. + pub fn new(tick_rate: u64) -> Events { + Events::with_config(EventConfig { + tick_rate: Duration::from_millis(tick_rate), + ..Default::default() + }) + } - pub fn with_config(config: Config) -> Events { - let (tx, rx) = mpsc::channel(); - let ignore_exit_key = Arc::new(AtomicBool::new(false)); - let input_handle = { - let tx = tx.clone(); - let ignore_exit_key = ignore_exit_key.clone(); - thread::spawn(move || { - let stdin = io::stdin(); - for evt in stdin.keys() { - if let Ok(key) = evt { - if let Err(err) = tx.send(Event::Input(key)) { - eprintln!("{}", err); - return; - } - if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key { - return; - } - } - } - }) - }; - let tick_handle = { - thread::spawn(move || loop { - if tx.send(Event::Tick).is_err() { - break; - } - thread::sleep(config.tick_rate); - }) - }; - Events { - rx, - ignore_exit_key, - input_handle, - tick_handle, + pub fn with_config(config: EventConfig) -> Events { + use crossterm::event::{KeyCode::*, KeyModifiers}; + let (tx, rx) = mpsc::channel(); + + let event_tx = tx.clone(); + thread::spawn(move || { + loop { + // poll for tick rate duration, if no event, sent tick event. + if event::poll(config.tick_rate).unwrap() { + if let event::Event::Key(key) = event::read().unwrap() { + let key = match key.code { + Backspace => Key::Backspace, + Enter => Key::Char('\n'), + Left => Key::Left, + Right => Key::Right, + Up => Key::Up, + Down => Key::Down, + Home => Key::Home, + End => Key::End, + PageUp => Key::PageUp, + PageDown => Key::PageDown, + Tab => Key::Char('\t'), + BackTab => Key::BackTab, + Delete => Key::Delete, + Insert => Key::Insert, + F(k) => Key::F(k), + Null => Key::Null, + Esc => Key::Esc, + Char(c) => match key.modifiers { + KeyModifiers::NONE | KeyModifiers::SHIFT => Key::Char(c), + KeyModifiers::CONTROL => Key::Ctrl(c), + KeyModifiers::ALT => Key::Alt(c), + _ => Key::Null, + }, + }; + event_tx.send(Event::Input(key)).unwrap(); + } } - } - pub fn next(&self) -> Result, mpsc::RecvError> { - self.rx.recv() - } + event_tx.send(Event::Tick).unwrap(); + } + }); - pub fn disable_exit_key(&mut self) { - self.ignore_exit_key.store(true, Ordering::Relaxed); - } + Events { rx, tx } + } - pub fn enable_exit_key(&mut self) { - self.ignore_exit_key.store(false, Ordering::Relaxed); - } + /// Attempts to read an event. + /// This function will block the current thread. + pub fn next(&self) -> Result, mpsc::RecvError> { + self.rx.recv() + } }