From f7edfecba946e6051aa07b4b952a67cf2b098dba Mon Sep 17 00:00:00 2001 From: Ferdinand Schober Date: Thu, 11 Apr 2024 13:53:49 +0200 Subject: [PATCH] add tests for capture and emulation --- src/capture.rs | 2 +- src/capture_test.rs | 42 +++++++++++++++++++++++++++++++++++++++ src/config.rs | 14 +++++++++++++ src/emulation_test.rs | 46 +++++++++++++++++++++++++++++++++++++++++++ src/event.rs | 10 +++++++++- src/lib.rs | 2 ++ src/main.rs | 8 ++++++-- 7 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/capture_test.rs create mode 100644 src/emulation_test.rs diff --git a/src/capture.rs b/src/capture.rs index 02392c5..d3b99db 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -25,7 +25,7 @@ pub mod x11; /// fallback input capture (does not produce events) pub mod dummy; -pub async fn create() -> Box { +pub async fn create() -> Box>> { #[cfg(target_os = "macos")] match macos::MacOSInputCapture::new() { Ok(p) => return Box::new(p), diff --git a/src/capture_test.rs b/src/capture_test.rs new file mode 100644 index 0000000..584ab97 --- /dev/null +++ b/src/capture_test.rs @@ -0,0 +1,42 @@ +use crate::capture; +use crate::client::{ClientEvent, Position}; +use crate::event::{Event, KeyboardEvent}; +use anyhow::{anyhow, Result}; +use futures::StreamExt; +use tokio::task::LocalSet; + +pub fn run() -> Result<()> { + log::info!("running input capture test"); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build()?; + + runtime.block_on(LocalSet::new().run_until(input_capture_test())) +} + +async fn input_capture_test() -> Result<()> { + log::info!("creating input capture"); + let mut input_capture = capture::create().await; + log::info!("creating clients"); + input_capture.notify(ClientEvent::Create(0, Position::Left))?; + input_capture.notify(ClientEvent::Create(1, Position::Right))?; + input_capture.notify(ClientEvent::Create(2, Position::Top))?; + input_capture.notify(ClientEvent::Create(3, Position::Bottom))?; + loop { + let (client, event) = input_capture + .next() + .await + .ok_or(anyhow!("capture stream closed"))??; + let pos = match client { + 0 => Position::Left, + 1 => Position::Right, + 2 => Position::Top, + _ => Position::Bottom, + }; + log::info!("position: {pos}, event: {event}"); + if let Event::Keyboard(KeyboardEvent::Key { key: 1, .. }) = event { + input_capture.release()?; + } + } +} diff --git a/src/config.rs b/src/config.rs index a50dfa1..d5c855f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -59,6 +59,14 @@ struct CliArgs { /// run only the service as a daemon without the frontend #[arg(short, long)] daemon: bool, + + /// test input capture + #[arg(long)] + test_capture: bool, + + /// test input emulation + #[arg(long)] + test_emulation: bool, } #[derive(Debug, PartialEq, Eq)] @@ -74,6 +82,8 @@ pub struct Config { pub clients: Vec<(TomlClient, Position)>, pub daemon: bool, pub release_bind: Vec, + pub test_capture: bool, + pub test_emulation: bool, } pub struct ConfigClient { @@ -169,6 +179,8 @@ impl Config { } let daemon = args.daemon; + let test_capture = args.test_capture; + let test_emulation = args.test_emulation; Ok(Config { daemon, @@ -176,6 +188,8 @@ impl Config { clients, port, release_bind, + test_capture, + test_emulation, }) } diff --git a/src/emulation_test.rs b/src/emulation_test.rs new file mode 100644 index 0000000..a544b25 --- /dev/null +++ b/src/emulation_test.rs @@ -0,0 +1,46 @@ +use std::f64::consts::PI; +use std::time::{Duration, Instant}; +use anyhow::Result; +use tokio::task::LocalSet; +use crate::client::{ClientEvent, Position}; +use crate::emulate; +use crate::event::{Event, PointerEvent}; + +pub fn run() -> Result<()> { + log::info!("running input emulation test"); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build()?; + + runtime.block_on(LocalSet::new().run_until(input_emulation_test())) +} + +const FREQUENCY_HZ: f64 = 1.0; +const RADIUS: f64 = 100.0; + +async fn input_emulation_test() -> Result<()> { + let mut emulation = emulate::create().await; + emulation.notify(ClientEvent::Create(0, Position::Left)).await; + let start = Instant::now(); + let mut offset = (0, 0); + loop { + tokio::select! { + _ = emulation.dispatch() => {} + _ = tokio::time::sleep(Duration::from_millis(1)) => { + let elapsed = start.elapsed(); + let elapsed_sec_f64 = elapsed.as_secs_f64(); + let second_fraction = elapsed_sec_f64 - elapsed_sec_f64 as u64 as f64; + let radians = second_fraction * 2. * PI * FREQUENCY_HZ; + let new_offset_f = (radians.cos() * RADIUS * 2., (radians * 2.).sin() * RADIUS); + let new_offset = (new_offset_f.0 as i32, new_offset_f.1 as i32); + if new_offset != offset { + let relative_motion = (new_offset.0 - offset.0, new_offset.1 - offset.1); + offset = new_offset; + let (relative_x, relative_y) = (relative_motion.0 as f64, relative_motion.1 as f64); + emulation.consume(Event::Pointer(PointerEvent::Motion {time: 0, relative_x, relative_y }), 0).await; + } + } + } + } +} diff --git a/src/event.rs b/src/event.rs index 2de64f7..e785587 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,3 +1,4 @@ +use crate::scancode; use anyhow::{anyhow, Result}; use std::{ error::Error, @@ -101,7 +102,14 @@ impl Display for KeyboardEvent { time: _, key, state, - } => write!(f, "key({key}, {state})"), + } => { + let scan = scancode::Linux::try_from(*key); + if let Ok(scan) = scan { + write!(f, "key({scan:?}, {state})") + } else { + write!(f, "key({key}, {state})") + } + } KeyboardEvent::Modifiers { mods_depressed, mods_latched, diff --git a/src/lib.rs b/src/lib.rs index ed36a52..7e756ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,5 +7,7 @@ pub mod server; pub mod capture; pub mod emulate; +pub mod capture_test; +pub mod emulation_test; pub mod frontend; pub mod scancode; diff --git a/src/main.rs b/src/main.rs index 2cfc7e7..48bb136 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::Result; use std::process::{self, Child, Command}; use env_logger::Env; -use lan_mouse::{config::Config, frontend, server::Server}; +use lan_mouse::{capture_test, config::Config, emulation_test, frontend, server::Server}; use tokio::task::LocalSet; @@ -31,7 +31,11 @@ pub fn run() -> Result<()> { log::debug!("{config:?}"); log::info!("release bind: {:?}", config.release_bind); - if config.daemon { + if config.test_capture { + capture_test::run()?; + } else if config.test_emulation { + emulation_test::run()?; + } else if config.daemon { // if daemon is specified we run the service run_service(&config)?; } else {