From 80979a179f924af87a33fc81ccca055ce6df5636 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 29 Mar 2020 23:15:50 +0800 Subject: [PATCH] Don't try to shutdown keyinput thread to not lose input events --- Cargo.lock | 16 +++ Cargo.toml | 1 + src/interactive/app/eventloop.rs | 173 +++++++++++++----------------- src/interactive/app_test/utils.rs | 3 +- src/main.rs | 4 +- 5 files changed, 98 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 859b7862..6c5f99d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,6 +189,7 @@ dependencies = [ "failure", "failure-tools", "filesize", + "flume", "itertools 0.9.0", "jwalk", "log", @@ -255,6 +256,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +[[package]] +name = "flume" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "855e285c3835897065a6ba6f9463b44553eb9f29c7988d692f3d41283b47388b" +dependencies = [ + "spin", +] + [[package]] name = "heck" version = "0.3.1" @@ -497,6 +507,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index e786b0a3..a21faa09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ tui-react = { path = "./tui-react", version = "0.2" } num_cpus = "1.10.0" unicode-segmentation = "1.3.0" filesize = "0.2.0" +flume = {version = "0.7.1", default-features = false} [[bin]] name="dua" diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 94b6123f..9e931026 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -62,13 +62,13 @@ impl AppState { draw_window(window, props, terminal) } - pub fn process_event( + pub fn process_events( &mut self, window: &mut MainWindow, traversal: &mut Traversal, display: &mut DisplayOptions, terminal: &mut Terminal, - key: Key, + keys: impl Iterator>, ) -> Result where B: Backend, @@ -76,82 +76,61 @@ impl AppState { use termion::event::Key::*; use FocussedPane::*; - self.reset_message(); - match key { - Char('?') => self.toggle_help_pane(window), - Char('\t') => { - self.cycle_focus(window); - } - Ctrl('c') => { - return Ok(ProcessingResult::ExitRequested(WalkResult { - num_errors: traversal.io_errors, - })) - } - Char('q') | Esc => match self.focussed { - Main => { + self.draw(window, traversal, display.clone(), terminal)?; + for key in keys.filter_map(Result::ok) { + self.reset_message(); + match key { + Char('?') => self.toggle_help_pane(window), + Char('\t') => { + self.cycle_focus(window); + } + Ctrl('c') => { return Ok(ProcessingResult::ExitRequested(WalkResult { num_errors: traversal.io_errors, })) } - Mark => self.focussed = Main, - Help => { - self.focussed = Main; - window.help_pane = None - } - }, - _ => {} - } - - match self.focussed { - FocussedPane::Mark => { - self.dispatch_to_mark_pane(key, window, traversal, display.clone(), terminal) - } - FocussedPane::Help => { - window.help_pane.as_mut().expect("help pane").key(key); + Char('q') | Esc => match self.focussed { + Main => { + return Ok(ProcessingResult::ExitRequested(WalkResult { + num_errors: traversal.io_errors, + })) + } + Mark => self.focussed = Main, + Help => { + self.focussed = Main; + window.help_pane = None + } + }, + _ => {} } - FocussedPane::Main => match key { - Char('O') => self.open_that(traversal), - Char(' ') => self.mark_entry(false, window, traversal), - Char('d') => self.mark_entry(true, window, traversal), - Char('u') | Char('h') | Backspace | Left => { - self.exit_node_with_traversal(traversal) + + match self.focussed { + FocussedPane::Mark => { + self.dispatch_to_mark_pane(key, window, traversal, display.clone(), terminal) } - Char('o') | Char('l') | Char('\n') | Right => { - self.enter_node_with_traversal(traversal) + FocussedPane::Help => { + window.help_pane.as_mut().expect("help pane").key(key); } - Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp), - Char('k') | Up => self.change_entry_selection(CursorDirection::Up), - Char('j') | Down => self.change_entry_selection(CursorDirection::Down), - Ctrl('d') | PageDown => self.change_entry_selection(CursorDirection::PageDown), - Char('s') => self.cycle_sorting(traversal), - Char('g') => display.byte_vis.cycle(), - _ => {} - }, - }; - self.draw(window, traversal, display.clone(), terminal)?; - Ok(ProcessingResult::Finished(WalkResult { - num_errors: traversal.io_errors, - })) - } - pub fn process_events( - &mut self, - window: &mut MainWindow, - traversal: &mut Traversal, - display: &mut DisplayOptions, - terminal: &mut Terminal, - keys: impl Iterator>, - ) -> Result - where - B: Backend, - { - self.reset_message(); - self.draw(window, traversal, display.clone(), terminal)?; - for key in keys.filter_map(Result::ok) { - if let r @ ProcessingResult::ExitRequested(_) = - self.process_event(window, traversal, display, terminal, key)? - { - return Ok(r); - } + FocussedPane::Main => match key { + Char('O') => self.open_that(traversal), + Char(' ') => self.mark_entry(false, window, traversal), + Char('d') => self.mark_entry(true, window, traversal), + Char('u') | Char('h') | Backspace | Left => { + self.exit_node_with_traversal(traversal) + } + Char('o') | Char('l') | Char('\n') | Right => { + self.enter_node_with_traversal(traversal) + } + Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp), + Char('k') | Up => self.change_entry_selection(CursorDirection::Up), + Char('j') | Down => self.change_entry_selection(CursorDirection::Down), + Ctrl('d') | PageDown => self.change_entry_selection(CursorDirection::PageDown), + Char('s') => self.cycle_sorting(traversal), + Char('g') => display.byte_vis.cycle(), + _ => {} + }, + }; + self.draw(window, traversal, display.clone(), terminal)?; } Ok(ProcessingResult::Finished(WalkResult { num_errors: traversal.io_errors, @@ -206,7 +185,7 @@ impl TerminalApp { options: WalkOptions, input: Vec, mode: Interaction, - ) -> Result, Error> + ) -> Result>, TerminalApp)>, Error> where B: Backend, { @@ -215,7 +194,7 @@ impl TerminalApp { let mut display: DisplayOptions = options.clone().into(); display.byte_vis = ByteVisualization::PercentageAndBar; let mut window = MainWindow::default(); - let (keys_tx, keys_rx) = std::sync::mpsc::channel(); // unbounded + let (keys_tx, keys_rx) = flume::unbounded(); match mode { Interaction::None => drop(keys_tx), Interaction::Full => drop(std::thread::spawn(move || { @@ -228,7 +207,7 @@ impl TerminalApp { })), } - let fetch_buffered_key_events = move || { + let fetch_buffered_key_events = || { let mut keys = Vec::new(); while let Ok(key) = keys_rx.try_recv() { keys.push(key); @@ -273,30 +252,32 @@ impl TerminalApp { Some(t) => t, None => return Ok(None), }; - drop(fetch_buffered_key_events); // shutdown input event handler early for good measure - Ok(Some(TerminalApp { - state: { - let mut s = state.unwrap_or_else(|| { - let sorting = Default::default(); - let root = traversal.root_index; - let entries = sorted_entries(&traversal.tree, root, sorting); - AppState { - root, - sorting, - entries, - ..Default::default() - } - }); - s.is_scanning = false; - s.entries = sorted_entries(&traversal.tree, s.root, s.sorting); - s.selected = s.selected.or_else(|| s.entries.get(0).map(|b| b.index)); - s + Ok(Some(( + keys_rx, + TerminalApp { + state: { + let mut s = state.unwrap_or_else(|| { + let sorting = Default::default(); + let root = traversal.root_index; + let entries = sorted_entries(&traversal.tree, root, sorting); + AppState { + root, + sorting, + entries, + ..Default::default() + } + }); + s.is_scanning = false; + s.entries = sorted_entries(&traversal.tree, s.root, s.sorting); + s.selected = s.selected.or_else(|| s.entries.get(0).map(|b| b.index)); + s + }, + display, + traversal, + window, }, - display, - traversal, - window, - })) + ))) } } diff --git a/src/interactive/app_test/utils.rs b/src/interactive/app_test/utils.rs index e46f9e7b..8fde29fd 100644 --- a/src/interactive/app_test/utils.rs +++ b/src/interactive/app_test/utils.rs @@ -178,7 +178,8 @@ pub fn initialized_app_and_terminal_with_closure>( }, input, Interaction::None, - )?; + )? + .map(|(_, app)| app); Ok(( terminal, app.expect("app that didn't try to abort iteration"), diff --git a/src/main.rs b/src/main.rs index 9c422726..e9ea37eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use failure::{Error, ResultExt}; use failure_tools::ok_or_exit; use std::{fs, io, io::Write, path::PathBuf, process}; use structopt::StructOpt; -use termion::{input::TermRead, raw::IntoRawMode, screen::AlternateScreen}; +use termion::{raw::IntoRawMode, screen::AlternateScreen}; use tui::backend::TermionBackend; use tui_react::Terminal; @@ -49,7 +49,7 @@ fn run() -> Result<(), Error> { paths_from(input)?, Interaction::Full, )? - .map(|mut app| app.process_events(&mut terminal, io::stdin().keys())); + .map(|(keys_rx, mut app)| app.process_events(&mut terminal, keys_rx.into_iter())); drop(terminal); io::stdout().flush().ok();