feat: Acutal fix the the mouse events output

The EnableMouseCapture from Crossterm was too broad, by only enabling a subject of the events, 1) performance is improvedand 2) and intermittent bug where mouse events were output to stdout has been removed
This commit is contained in:
Jack Wills
2023-03-02 01:09:17 +00:00
parent e650034d50
commit 0a1b531116
5 changed files with 32 additions and 43 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ COPY ./target/x86_64-unknown-linux-musl/release/oxker /app/
ENTRYPOINT [ "/app/oxker"] ENTRYPOINT [ "/app/oxker"]
# Dev build for testing # Dev build for testing
# docker build -t oxker_dev -f Dockerfile . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev # docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev
# Dev build one liner, x86 host # Dev build one liner, x86 host
# docker image prune -a; cargo build --release --target x86_64-unknown-linux-musl && docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev # docker image prune -a; cargo build --release --target x86_64-unknown-linux-musl && docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev
+2 -2
View File
@@ -16,7 +16,6 @@ use crate::{
app_data::{AppData, ContainerId, DockerControls}, app_data::{AppData, ContainerId, DockerControls},
app_error::AppError, app_error::AppError,
parse_args::CliArgs, parse_args::CliArgs,
stop_running,
ui::{GuiState, Status}, ui::{GuiState, Status},
ENTRY_POINT, ENTRY_POINT,
}; };
@@ -405,7 +404,8 @@ impl DockerData {
.values() .values()
.into_iter() .into_iter()
.for_each(tokio::task::JoinHandle::abort); .for_each(tokio::task::JoinHandle::abort);
stop_running(&self.is_running); self.is_running
.store(false, std::sync::atomic::Ordering::SeqCst);
} }
} }
} }
+7 -14
View File
@@ -4,9 +4,7 @@ use std::sync::{
}; };
use crossterm::{ use crossterm::{
event::{ event::{DisableMouseCapture, KeyCode, MouseButton, MouseEvent, MouseEventKind},
DisableMouseCapture, EnableMouseCapture, KeyCode, MouseButton, MouseEvent, MouseEventKind,
},
execute, execute,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -21,8 +19,7 @@ use crate::{
app_data::{AppData, DockerControls, Header}, app_data::{AppData, DockerControls, Header},
app_error::AppError, app_error::AppError,
docker_data::DockerMessage, docker_data::DockerMessage,
stop_running, ui::{enable_mouse_capture, GuiState, SelectablePanel, Status},
ui::{GuiState, SelectablePanel, Status},
}; };
pub use message::InputMessages; pub use message::InputMessages;
@@ -95,15 +92,10 @@ impl InputHandler {
} }
} }
} else { } else {
match execute!(std::io::stdout(), EnableMouseCapture) { enable_mouse_capture();
Ok(_) => self self.gui_state
.gui_state
.lock() .lock()
.set_info_box("✓ mouse capture enabled".to_owned()), .set_info_box("✓ mouse capture enabled".to_owned());
Err(_) => {
self.app_data.lock().set_error(AppError::MouseCapture(true));
}
}
}; };
// If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window // If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window
@@ -135,7 +127,8 @@ impl InputHandler {
.lock() .lock()
.status_contains(&[Status::Error, Status::Init]); .status_contains(&[Status::Error, Status::Init]);
if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() { if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() {
stop_running(&self.is_running); self.is_running
.store(false, std::sync::atomic::Ordering::SeqCst);
} }
} }
-13
View File
@@ -19,10 +19,6 @@
use app_data::AppData; use app_data::AppData;
use app_error::AppError; use app_error::AppError;
use bollard::Docker; use bollard::Docker;
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
};
use docker_data::DockerData; use docker_data::DockerData;
use input_handler::InputMessages; use input_handler::InputMessages;
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -67,15 +63,6 @@ fn check_if_containerised() -> bool {
} }
} }
/// Set is_running to false, disable mouse capture, due to weird bug on Linux & WSL terminals
/// where mouse movement will be piped to the stdout
fn stop_running(is_running: &AtomicBool) {
std::thread::spawn(|| {
execute!(std::io::stdout(), EnableMouseCapture).unwrap_or(());
execute!(std::io::stdout(), DisableMouseCapture).unwrap_or(());
});
is_running.store(false, std::sync::atomic::Ordering::SeqCst);
}
/// Create docker daemon handler, and only spawn up the docker data handler if a ping returns non-error /// Create docker daemon handler, and only spawn up the docker data handler if a ping returns non-error
async fn docker_init( async fn docker_init(
app_data: &Arc<Mutex<AppData>>, app_data: &Arc<Mutex<AppData>>,
+21 -12
View File
@@ -1,12 +1,12 @@
use anyhow::Result; use anyhow::Result;
use crossterm::{ use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event}, event::{self, DisableMouseCapture, Event},
execute, execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
io::{self, Stdout}, io::{self, Stdout, Write},
sync::{atomic::Ordering, Arc}, sync::{atomic::Ordering, Arc},
}; };
use std::{sync::atomic::AtomicBool, time::Instant}; use std::{sync::atomic::AtomicBool, time::Instant};
@@ -39,6 +39,20 @@ pub struct Ui {
terminal: Terminal<CrosstermBackend<Stdout>>, terminal: Terminal<CrosstermBackend<Stdout>>,
} }
/// Enable moust capture, but don't enable all the mouse movements, which improves performance, and fixes the weird mouse event output bug
pub fn enable_mouse_capture() {
io::stdout()
.write_all(
concat!(
crossterm::csi!("?1000h"),
crossterm::csi!("?1015h"),
crossterm::csi!("?1006h"),
)
.as_bytes(),
)
.unwrap_or(());
}
impl Ui { impl Ui {
/// Create a new Ui struct, and execute the drawing loop /// Create a new Ui struct, and execute the drawing loop
pub async fn create( pub async fn create(
@@ -73,7 +87,8 @@ impl Ui {
fn setup_terminal() -> io::Result<Terminal<CrosstermBackend<Stdout>>> { fn setup_terminal() -> io::Result<Terminal<CrosstermBackend<Stdout>>> {
enable_raw_mode()?; enable_raw_mode()?;
let mut stdout = io::stdout(); let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; execute!(stdout, EnterAlternateScreen)?;
enable_mouse_capture();
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
Terminal::new(backend) Terminal::new(backend)
} }
@@ -96,12 +111,6 @@ impl Ui {
fn err_loop(&mut self) -> Result<(), AppError> { fn err_loop(&mut self) -> Result<(), AppError> {
let mut seconds = 5; let mut seconds = 5;
loop { loop {
// This is a fix for a weird bug on Linux + WSL which will output mouse movement to stdout
std::thread::spawn(|| {
execute!(io::stdout(), EnableMouseCapture).unwrap_or(());
execute!(io::stdout(), DisableMouseCapture).unwrap_or(());
});
if self.now.elapsed() >= std::time::Duration::from_secs(1) { if self.now.elapsed() >= std::time::Duration::from_secs(1) {
seconds -= 1; seconds -= 1;
self.now = Instant::now(); self.now = Instant::now();
@@ -124,14 +133,14 @@ impl Ui {
/// The loop for drawing the main UI to the terminal /// The loop for drawing the main UI to the terminal
async fn gui_loop(&mut self) -> Result<(), AppError> { async fn gui_loop(&mut self) -> Result<(), AppError> {
let input_poll_rate = std::time::Duration::from_millis(1); let input_poll_rate = std::time::Duration::from_millis(100);
let update_duration = let update_duration =
std::time::Duration::from_millis(u64::from(self.app_data.lock().args.docker_interval)); std::time::Duration::from_millis(u64::from(self.app_data.lock().args.docker_interval));
while self.is_running.load(Ordering::SeqCst) { while self.is_running.load(Ordering::SeqCst) {
if self if self
.terminal .terminal
.draw(|frame| ui_frame(frame, &self.app_data, &self.gui_state)) .draw(|frame| draw_frame(frame, &self.app_data, &self.gui_state))
.is_err() .is_err()
{ {
return Err(AppError::Terminal); return Err(AppError::Terminal);
@@ -183,7 +192,7 @@ impl Ui {
} }
/// Draw the main ui to a frame of the terminal /// Draw the main ui to a frame of the terminal
fn ui_frame<B: Backend>( fn draw_frame<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app_data: &Arc<Mutex<AppData>>, app_data: &Arc<Mutex<AppData>>,
gui_state: &Arc<Mutex<GuiState>>, gui_state: &Arc<Mutex<GuiState>>,