diff --git a/Cargo.lock b/Cargo.lock index d4b77036..990dd119 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,7 +144,7 @@ dependencies = [ "event-listener", "futures-lite", "once_cell", - "signal-hook", + "signal-hook 0.1.17", "winapi", ] @@ -454,25 +454,25 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.18.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb" +checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" dependencies = [ "bitflags", "crossterm_winapi", - "lazy_static", "libc", "mio", "parking_lot", - "signal-hook", + "signal-hook 0.3.9", + "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" -version = "0.6.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db" +checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" dependencies = [ "winapi", ] @@ -1333,20 +1333,40 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" dependencies = [ "libc", - "mio", "signal-hook-registry", ] [[package]] -name = "signal-hook-registry" -version = "1.2.2" +name = "signal-hook" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" +checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" +dependencies = [ + "libc", + "mio", + "signal-hook 0.3.9", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] @@ -1483,9 +1503,9 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] name = "tui" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ced152a8e9295a5b168adc254074525c17ac4a83c90b2716274cc38118bddc9" +checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23" dependencies = [ "bitflags", "cassowary", diff --git a/Cargo.toml b/Cargo.toml index e7bdfe3b..9a41e0d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ anyhow = "1.0.40" backtrace = "0.3.59" battery = "0.7.8" chrono = "0.4.19" -crossterm = "0.18.2" +crossterm = "0.20.0" ctrlc = { version = "3.1.9", features = ["termination"] } clap = "2.33" cfg-if = "1.0" @@ -55,7 +55,7 @@ serde = { version = "1.0.125", features = ["derive"] } sysinfo = "0.18.2" thiserror = "1.0.24" toml = "0.5.8" -tui = { version = "0.14.0", features = ["crossterm"], default-features = false } +tui = { version = "0.16.0", features = ["crossterm"], default-features = false } typed-builder = "0.9.0" unicode-segmentation = "1.7.1" unicode-width = "0.1" diff --git a/src/bin/main.rs b/src/bin/main.rs index 48a757a0..d6d81ad6 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -8,7 +8,7 @@ use bottom::{canvas, constants::*, data_conversion::*, options::*, *}; use std::{ boxed::Box, - io::{stdout, Write}, + io::stdout, panic, sync::{ atomic::{AtomicBool, Ordering}, @@ -96,7 +96,7 @@ fn main() -> Result<()> { }; // Event loop - let (collection_thread_ctrl_sender, collection_thread_ctrl_receiver) = mpsc::channel(); + let (collection_sender, collection_thread_ctrl_receiver) = mpsc::channel(); let _collection_thread = create_collection_thread( sender, collection_thread_ctrl_receiver, @@ -131,15 +131,29 @@ fn main() -> Result<()> { if let Ok(recv) = receiver.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { match recv { BottomEvent::KeyInput(event) => { - if handle_key_event_or_break(event, &mut app, &collection_thread_ctrl_sender) { + match handle_key_event(event, &mut app, &collection_sender) { + EventResult::Quit => { + break; + } + EventResult::Redraw => { + // TODO: Be even more granular! Maybe the event triggered no change, then we shouldn't redraw. + force_redraw(&mut app); + try_drawing(&mut terminal, &mut app, &mut painter)?; + } + EventResult::Continue => {} + } + } + BottomEvent::MouseInput(event) => match handle_mouse_event(event, &mut app) { + EventResult::Quit => { break; } - handle_force_redraws(&mut app); - } - BottomEvent::MouseInput(event) => { - handle_mouse_event(event, &mut app); - handle_force_redraws(&mut app); - } + EventResult::Redraw => { + // TODO: Be even more granular! Maybe the event triggered no change, then we shouldn't redraw. + force_redraw(&mut app); + try_drawing(&mut terminal, &mut app, &mut painter)?; + } + EventResult::Continue => {} + }, BottomEvent::Update(data) => { app.data_collection.eat_data(data); @@ -220,6 +234,8 @@ fn main() -> Result<()> { app.canvas_data.battery_data = convert_battery_harvest(&app.data_collection); } + + try_drawing(&mut terminal, &mut app, &mut painter)?; } } BottomEvent::Clean => { @@ -228,17 +244,11 @@ fn main() -> Result<()> { } } } - - // TODO: [OPT] Should not draw if no change (ie: scroll max) - try_drawing(&mut terminal, &mut app, &mut painter)?; } // I think doing it in this order is safe... - *thread_termination_lock.lock().unwrap() = true; - thread_termination_cvar.notify_all(); - cleanup_terminal(&mut terminal)?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 87ca1c69..f5aae7d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,10 @@ use std::{ }; use crossterm::{ - event::{poll, read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent}, + event::{ + read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, + MouseEventKind, + }, execute, style::Print, terminal::{disable_raw_mode, LeaveAlternateScreen}, @@ -71,29 +74,36 @@ pub enum ThreadControlEvent { UpdateUpdateTime(u64), } -pub fn handle_mouse_event(event: MouseEvent, app: &mut AppState) { - match event { - MouseEvent::ScrollUp(_x, _y, _modifiers) => app.handle_scroll_up(), - MouseEvent::ScrollDown(_x, _y, _modifiers) => app.handle_scroll_down(), - MouseEvent::Down(button, x, y, _modifiers) => { - if !app.app_config_fields.disable_click { - match button { - crossterm::event::MouseButton::Left => { - // Trigger left click widget activity - app.on_left_mouse_up(x, y); - } - crossterm::event::MouseButton::Right => {} - _ => {} - } - } - } - _ => {} - }; +pub enum EventResult { + Quit, + Redraw, + Continue, } -pub fn handle_key_event_or_break( +pub fn handle_mouse_event(event: MouseEvent, app: &mut AppState) -> EventResult { + match event.kind { + MouseEventKind::Down(button) => match button { + MouseButton::Left => { + app.on_left_mouse_up(event.column, event.row); + EventResult::Redraw + } + _ => EventResult::Continue, + }, + MouseEventKind::ScrollUp => { + app.handle_scroll_up(); + EventResult::Redraw + } + MouseEventKind::ScrollDown => { + app.handle_scroll_down(); + EventResult::Redraw + } + _ => EventResult::Continue, + } +} + +pub fn handle_key_event( event: KeyEvent, app: &mut AppState, reset_sender: &std::sync::mpsc::Sender, -) -> bool { +) -> EventResult { // debug!("KeyEvent: {:?}", event); // TODO: [PASTE] Note that this does NOT support some emojis like flags. This is due to us @@ -104,7 +114,7 @@ pub fn handle_key_event_or_break( if event.modifiers.is_empty() { // Required catch for searching - otherwise you couldn't search with q. if event.code == KeyCode::Char('q') && !app.is_in_search_widget() { - return true; + return EventResult::Quit; } match event.code { KeyCode::End => app.skip_to_last(), @@ -125,7 +135,9 @@ pub fn handle_key_event_or_break( KeyCode::F(5) => app.toggle_tree_mode(), KeyCode::F(6) => app.toggle_sort(), KeyCode::F(9) => app.start_killing_process(), - _ => {} + _ => { + return EventResult::Continue; + } } } else { // Otherwise, track the modifier as well... @@ -140,7 +152,7 @@ pub fn handle_key_event_or_break( } } else if let KeyModifiers::CONTROL = event.modifiers { if event.code == KeyCode::Char('c') { - return true; + return EventResult::Quit; } match event.code { @@ -166,7 +178,9 @@ pub fn handle_key_event_or_break( // Can't do now, CTRL+BACKSPACE doesn't work and graphemes // are hard to iter while truncating last (eloquently). // KeyCode::Backspace => app.skip_word_backspace(), - _ => {} + _ => { + return EventResult::Continue; + } } } else if let KeyModifiers::SHIFT = event.modifiers { match event.code { @@ -175,12 +189,14 @@ pub fn handle_key_event_or_break( KeyCode::Up => app.move_widget_selection(&WidgetDirection::Up), KeyCode::Down => app.move_widget_selection(&WidgetDirection::Down), KeyCode::Char(caught_char) => app.on_char_key(caught_char), - _ => {} + _ => { + return EventResult::Continue; + } } } } - false + EventResult::Redraw } pub fn read_config(config_location: Option<&str>) -> error::Result> { @@ -300,7 +316,7 @@ pub fn panic_hook(panic_info: &PanicInfo<'_>) { .unwrap(); } -pub fn handle_force_redraws(app: &mut AppState) { +pub fn force_redraw(app: &mut AppState) { // Currently we use an Option... because we might want to future-proof this // if we eventually get widget-specific redrawing! if app.proc_state.force_update_all { @@ -607,25 +623,30 @@ pub fn create_input_thread( break; } } - if let Ok(poll) = poll(Duration::from_millis(20)) { - if poll { - if let Ok(event) = read() { - if let Event::Key(key) = event { - if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { - if sender.send(BottomEvent::KeyInput(key)).is_err() { - break; - } - keyboard_timer = Instant::now(); + + if let Ok(event) = read() { + match event { + Event::Key(event) => { + if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { + if sender.send(BottomEvent::KeyInput(event)).is_err() { + break; } - } else if let Event::Mouse(mouse) = event { + keyboard_timer = Instant::now(); + } + } + Event::Mouse(event) => match &event.kind { + MouseEventKind::Drag(_) => {} + MouseEventKind::Moved => {} + _ => { if Instant::now().duration_since(mouse_timer).as_millis() >= 20 { - if sender.send(BottomEvent::MouseInput(mouse)).is_err() { + if sender.send(BottomEvent::MouseInput(event)).is_err() { break; } mouse_timer = Instant::now(); } } - } + }, + Event::Resize(_, _) => {} } } } diff --git a/src/utils/error.rs b/src/utils/error.rs index e265ad3e..7a9009d2 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -16,9 +16,6 @@ pub enum BottomError { /// An error when the heim library encounters a problem. #[error("Error caused by Heim, {0}")] InvalidHeim(String), - /// An error when the Crossterm library encounters a problem. - #[error("Error caused by Crossterm, {0}")] - CrosstermError(String), /// An error to represent generic errors. #[error("Generic error, {0}")] GenericError(String), @@ -55,11 +52,6 @@ impl From for BottomError { } } -impl From for BottomError { - fn from(err: crossterm::ErrorKind) -> Self { - BottomError::CrosstermError(err.to_string()) - } -} impl From for BottomError { fn from(err: std::num::ParseIntError) -> Self {