Improve input reading by eliminating pause

Stop the thread instead of pausing it. This improves the CPU usage and
responsiveness a slightly.
pull/350/head
Arijit Basu 3 years ago committed by Arijit Basu
parent ac1b40799a
commit bec80e98df

2
Cargo.lock generated

@ -1091,7 +1091,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xplr"
version = "0.15.0"
version = "0.15.1"
dependencies = [
"ansi-to-tui",
"anyhow",

@ -1,6 +1,6 @@
[package]
name = "xplr"
version = "0.15.0" # Update lua.rs
version = "0.15.1" # Update lua.rs
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer"

@ -45,7 +45,7 @@ compatibility.
### Instructions
#### [v0.14.7][3] -> [v0.15.0][43]
#### [v0.14.7][3] -> [v0.15.1][43]
- Deprecated `config` field from `CallLua` argument. Use the globally available
`xplr.config` instead.
@ -277,4 +277,4 @@ Else do the following:
[40]: https://github.com/sayanarijit/xplr/releases/tag/v0.3.8
[41]: https://github.com/sayanarijit/xplr/releases/tag/v0.3.0
[42]: https://github.com/sayanarijit/xplr/releases/tag/v0.14.4
[43]: https://github.com/sayanarijit/xplr/releases/tag/v0.15.0
[43]: https://github.com/sayanarijit/xplr/releases/tag/v0.15.1

@ -1,77 +1,86 @@
use crate::app::Task;
use crate::app::{ExternalMsg, InternalMsg, MsgIn};
use crate::input::Key;
use anyhow::Result;
use crossterm::event::{self, Event, MouseEventKind};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
pub fn pause_reading(tx_event_reader: &Sender<bool>, rx_loop_waiter: &Receiver<()>) -> Result<()> {
tx_event_reader.send(true)?;
rx_loop_waiter.recv()?;
Ok(())
pub(crate) struct EventReader {
task_sender: Sender<Task>,
stopper: Option<(Sender<bool>, Receiver<()>)>,
}
pub fn resume_reading(tx_event_reader: &Sender<bool>, rx_loop_waiter: &Receiver<()>) -> Result<()> {
tx_event_reader.send(false)?;
rx_loop_waiter.recv()?;
Ok(())
}
impl EventReader {
pub(crate) fn new(task_sender: Sender<Task>) -> Self {
Self {
task_sender,
stopper: None,
}
}
pub fn keep_reading(
tx_msg_in: Sender<Task>,
rx_event_reader: Receiver<bool>,
tx_loop_waiter: Sender<()>,
) {
thread::spawn(move || {
let mut is_paused = false;
loop {
if let Ok(paused) = rx_event_reader.try_recv() {
is_paused = paused;
tx_loop_waiter.send(()).unwrap();
};
pub(crate) fn start(&mut self) {
let sender = self.task_sender.clone();
let (tx_stopper, rx_stopper) = mpsc::channel();
let (tx_ack, rx_ack) = mpsc::channel();
self.stopper = Some((tx_stopper, rx_ack));
if is_paused {
thread::sleep(std::time::Duration::from_millis(200));
} else if event::poll(std::time::Duration::from_millis(150)).unwrap_or_default() {
// NOTE: The poll timeout need to stay low, else spawning sub subshell
// and start typing immediately will cause panic.
// To reproduce, press `:`, then press and hold `!`.
match event::read() {
Ok(Event::Key(key)) => {
let key = Key::from_event(key);
let msg = MsgIn::Internal(InternalMsg::HandleKey(key));
tx_msg_in.send(Task::new(msg, Some(key))).unwrap();
}
thread::spawn(move || {
keep_reading(sender, rx_stopper, tx_ack);
});
}
Ok(Event::Mouse(evt)) => match evt.kind {
MouseEventKind::ScrollUp => {
let msg = MsgIn::External(ExternalMsg::FocusPrevious);
tx_msg_in.send(Task::new(msg, None)).unwrap();
}
pub(crate) fn stop(&self) {
if let Some((stopper, ack)) = &self.stopper {
stopper.send(true).unwrap();
ack.recv().unwrap();
}
}
}
MouseEventKind::ScrollDown => {
let msg = MsgIn::External(ExternalMsg::FocusNext);
tx_msg_in.send(Task::new(msg, None)).unwrap();
}
_ => {}
},
fn keep_reading(tx_msg_in: Sender<Task>, rx_stopper: Receiver<bool>, tx_ack: Sender<()>) {
loop {
if rx_stopper.try_recv().unwrap_or(false) {
tx_ack.send(()).unwrap();
break;
} else if event::poll(std::time::Duration::from_millis(150)).unwrap_or_default() {
// NOTE: The poll timeout need to stay low, else spawning sub subshell
// and start typing immediately will cause panic.
// To reproduce, press `:`, then press and hold `!`.
match event::read() {
Ok(Event::Key(key)) => {
let key = Key::from_event(key);
let msg = MsgIn::Internal(InternalMsg::HandleKey(key));
tx_msg_in.send(Task::new(msg, Some(key))).unwrap();
}
Ok(Event::Resize(_, _)) => {
let msg = MsgIn::External(ExternalMsg::Refresh);
Ok(Event::Mouse(evt)) => match evt.kind {
MouseEventKind::ScrollUp => {
let msg = MsgIn::External(ExternalMsg::FocusPrevious);
tx_msg_in.send(Task::new(msg, None)).unwrap();
}
Err(e) => {
tx_msg_in
.send(Task::new(
MsgIn::External(ExternalMsg::LogError(e.to_string())),
None,
))
.unwrap();
MouseEventKind::ScrollDown => {
let msg = MsgIn::External(ExternalMsg::FocusNext);
tx_msg_in.send(Task::new(msg, None)).unwrap();
}
_ => {}
},
Ok(Event::Resize(_, _)) => {
let msg = MsgIn::External(ExternalMsg::Refresh);
tx_msg_in.send(Task::new(msg, None)).unwrap();
}
Err(e) => {
tx_msg_in
.send(Task::new(
MsgIn::External(ExternalMsg::LogError(e.to_string())),
None,
))
.unwrap();
}
}
}
});
}
}

@ -133,25 +133,25 @@ mod test {
assert!(check_version(VERSION, "foo path").is_ok());
// Current release if OK
assert!(check_version("0.15.0", "foo path").is_ok());
assert!(check_version("0.15.1", "foo path").is_ok());
// Prev major release is ERR
// - Not yet
// Prev minor release is ERR (Change when we get to v1)
assert!(check_version("0.14.0", "foo path").is_err());
assert!(check_version("0.14.1", "foo path").is_err());
// Prev bugfix release is OK
// assert!(check_version("0.15.-1", "foo path").is_ok());
assert!(check_version("0.15.0", "foo path").is_ok());
// Next major release is ERR
assert!(check_version("1.15.0", "foo path").is_err());
assert!(check_version("1.15.1", "foo path").is_err());
// Next minor release is ERR
assert!(check_version("0.16.7", "foo path").is_err());
assert!(check_version("0.16.1", "foo path").is_err());
// Next bugfix release is ERR (Change when we get to v1)
assert!(check_version("0.15.8", "foo path").is_err());
assert!(check_version("0.15.2", "foo path").is_err());
}
#[test]

@ -2,7 +2,7 @@
use crate::app;
use crate::cli::Cli;
use crate::event_reader;
use crate::event_reader::EventReader;
use crate::explorer;
use crate::lua;
use crate::pipe_reader;
@ -146,8 +146,6 @@ impl Runner {
fs::create_dir_all(app.session_path())?;
let (tx_msg_in, rx_msg_in) = mpsc::channel();
let (tx_event_reader, rx_event_reader) = mpsc::channel();
let (tx_loop_waiter, rx_loop_waiter) = mpsc::channel();
let (tx_pwd_watcher, rx_pwd_watcher) = mpsc::channel();
app = app.explore_pwd()?;
@ -208,8 +206,9 @@ impl Runner {
terminal.hide_cursor()?;
// Threads
event_reader::keep_reading(tx_msg_in.clone(), rx_event_reader, tx_loop_waiter);
pwd_watcher::keep_watching(app.pwd(), tx_msg_in.clone(), rx_pwd_watcher)?;
let mut event_reader = EventReader::new(tx_msg_in.clone());
event_reader.start();
// Enqueue on_load messages
for msg in self.on_load {
@ -435,7 +434,7 @@ impl Runner {
execute!(terminal.backend_mut(), event::DisableMouseCapture)
.unwrap_or_default();
event_reader::pause_reading(&tx_event_reader, &rx_loop_waiter)?;
event_reader.stop();
terminal.clear()?;
terminal.set_cursor(0, 0)?;
@ -460,7 +459,7 @@ impl Runner {
terminal.clear()?;
term::enable_raw_mode()?;
terminal.hide_cursor()?;
event_reader::resume_reading(&tx_event_reader, &rx_loop_waiter)?;
event_reader.start();
if mouse_enabled {
match execute!(
@ -481,7 +480,7 @@ impl Runner {
execute!(terminal.backend_mut(), event::DisableMouseCapture)
.unwrap_or_default();
event_reader::pause_reading(&tx_event_reader, &rx_loop_waiter)?;
event_reader.stop();
terminal.clear()?;
terminal.set_cursor(0, 0)?;
@ -522,7 +521,7 @@ impl Runner {
terminal.clear()?;
term::enable_raw_mode()?;
terminal.hide_cursor()?;
event_reader::resume_reading(&tx_event_reader, &rx_loop_waiter)?;
event_reader.start();
if mouse_enabled {
match execute!(

Loading…
Cancel
Save