diff --git a/Cargo.lock b/Cargo.lock index f3d3ddb..915a8b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,11 +1242,11 @@ dependencies = [ name = "input-event" version = "0.1.0" dependencies = [ - "anyhow", "futures-core", "log", "num_enum", "serde", + "thiserror", ] [[package]] diff --git a/input-capture/src/libei.rs b/input-capture/src/libei.rs index 84b9099..d2e84ef 100644 --- a/input-capture/src/libei.rs +++ b/input-capture/src/libei.rs @@ -440,8 +440,8 @@ async fn handle_ei_event( EiEvent::PointerMotion(motion) => { let motion_event = PointerEvent::Motion { time: motion.time as u32, - relative_x: motion.dx as f64, - relative_y: motion.dy as f64, + dx: motion.dx as f64, + dy: motion.dy as f64, }; if let Some(current_client) = current_client { event_tx diff --git a/input-capture/src/wayland.rs b/input-capture/src/wayland.rs index d11030c..b349238 100644 --- a/input-capture/src/wayland.rs +++ b/input-capture/src/wayland.rs @@ -849,8 +849,8 @@ impl Dispatch for State { *client, Event::Pointer(PointerEvent::Motion { time, - relative_x: surface_x, - relative_y: surface_y, + dx: surface_x, + dy: surface_y, }), )); } diff --git a/input-emulation/src/error.rs b/input-emulation/src/error.rs index 0d5b025..6e85ae9 100644 --- a/input-emulation/src/error.rs +++ b/input-emulation/src/error.rs @@ -1,6 +1,7 @@ -use std::{fmt::Display, io}; - +use reis::tokio::EiConvertEventStreamError; +use std::io; use thiserror::Error; + #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] use wayland_client::{ backend::WaylandError, @@ -11,11 +12,29 @@ use wayland_client::{ #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] use reis::tokio::HandshakeError; +#[cfg(all(unix, feature = "libei", not(target_os = "macos")))] +#[derive(Debug, Error)] +#[error("error in libei stream: {inner:?}")] +pub struct ReisConvertStreamError { + inner: EiConvertEventStreamError, +} + +impl From for ReisConvertStreamError { + fn from(e: EiConvertEventStreamError) -> Self { + Self { inner: e } + } +} + #[derive(Debug, Error)] pub enum EmulationError { + #[error("event stream closed")] + EndOfStream, #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] #[error("libei error flushing events: `{0}`")] Libei(#[from] reis::event::Error), + #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] + #[error("")] + LibeiConvertStream(#[from] ReisConvertStreamError), #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] #[error("wayland error: `{0}`")] Wayland(#[from] wayland_client::backend::WaylandError), @@ -33,58 +52,52 @@ pub enum EmulationError { #[derive(Debug, Error)] pub enum EmulationCreationError { #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] + #[error("wlroots backend: `{0}`")] Wlroots(#[from] WlrootsEmulationCreationError), #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] + #[error("libei backend: `{0}`")] Libei(#[from] LibeiEmulationCreationError), #[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))] + #[error("xdg-desktop-portal: `{0}`")] Xdp(#[from] XdpEmulationCreationError), #[cfg(all(unix, feature = "x11", not(target_os = "macos")))] + #[error("x11: `{0}`")] X11(#[from] X11EmulationCreationError), #[cfg(target_os = "macos")] + #[error("macos: `{0}`")] MacOs(#[from] MacOSEmulationCreationError), #[cfg(windows)] + #[error("windows: `{0}`")] Windows(#[from] WindowsEmulationCreationError), + #[error("capture error")] NoAvailableBackend, } -impl Display for EmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let reason = match self { - #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] - EmulationCreationError::Wlroots(e) => format!("wlroots backend: {e}"), - #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] - EmulationCreationError::Libei(e) => format!("libei backend: {e}"), - #[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))] - EmulationCreationError::Xdp(e) => format!("desktop portal backend: {e}"), - #[cfg(all(unix, feature = "x11", not(target_os = "macos")))] - EmulationCreationError::X11(e) => format!("x11 backend: {e}"), - #[cfg(target_os = "macos")] - EmulationCreationError::MacOs(e) => format!("macos backend: {e}"), - #[cfg(windows)] - EmulationCreationError::Windows(e) => format!("windows backend: {e}"), - EmulationCreationError::NoAvailableBackend => "no backend available".to_string(), - }; - write!(f, "could not create input emulation backend: {reason}") - } -} - #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] #[derive(Debug, Error)] pub enum WlrootsEmulationCreationError { + #[error(transparent)] Connect(#[from] ConnectError), + #[error(transparent)] Global(#[from] GlobalError), + #[error(transparent)] Wayland(#[from] WaylandError), + #[error(transparent)] Bind(#[from] WaylandBindError), + #[error(transparent)] Dispatch(#[from] DispatchError), + #[error(transparent)] Io(#[from] std::io::Error), } #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] #[derive(Debug, Error)] +#[error("wayland protocol \"{protocol}\" not supported: {inner}")] pub struct WaylandBindError { inner: BindError, protocol: &'static str, } + #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] impl WaylandBindError { pub(crate) fn new(inner: BindError, protocol: &'static str) -> Self { @@ -92,101 +105,38 @@ impl WaylandBindError { } } -#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] -impl Display for WaylandBindError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} protocol not supported: {}", - self.protocol, self.inner - ) - } -} - -#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] -impl Display for WlrootsEmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - WlrootsEmulationCreationError::Bind(e) => write!(f, "{e}"), - WlrootsEmulationCreationError::Connect(e) => { - write!(f, "could not connect to wayland compositor: {e}") - } - WlrootsEmulationCreationError::Global(e) => write!(f, "wayland error: {e}"), - WlrootsEmulationCreationError::Wayland(e) => write!(f, "wayland error: {e}"), - WlrootsEmulationCreationError::Dispatch(e) => { - write!(f, "error dispatching wayland events: {e}") - } - WlrootsEmulationCreationError::Io(e) => write!(f, "io error: {e}"), - } - } -} - #[cfg(all(unix, feature = "libei", not(target_os = "macos")))] #[derive(Debug, Error)] pub enum LibeiEmulationCreationError { + #[error(transparent)] Ashpd(#[from] ashpd::Error), + #[error(transparent)] Io(#[from] std::io::Error), + #[error(transparent)] Handshake(#[from] HandshakeError), } -#[cfg(all(unix, feature = "libei", not(target_os = "macos")))] -impl Display for LibeiEmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - LibeiEmulationCreationError::Ashpd(e) => write!(f, "xdg-desktop-portal: {e}"), - LibeiEmulationCreationError::Io(e) => write!(f, "io error: {e}"), - LibeiEmulationCreationError::Handshake(e) => write!(f, "error in libei handshake: {e}"), - } - } -} - #[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))] #[derive(Debug, Error)] pub enum XdpEmulationCreationError { + #[error(transparent)] Ashpd(#[from] ashpd::Error), } -#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))] -impl Display for XdpEmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - XdpEmulationCreationError::Ashpd(e) => write!(f, "portal error: {e}"), - } - } -} - #[cfg(all(unix, feature = "x11", not(target_os = "macos")))] #[derive(Debug, Error)] pub enum X11EmulationCreationError { + #[error("could not open display")] OpenDisplay, } -#[cfg(all(unix, feature = "x11", not(target_os = "macos")))] -impl Display for X11EmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - X11EmulationCreationError::OpenDisplay => write!(f, "could not open display!"), - } - } -} - #[cfg(target_os = "macos")] #[derive(Debug, Error)] pub enum MacOSEmulationCreationError { + #[error("could not create event source")] EventSourceCreation, } -#[cfg(target_os = "macos")] -impl Display for MacOSEmulationCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - MacOSEmulationCreationError::EventSourceCreation => { - write!(f, "could not create event source") - } - } - } -} - #[cfg(windows)] #[derive(Debug, Error)] pub enum WindowsEmulationCreationError {} diff --git a/input-emulation/src/lib.rs b/input-emulation/src/lib.rs index 17a8c7d..d05aff8 100644 --- a/input-emulation/src/lib.rs +++ b/input-emulation/src/lib.rs @@ -4,8 +4,6 @@ use std::fmt::Display; use input_event::Event; -use anyhow::Result; - use self::error::EmulationCreationError; #[cfg(windows)] diff --git a/input-emulation/src/libei.rs b/input-emulation/src/libei.rs index d417431..6dc92a0 100644 --- a/input-emulation/src/libei.rs +++ b/input-emulation/src/libei.rs @@ -1,4 +1,3 @@ -use anyhow::{anyhow, Result}; use futures::StreamExt; use once_cell::sync::Lazy; use std::{ @@ -33,7 +32,7 @@ use reis::{ use input_event::{Event, KeyboardEvent, PointerEvent}; -use crate::error::EmulationError; +use crate::error::{EmulationError, ReisConvertStreamError}; use super::{error::LibeiEmulationCreationError, EmulationHandle, InputEmulation}; @@ -65,7 +64,7 @@ pub struct LibeiEmulation { context: ei::Context, devices: Devices, serial: AtomicU32, - ei_task: JoinHandle>, + ei_task: JoinHandle>, } async fn get_ei_fd() -> Result { @@ -149,14 +148,10 @@ impl InputEmulation for LibeiEmulation { .as_micros() as u64; match event { Event::Pointer(p) => match p { - PointerEvent::Motion { - time: _, - relative_x, - relative_y, - } => { + PointerEvent::Motion { time: _, dx, dy } => { let pointer_device = self.devices.pointer.read().unwrap(); if let Some((d, p)) = pointer_device.as_ref() { - p.motion_relative(relative_x as f32, relative_y as f32); + p.motion_relative(dx as f32, dy as f32); d.frame(self.serial.load(Ordering::SeqCst), now); } } @@ -239,13 +234,13 @@ async fn ei_event_handler( mut events: EiConvertEventStream, context: ei::Context, devices: Devices, -) -> Result<()> { +) -> Result<(), EmulationError> { loop { let event = events .next() .await - .ok_or(anyhow!("ei stream closed"))? - .map_err(|e| anyhow!("libei error: {e:?}"))?; + .ok_or(EmulationError::EndOfStream)? + .map_err(ReisConvertStreamError::from)?; const CAPABILITIES: &[DeviceCapability] = &[ DeviceCapability::Pointer, DeviceCapability::PointerAbsolute, @@ -330,7 +325,7 @@ async fn ei_event_handler( // EiEvent::TouchMotion(_) => { }, _ => unreachable!("unexpected ei event"), } - context.flush()?; + context.flush().map_err(|e| io::Error::new(e.kind(), e))?; } Ok(()) } diff --git a/input-emulation/src/macos.rs b/input-emulation/src/macos.rs index ee4afe6..392aec1 100644 --- a/input-emulation/src/macos.rs +++ b/input-emulation/src/macos.rs @@ -114,11 +114,7 @@ impl InputEmulation for MacOSEmulation { ) -> Result<(), EmulationError> { match event { Event::Pointer(pointer_event) => match pointer_event { - PointerEvent::Motion { - time: _, - relative_x, - relative_y, - } => { + PointerEvent::Motion { time: _, dx, dy } => { // FIXME secondary displays? let (min_x, min_y, max_x, max_y) = unsafe { let display = CGMainDisplayID(); @@ -137,8 +133,8 @@ impl InputEmulation for MacOSEmulation { } }; - mouse_location.x = (mouse_location.x + relative_x).clamp(min_x, max_x - 1.); - mouse_location.y = (mouse_location.y + relative_y).clamp(min_y, max_y - 1.); + mouse_location.x = (mouse_location.x + dx).clamp(min_x, max_x - 1.); + mouse_location.y = (mouse_location.y + dy).clamp(min_y, max_y - 1.); let mut event_type = CGEventType::MouseMoved; if self.button_state.left { @@ -160,14 +156,8 @@ impl InputEmulation for MacOSEmulation { return Ok(()); } }; - event.set_integer_value_field( - EventField::MOUSE_EVENT_DELTA_X, - relative_x as i64, - ); - event.set_integer_value_field( - EventField::MOUSE_EVENT_DELTA_Y, - relative_y as i64, - ); + event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_X, dx as i64); + event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_Y, dy as i64); event.post(CGEventTapLocation::HID); } PointerEvent::Button { diff --git a/input-emulation/src/windows.rs b/input-emulation/src/windows.rs index 1ec0b74..3276769 100644 --- a/input-emulation/src/windows.rs +++ b/input-emulation/src/windows.rs @@ -39,12 +39,8 @@ impl InputEmulation for WindowsEmulation { async fn consume(&mut self, event: Event, _: EmulationHandle) -> Result<(), EmulationError> { match event { Event::Pointer(pointer_event) => match pointer_event { - PointerEvent::Motion { - time: _, - relative_x, - relative_y, - } => { - rel_mouse(relative_x as i32, relative_y as i32); + PointerEvent::Motion { time: _, dx, dy } => { + rel_mouse(dx as i32, dy as i32); } PointerEvent::Button { time: _, diff --git a/input-emulation/src/wlroots.rs b/input-emulation/src/wlroots.rs index 4423aba..0abb61e 100644 --- a/input-emulation/src/wlroots.rs +++ b/input-emulation/src/wlroots.rs @@ -179,8 +179,8 @@ impl VirtualInput { match e { PointerEvent::Motion { time, - relative_x, - relative_y, + dx: relative_x, + dy: relative_y, } => self.pointer.motion(time, relative_x, relative_y), PointerEvent::Button { time, diff --git a/input-emulation/src/x11.rs b/input-emulation/src/x11.rs index 5470cac..790466a 100644 --- a/input-emulation/src/x11.rs +++ b/input-emulation/src/x11.rs @@ -103,12 +103,8 @@ impl InputEmulation for X11Emulation { async fn consume(&mut self, event: Event, _: EmulationHandle) -> Result<(), EmulationError> { match event { Event::Pointer(pointer_event) => match pointer_event { - PointerEvent::Motion { - time: _, - relative_x, - relative_y, - } => { - self.relative_motion(relative_x as i32, relative_y as i32); + PointerEvent::Motion { time: _, dx, dy } => { + self.relative_motion(dx as i32, dy as i32); } PointerEvent::Button { time: _, diff --git a/input-emulation/src/xdg_desktop_portal.rs b/input-emulation/src/xdg_desktop_portal.rs index b0f7bf3..ad25107 100644 --- a/input-emulation/src/xdg_desktop_portal.rs +++ b/input-emulation/src/xdg_desktop_portal.rs @@ -70,13 +70,9 @@ impl<'a> InputEmulation for DesktopPortalEmulation<'a> { ) -> Result<(), EmulationError> { match event { Pointer(p) => match p { - PointerEvent::Motion { - time: _, - relative_x, - relative_y, - } => { + PointerEvent::Motion { time: _, dx, dy } => { self.proxy - .notify_pointer_motion(&self.session, relative_x, relative_y) + .notify_pointer_motion(&self.session, dx, dy) .await?; } PointerEvent::Button { diff --git a/input-event/Cargo.toml b/input-event/Cargo.toml index fb486e1..82d6f6b 100644 --- a/input-event/Cargo.toml +++ b/input-event/Cargo.toml @@ -7,8 +7,8 @@ license = "GPL-3.0-or-later" repository = "https://github.com/ferdinandschober/lan-mouse" [dependencies] -anyhow = "1.0.86" futures-core = "0.3.30" log = "0.4.22" num_enum = "0.7.2" serde = { version = "1.0", features = ["derive"] } +thiserror = "1.0.61" diff --git a/input-event/src/error.rs b/input-event/src/error.rs new file mode 100644 index 0000000..8700adf --- /dev/null +++ b/input-event/src/error.rs @@ -0,0 +1,17 @@ +use std::array::TryFromSliceError; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ProtocolError { + #[error(transparent)] + MissingData(#[from] TryFromSliceError), + #[error("invalid event id: `{0}`")] + InvalidEventId(u8), + #[error("invalid pointer event type: `{0}`")] + InvalidPointerEventId(u8), + #[error("invalid keyboard event type: `{0}`")] + InvalidKeyboardEventId(u8), + #[error("expected data at idx `{0:?}`")] + Data(String), +} diff --git a/input-event/src/lib.rs b/input-event/src/lib.rs index db31a25..5d9bf0b 100644 --- a/input-event/src/lib.rs +++ b/input-event/src/lib.rs @@ -1,9 +1,8 @@ -use anyhow::{anyhow, Result}; -use std::{ - error::Error, - fmt::{self, Display}, -}; +pub use error::ProtocolError; +use std::fmt::{self, Display}; +pub mod error; +pub mod proto; pub mod scancode; // FIXME @@ -15,35 +14,23 @@ pub const BTN_FORWARD: u32 = 0x114; #[derive(Debug, PartialEq, Clone, Copy)] pub enum PointerEvent { - Motion { - time: u32, - relative_x: f64, - relative_y: f64, - }, - Button { - time: u32, - button: u32, - state: u32, - }, - Axis { - time: u32, - axis: u8, - value: f64, - }, - AxisDiscrete120 { - axis: u8, - value: i32, - }, + /// relative motion event + Motion { time: u32, dx: f64, dy: f64 }, + /// mouse button event + Button { time: u32, button: u32, state: u32 }, + /// axis event, scroll event for touchpads + Axis { time: u32, axis: u8, value: f64 }, + /// discrete axis event, scroll event for mice - 120 = one scroll tick + AxisDiscrete120 { axis: u8, value: i32 }, + /// frame event Frame {}, } #[derive(Debug, PartialEq, Clone, Copy)] pub enum KeyboardEvent { - Key { - time: u32, - key: u32, - state: u8, - }, + /// a key press / release event + Key { time: u32, key: u32, state: u8 }, + /// modifiers changed state Modifiers { mods_depressed: u32, mods_latched: u32, @@ -82,8 +69,8 @@ impl Display for PointerEvent { match self { PointerEvent::Motion { time: _, - relative_x, - relative_y, + dx: relative_x, + dy: relative_y, } => write!(f, "motion({relative_x},{relative_y})"), PointerEvent::Button { time: _, @@ -158,430 +145,3 @@ impl Display for Event { } } } - -impl Event { - fn event_type(&self) -> EventType { - match self { - Self::Pointer(_) => EventType::Pointer, - Self::Keyboard(_) => EventType::Keyboard, - Self::Enter() => EventType::Enter, - Self::Leave() => EventType::Leave, - Self::Ping() => EventType::Ping, - Self::Pong() => EventType::Pong, - Self::Disconnect() => EventType::Disconnect, - } - } -} - -impl PointerEvent { - fn event_type(&self) -> PointerEventType { - match self { - Self::Motion { .. } => PointerEventType::Motion, - Self::Button { .. } => PointerEventType::Button, - Self::Axis { .. } => PointerEventType::Axis, - Self::AxisDiscrete120 { .. } => PointerEventType::AxisDiscrete120, - Self::Frame { .. } => PointerEventType::Frame, - } - } -} - -impl KeyboardEvent { - fn event_type(&self) -> KeyboardEventType { - match self { - KeyboardEvent::Key { .. } => KeyboardEventType::Key, - KeyboardEvent::Modifiers { .. } => KeyboardEventType::Modifiers, - } - } -} - -enum PointerEventType { - Motion, - Button, - Axis, - AxisDiscrete120, - Frame, -} -enum KeyboardEventType { - Key, - Modifiers, -} -enum EventType { - Pointer, - Keyboard, - Enter, - Leave, - Ping, - Pong, - Disconnect, -} - -impl TryFrom for PointerEventType { - type Error = anyhow::Error; - - fn try_from(value: u8) -> Result { - match value { - x if x == Self::Motion as u8 => Ok(Self::Motion), - x if x == Self::Button as u8 => Ok(Self::Button), - x if x == Self::Axis as u8 => Ok(Self::Axis), - x if x == Self::AxisDiscrete120 as u8 => Ok(Self::AxisDiscrete120), - x if x == Self::Frame as u8 => Ok(Self::Frame), - _ => Err(anyhow!(ProtocolError { - msg: format!("invalid pointer event type {}", value), - })), - } - } -} - -impl TryFrom for KeyboardEventType { - type Error = anyhow::Error; - - fn try_from(value: u8) -> Result { - match value { - x if x == Self::Key as u8 => Ok(Self::Key), - x if x == Self::Modifiers as u8 => Ok(Self::Modifiers), - _ => Err(anyhow!(ProtocolError { - msg: format!("invalid keyboard event type {}", value), - })), - } - } -} - -impl From<&Event> for Vec { - fn from(event: &Event) -> Self { - let event_id = vec![event.event_type() as u8]; - let event_data = match event { - Event::Pointer(p) => p.into(), - Event::Keyboard(k) => k.into(), - Event::Enter() => vec![], - Event::Leave() => vec![], - Event::Ping() => vec![], - Event::Pong() => vec![], - Event::Disconnect() => vec![], - }; - [event_id, event_data].concat() - } -} - -#[derive(Debug)] -struct ProtocolError { - msg: String, -} - -impl fmt::Display for ProtocolError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Protocol violation: {}", self.msg) - } -} -impl Error for ProtocolError {} - -impl TryFrom> for Event { - type Error = anyhow::Error; - - fn try_from(value: Vec) -> Result { - let event_id = u8::from_be_bytes(value[..1].try_into()?); - match event_id { - i if i == (EventType::Pointer as u8) => Ok(Event::Pointer(value.try_into()?)), - i if i == (EventType::Keyboard as u8) => Ok(Event::Keyboard(value.try_into()?)), - i if i == (EventType::Enter as u8) => Ok(Event::Enter()), - i if i == (EventType::Leave as u8) => Ok(Event::Leave()), - i if i == (EventType::Ping as u8) => Ok(Event::Ping()), - i if i == (EventType::Pong as u8) => Ok(Event::Pong()), - i if i == (EventType::Disconnect as u8) => Ok(Event::Disconnect()), - _ => Err(anyhow!(ProtocolError { - msg: format!("invalid event_id {}", event_id), - })), - } - } -} - -impl From<&PointerEvent> for Vec { - fn from(event: &PointerEvent) -> Self { - let id = vec![event.event_type() as u8]; - let data = match event { - PointerEvent::Motion { - time, - relative_x, - relative_y, - } => { - let time = time.to_be_bytes(); - let relative_x = relative_x.to_be_bytes(); - let relative_y = relative_y.to_be_bytes(); - [&time[..], &relative_x[..], &relative_y[..]].concat() - } - PointerEvent::Button { - time, - button, - state, - } => { - let time = time.to_be_bytes(); - let button = button.to_be_bytes(); - let state = state.to_be_bytes(); - [&time[..], &button[..], &state[..]].concat() - } - PointerEvent::Axis { time, axis, value } => { - let time = time.to_be_bytes(); - let axis = axis.to_be_bytes(); - let value = value.to_be_bytes(); - [&time[..], &axis[..], &value[..]].concat() - } - PointerEvent::AxisDiscrete120 { axis, value } => { - let axis = axis.to_be_bytes(); - let value = value.to_be_bytes(); - [&axis[..], &value[..]].concat() - } - PointerEvent::Frame {} => { - vec![] - } - }; - [id, data].concat() - } -} - -impl TryFrom> for PointerEvent { - type Error = anyhow::Error; - - fn try_from(data: Vec) -> Result { - match data.get(1) { - Some(id) => { - let event_type = match id.to_owned().try_into() { - Ok(event_type) => event_type, - Err(e) => return Err(e), - }; - match event_type { - PointerEventType::Motion => { - let time = match data.get(2..6) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 2".into(), - })) - } - }; - let relative_x = match data.get(6..14) { - Some(d) => f64::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 8 Bytes at index 6".into(), - })) - } - }; - let relative_y = match data.get(14..22) { - Some(d) => f64::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 8 Bytes at index 14".into(), - })) - } - }; - Ok(Self::Motion { - time, - relative_x, - relative_y, - }) - } - PointerEventType::Button => { - let time = match data.get(2..6) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 2".into(), - })) - } - }; - let button = match data.get(6..10) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 10".into(), - })) - } - }; - let state = match data.get(10..14) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 14".into(), - })) - } - }; - Ok(Self::Button { - time, - button, - state, - }) - } - PointerEventType::Axis => { - let time = match data.get(2..6) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 2".into(), - })) - } - }; - let axis = match data.get(6) { - Some(d) => *d, - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 1 Byte at index 6".into(), - })); - } - }; - let value = match data.get(7..15) { - Some(d) => f64::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 8 Bytes at index 7".into(), - })); - } - }; - Ok(Self::Axis { time, axis, value }) - } - PointerEventType::AxisDiscrete120 => { - let axis = match data.get(2) { - Some(d) => *d, - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 1 Byte at index 2".into(), - })); - } - }; - let value = match data.get(3..7) { - Some(d) => i32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 3".into(), - })); - } - }; - Ok(Self::AxisDiscrete120 { axis, value }) - } - PointerEventType::Frame => Ok(Self::Frame {}), - } - } - None => Err(anyhow!(ProtocolError { - msg: "Expected an element at index 0".into(), - })), - } - } -} - -impl From<&KeyboardEvent> for Vec { - fn from(event: &KeyboardEvent) -> Self { - let id = vec![event.event_type() as u8]; - let data = match event { - KeyboardEvent::Key { time, key, state } => { - let time = time.to_be_bytes(); - let key = key.to_be_bytes(); - let state = state.to_be_bytes(); - [&time[..], &key[..], &state[..]].concat() - } - KeyboardEvent::Modifiers { - mods_depressed, - mods_latched, - mods_locked, - group, - } => { - let mods_depressed = mods_depressed.to_be_bytes(); - let mods_latched = mods_latched.to_be_bytes(); - let mods_locked = mods_locked.to_be_bytes(); - let group = group.to_be_bytes(); - [ - &mods_depressed[..], - &mods_latched[..], - &mods_locked[..], - &group[..], - ] - .concat() - } - }; - [id, data].concat() - } -} - -impl TryFrom> for KeyboardEvent { - type Error = anyhow::Error; - - fn try_from(data: Vec) -> Result { - match data.get(1) { - Some(id) => { - let event_type = match id.to_owned().try_into() { - Ok(event_type) => event_type, - Err(e) => return Err(e), - }; - match event_type { - KeyboardEventType::Key => { - let time = match data.get(2..6) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 6".into(), - })) - } - }; - let key = match data.get(6..10) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 10".into(), - })) - } - }; - let state = match data.get(10) { - Some(d) => *d, - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 1 Bytes at index 14".into(), - })) - } - }; - Ok(KeyboardEvent::Key { time, key, state }) - } - KeyboardEventType::Modifiers => { - let mods_depressed = match data.get(2..6) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 6".into(), - })) - } - }; - let mods_latched = match data.get(6..10) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 10".into(), - })) - } - }; - let mods_locked = match data.get(10..14) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 14".into(), - })) - } - }; - let group = match data.get(14..18) { - Some(d) => u32::from_be_bytes(d.try_into()?), - None => { - return Err(anyhow!(ProtocolError { - msg: "Expected 4 Bytes at index 18".into(), - })) - } - }; - Ok(KeyboardEvent::Modifiers { - mods_depressed, - mods_latched, - mods_locked, - group, - }) - } - } - } - None => Err(anyhow!(ProtocolError { - msg: "Expected an element at index 0".into(), - })), - } - } -} diff --git a/input-event/src/proto.rs b/input-event/src/proto.rs new file mode 100644 index 0000000..0ac4aca --- /dev/null +++ b/input-event/src/proto.rs @@ -0,0 +1,311 @@ +use std::{fmt::Debug, slice::SliceIndex}; + +use crate::ProtocolError; + +use super::{Event, KeyboardEvent, PointerEvent}; + +enum PointerEventType { + Motion, + Button, + Axis, + AxisDiscrete120, + Frame, +} + +enum KeyboardEventType { + Key, + Modifiers, +} + +enum EventType { + Pointer, + Keyboard, + Enter, + Leave, + Ping, + Pong, + Disconnect, +} + +impl Event { + fn event_type(&self) -> EventType { + match self { + Self::Pointer(_) => EventType::Pointer, + Self::Keyboard(_) => EventType::Keyboard, + Self::Enter() => EventType::Enter, + Self::Leave() => EventType::Leave, + Self::Ping() => EventType::Ping, + Self::Pong() => EventType::Pong, + Self::Disconnect() => EventType::Disconnect, + } + } +} + +impl PointerEvent { + fn event_type(&self) -> PointerEventType { + match self { + Self::Motion { .. } => PointerEventType::Motion, + Self::Button { .. } => PointerEventType::Button, + Self::Axis { .. } => PointerEventType::Axis, + Self::AxisDiscrete120 { .. } => PointerEventType::AxisDiscrete120, + Self::Frame { .. } => PointerEventType::Frame, + } + } +} + +impl KeyboardEvent { + fn event_type(&self) -> KeyboardEventType { + match self { + KeyboardEvent::Key { .. } => KeyboardEventType::Key, + KeyboardEvent::Modifiers { .. } => KeyboardEventType::Modifiers, + } + } +} + +impl TryFrom for PointerEventType { + type Error = ProtocolError; + + fn try_from(value: u8) -> Result { + match value { + x if x == Self::Motion as u8 => Ok(Self::Motion), + x if x == Self::Button as u8 => Ok(Self::Button), + x if x == Self::Axis as u8 => Ok(Self::Axis), + x if x == Self::AxisDiscrete120 as u8 => Ok(Self::AxisDiscrete120), + x if x == Self::Frame as u8 => Ok(Self::Frame), + _ => Err(ProtocolError::InvalidPointerEventId(value)), + } + } +} + +impl TryFrom for KeyboardEventType { + type Error = ProtocolError; + + fn try_from(value: u8) -> Result { + match value { + x if x == Self::Key as u8 => Ok(Self::Key), + x if x == Self::Modifiers as u8 => Ok(Self::Modifiers), + _ => Err(ProtocolError::InvalidKeyboardEventId(value)), + } + } +} + +impl From<&Event> for Vec { + fn from(event: &Event) -> Self { + let event_id = vec![event.event_type() as u8]; + let event_data = match event { + Event::Pointer(p) => p.into(), + Event::Keyboard(k) => k.into(), + Event::Enter() => vec![], + Event::Leave() => vec![], + Event::Ping() => vec![], + Event::Pong() => vec![], + Event::Disconnect() => vec![], + }; + [event_id, event_data].concat() + } +} + +impl TryFrom> for Event { + type Error = ProtocolError; + + fn try_from(value: Vec) -> Result { + let event_id = u8::from_be_bytes(value[..1].try_into()?); + match event_id { + i if i == (EventType::Pointer as u8) => Ok(Event::Pointer(value.try_into()?)), + i if i == (EventType::Keyboard as u8) => Ok(Event::Keyboard(value.try_into()?)), + i if i == (EventType::Enter as u8) => Ok(Event::Enter()), + i if i == (EventType::Leave as u8) => Ok(Event::Leave()), + i if i == (EventType::Ping as u8) => Ok(Event::Ping()), + i if i == (EventType::Pong as u8) => Ok(Event::Pong()), + i if i == (EventType::Disconnect as u8) => Ok(Event::Disconnect()), + _ => Err(ProtocolError::InvalidEventId(event_id)), + } + } +} + +impl From<&PointerEvent> for Vec { + fn from(event: &PointerEvent) -> Self { + let id = vec![event.event_type() as u8]; + let data = match event { + PointerEvent::Motion { + time, + dx: relative_x, + dy: relative_y, + } => { + let time = time.to_be_bytes(); + let relative_x = relative_x.to_be_bytes(); + let relative_y = relative_y.to_be_bytes(); + [&time[..], &relative_x[..], &relative_y[..]].concat() + } + PointerEvent::Button { + time, + button, + state, + } => { + let time = time.to_be_bytes(); + let button = button.to_be_bytes(); + let state = state.to_be_bytes(); + [&time[..], &button[..], &state[..]].concat() + } + PointerEvent::Axis { time, axis, value } => { + let time = time.to_be_bytes(); + let axis = axis.to_be_bytes(); + let value = value.to_be_bytes(); + [&time[..], &axis[..], &value[..]].concat() + } + PointerEvent::AxisDiscrete120 { axis, value } => { + let axis = axis.to_be_bytes(); + let value = value.to_be_bytes(); + [&axis[..], &value[..]].concat() + } + PointerEvent::Frame {} => { + vec![] + } + }; + [id, data].concat() + } +} + +fn decode_u8(data: &[u8], idx: I) -> Result +where + I: SliceIndex<[u8], Output = [u8]> + Debug + Clone, +{ + let data = data + .get(idx.clone()) + .ok_or(ProtocolError::Data(format!("{:?}", idx)))?; + Ok(u8::from_be_bytes(data.try_into()?)) +} + +fn decode_u32(data: &[u8], idx: I) -> Result +where + I: SliceIndex<[u8], Output = [u8]> + Debug + Clone, +{ + let data = data + .get(idx.clone()) + .ok_or(ProtocolError::Data(format!("{:?}", idx)))?; + Ok(u32::from_be_bytes(data.try_into()?)) +} + +fn decode_i32(data: &[u8], idx: I) -> Result +where + I: SliceIndex<[u8], Output = [u8]> + Debug + Clone, +{ + let data = data + .get(idx.clone()) + .ok_or(ProtocolError::Data(format!("{:?}", idx)))?; + Ok(i32::from_be_bytes(data.try_into()?)) +} +fn decode_f64(data: &[u8], idx: I) -> Result +where + I: SliceIndex<[u8], Output = [u8]> + Debug + Clone, +{ + let data = data + .get(idx.clone()) + .ok_or(ProtocolError::Data(format!("{:?}", idx)))?; + Ok(f64::from_be_bytes(data.try_into()?)) +} + +impl TryFrom> for PointerEvent { + type Error = ProtocolError; + + fn try_from(data: Vec) -> Result { + match data.get(1) { + Some(id) => match id.to_owned().try_into()? { + PointerEventType::Motion => { + let time = decode_u32(&data, 2..6)?; + let dx = decode_f64(&data, 6..14)?; + let dy = decode_f64(&data, 14..22)?; + + Ok(Self::Motion { time, dx, dy }) + } + PointerEventType::Button => { + let time = decode_u32(&data, 2..6)?; + let button = decode_u32(&data, 6..10)?; + let state = decode_u32(&data, 10..14)?; + + Ok(Self::Button { + time, + button, + state, + }) + } + PointerEventType::Axis => { + let time = decode_u32(&data, 2..6)?; + let axis = decode_u8(&data, 6..7)?; + let value = decode_f64(&data, 7..15)?; + Ok(Self::Axis { time, axis, value }) + } + PointerEventType::AxisDiscrete120 => { + let axis = decode_u8(&data, 2..3)?; + let value = decode_i32(&data, 3..7)?; + Ok(Self::AxisDiscrete120 { axis, value }) + } + PointerEventType::Frame => Ok(Self::Frame {}), + }, + None => Err(ProtocolError::Data("0".to_string())), + } + } +} + +impl From<&KeyboardEvent> for Vec { + fn from(event: &KeyboardEvent) -> Self { + let id = vec![event.event_type() as u8]; + let data = match event { + KeyboardEvent::Key { time, key, state } => { + let time = time.to_be_bytes(); + let key = key.to_be_bytes(); + let state = state.to_be_bytes(); + [&time[..], &key[..], &state[..]].concat() + } + KeyboardEvent::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + } => { + let mods_depressed = mods_depressed.to_be_bytes(); + let mods_latched = mods_latched.to_be_bytes(); + let mods_locked = mods_locked.to_be_bytes(); + let group = group.to_be_bytes(); + [ + &mods_depressed[..], + &mods_latched[..], + &mods_locked[..], + &group[..], + ] + .concat() + } + }; + [id, data].concat() + } +} + +impl TryFrom> for KeyboardEvent { + type Error = ProtocolError; + + fn try_from(data: Vec) -> Result { + match data.get(1) { + Some(id) => match id.to_owned().try_into()? { + KeyboardEventType::Key => { + let time = decode_u32(&data, 2..6)?; + let key = decode_u32(&data, 6..10)?; + let state = decode_u8(&data, 10..11)?; + Ok(KeyboardEvent::Key { time, key, state }) + } + KeyboardEventType::Modifiers => { + let mods_depressed = decode_u32(&data, 2..6)?; + let mods_latched = decode_u32(&data, 6..10)?; + let mods_locked = decode_u32(&data, 10..14)?; + let group = decode_u32(&data, 14..18)?; + Ok(KeyboardEvent::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + }) + } + }, + None => Err(ProtocolError::Data("0".to_string())), + } + } +} diff --git a/src/emulation_test.rs b/src/emulation_test.rs index d132e38..64d1346 100644 --- a/src/emulation_test.rs +++ b/src/emulation_test.rs @@ -40,8 +40,8 @@ async fn input_emulation_test(config: Config) -> Result<()> { let (relative_x, relative_y) = (relative_motion.0 as f64, relative_motion.1 as f64); let event = Event::Pointer(PointerEvent::Motion { time: 0, - relative_x, - relative_y, + dx: relative_x, + dy: relative_y, }); emulation.consume(event, 0).await?; } diff --git a/src/server.rs b/src/server.rs index ca61ca2..4f6b9c8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -116,7 +116,7 @@ impl Server { sender_tx.clone(), capture_channel.clone(), timer_tx, - )?; + ); // create dns resolver let resolver = dns::DnsResolver::new().await?; diff --git a/src/server/emulation_task.rs b/src/server/emulation_task.rs index 5b0906b..619885c 100644 --- a/src/server/emulation_task.rs +++ b/src/server/emulation_task.rs @@ -1,12 +1,16 @@ -use anyhow::{anyhow, Result}; use std::net::SocketAddr; +use thiserror::Error; use tokio::{ sync::mpsc::{Receiver, Sender}, task::JoinHandle, }; -use crate::{client::ClientHandle, config::EmulationBackend, server::State}; +use crate::{ + client::{ClientHandle, ClientManager}, + config::EmulationBackend, + server::State, +}; use input_emulation::{ self, error::{EmulationCreationError, EmulationError}, @@ -14,7 +18,7 @@ use input_emulation::{ }; use input_event::{Event, KeyboardEvent}; -use super::{CaptureEvent, Server}; +use super::{network_task::NetworkError, CaptureEvent, Server}; #[derive(Clone, Debug)] pub enum EmulationEvent { @@ -31,51 +35,73 @@ pub enum EmulationEvent { pub fn new( backend: Option, server: Server, - mut udp_rx: Receiver>, + udp_rx: Receiver>, sender_tx: Sender<(Event, SocketAddr)>, capture_tx: Sender, timer_tx: Sender<()>, -) -> Result<(JoinHandle>, Sender), EmulationCreationError> { - let (tx, mut rx) = tokio::sync::mpsc::channel(32); - let emulate_task = tokio::task::spawn_local(async move { - let backend = backend.map(|b| b.into()); - let mut emulate = input_emulation::create(backend).await?; - let mut last_ignored = None; +) -> ( + JoinHandle>, + Sender, +) { + let (tx, rx) = tokio::sync::mpsc::channel(32); + let emulation_task = + emulation_task(backend, rx, server, udp_rx, sender_tx, capture_tx, timer_tx); + let emulate_task = tokio::task::spawn_local(emulation_task); + (emulate_task, tx) +} - loop { - tokio::select! { - udp_event = udp_rx.recv() => { - let udp_event = udp_event.ok_or(anyhow!("receiver closed"))??; - handle_udp_rx(&server, &capture_tx, &mut emulate, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await?; - } - emulate_event = rx.recv() => { - match emulate_event { - Some(e) => match e { - EmulationEvent::Create(h) => emulate.create(h).await, - EmulationEvent::Destroy(h) => emulate.destroy(h).await, - EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulate, c).await?, - EmulationEvent::Terminate => break, - }, - None => break, +#[derive(Debug, Error)] +pub enum LanMouseEmulationError { + #[error("error creating input-emulation: `{0}`")] + Create(#[from] EmulationCreationError), + #[error("error emulating input: `{0}`")] + Emulate(#[from] EmulationError), +} + +async fn emulation_task( + backend: Option, + mut rx: Receiver, + server: Server, + mut udp_rx: Receiver>, + sender_tx: Sender<(Event, SocketAddr)>, + capture_tx: Sender, + timer_tx: Sender<()>, +) -> Result<(), LanMouseEmulationError> { + let backend = backend.map(|b| b.into()); + let mut emulation = input_emulation::create(backend).await?; + + let mut last_ignored = None; + loop { + tokio::select! { + udp_event = udp_rx.recv() => { + let udp_event = match udp_event { + Some(Ok(e)) => e, + Some(Err(e)) => { + log::warn!("network error: {e}"); + continue; } + None => break, + }; + handle_udp_rx(&server, &capture_tx, &mut emulation, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await?; + } + emulate_event = rx.recv() => { + match emulate_event { + Some(e) => match e { + EmulationEvent::Create(h) => emulation.create(h).await, + EmulationEvent::Destroy(h) => emulation.destroy(h).await, + EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulation, c).await?, + EmulationEvent::Terminate => break, + }, + None => break, } } } + } - // release potentially still pressed keys - let clients = server - .client_manager - .borrow() - .get_client_states() - .map(|(h, _)| h) - .collect::>(); - for client in clients { - release_keys(&server, &mut emulate, client).await?; - } + // release potentially still pressed keys + release_all_keys(&server, &mut emulation).await?; - anyhow::Ok(()) - }); - Ok((emulate_task, tx)) + Ok(()) } async fn handle_udp_rx( @@ -89,38 +115,15 @@ async fn handle_udp_rx( ) -> Result<(), EmulationError> { let (event, addr) = event; - // get handle for addr - let handle = match server.client_manager.borrow().get_client(addr) { - Some(a) => a, - None => { - if last_ignored.is_none() || last_ignored.is_some() && last_ignored.unwrap() != addr { - log::warn!("ignoring events from client {addr}"); - last_ignored.replace(addr); - } - return Ok(()); - } + log::trace!("{:20} <-<-<-<------ {addr}", event.to_string()); + + // get client handle for addr + let Some(handle) = + activate_client_if_exists(&mut server.client_manager.borrow_mut(), addr, last_ignored) + else { + return Ok(()); }; - // next event can be logged as ignored again - last_ignored.take(); - - log::trace!("{:20} <-<-<-<------ {addr} ({handle})", event.to_string()); - { - let mut client_manager = server.client_manager.borrow_mut(); - let client_state = match client_manager.get_mut(handle) { - Some((_, s)) => s, - None => { - log::error!("unknown handle"); - return Ok(()); - } - }; - - // reset ttl for client and - client_state.alive = true; - // set addr as new default for this client - client_state.active_addr = Some(addr); - } - match (event, addr) { (Event::Pong(), _) => { /* ignore pong events */ } (Event::Ping(), addr) => { @@ -148,30 +151,22 @@ async fn handle_udp_rx( } } State::Receiving => { - let mut ignore_event = false; - if let Event::Keyboard(KeyboardEvent::Key { - time: _, - key, - state, - }) = event - { - let mut client_manager = server.client_manager.borrow_mut(); - let client_state = if let Some((_, s)) = client_manager.get_mut(handle) { - s + let ignore_event = + if let Event::Keyboard(KeyboardEvent::Key { key, state, .. }) = event { + let (ignore_event, restart_timer) = update_client_keys( + &mut server.client_manager.borrow_mut(), + handle, + key, + state, + ); + // restart timer if necessary + if restart_timer { + let _ = timer_tx.try_send(()); + } + ignore_event } else { - log::error!("unknown handle"); - return Ok(()); + false }; - if state == 0 { - // ignore release event if key not pressed - ignore_event = !client_state.pressed_keys.remove(&key); - } else { - // ignore press event if key not released - ignore_event = !client_state.pressed_keys.insert(key); - let _ = timer_tx.try_send(()); - } - } - // ignore double press / release events to // workaround buggy rdp backend. if !ignore_event { // consume event @@ -203,6 +198,22 @@ async fn handle_udp_rx( Ok(()) } +async fn release_all_keys( + server: &Server, + emulation: &mut Box, +) -> Result<(), EmulationError> { + let clients = server + .client_manager + .borrow() + .get_client_states() + .map(|(h, _)| h) + .collect::>(); + for client in clients { + release_keys(server, emulation, client).await?; + } + Ok(()) +} + async fn release_keys( server: &Server, emulate: &mut Box, @@ -237,3 +248,50 @@ async fn release_keys( emulate.consume(event, client).await?; Ok(()) } + +fn activate_client_if_exists( + client_manager: &mut ClientManager, + addr: SocketAddr, + last_ignored: &mut Option, +) -> Option { + let Some(handle) = client_manager.get_client(addr) else { + // log ignored if it is the first event from the client in a series + if last_ignored.is_none() || last_ignored.is_some() && last_ignored.unwrap() != addr { + log::warn!("ignoring events from client {addr}"); + last_ignored.replace(addr); + } + return None; + }; + // next event can be logged as ignored again + last_ignored.take(); + + let (_, client_state) = client_manager.get_mut(handle)?; + + // reset ttl for client + client_state.alive = true; + // set addr as new default for this client + client_state.active_addr = Some(addr); + Some(handle) +} + +fn update_client_keys( + client_manager: &mut ClientManager, + handle: ClientHandle, + key: u32, + state: u8, +) -> (bool, bool) { + let Some(client_state) = client_manager.get_mut(handle).map(|(_, s)| s) else { + return (true, false); + }; + + // ignore double press / release events + let ignore_event = if state == 0 { + // ignore release event if key not pressed + !client_state.pressed_keys.remove(&key) + } else { + // ignore press event if key not released + !client_state.pressed_keys.insert(key) + }; + let restart_timer = !client_state.pressed_keys.is_empty(); + (ignore_event, restart_timer) +} diff --git a/src/server/network_task.rs b/src/server/network_task.rs index cdc9c2a..e356124 100644 --- a/src/server/network_task.rs +++ b/src/server/network_task.rs @@ -1,6 +1,7 @@ -use std::net::SocketAddr; +use std::{io, net::SocketAddr}; use anyhow::Result; +use thiserror::Error; use tokio::{ net::UdpSocket, sync::mpsc::{Receiver, Sender}, @@ -8,7 +9,7 @@ use tokio::{ }; use crate::frontend::FrontendEvent; -use input_event::Event; +use input_event::{Event, ProtocolError}; use super::Server; @@ -18,30 +19,24 @@ pub async fn new( ) -> Result<( JoinHandle<()>, Sender<(Event, SocketAddr)>, - Receiver>, + Receiver>, Sender, )> { // bind the udp socket let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), server.port.get()); let mut socket = UdpSocket::bind(listen_addr).await?; let (receiver_tx, receiver_rx) = tokio::sync::mpsc::channel(32); - let (sender_tx, mut sender_rx) = tokio::sync::mpsc::channel(32); + let (sender_tx, sender_rx) = tokio::sync::mpsc::channel(32); let (port_tx, mut port_rx) = tokio::sync::mpsc::channel(32); let udp_task = tokio::task::spawn_local(async move { + let mut sender_rx = sender_rx; loop { + let udp_receiver = udp_receiver(&socket, &receiver_tx); + let udp_sender = udp_sender(&socket, &mut sender_rx); tokio::select! { - event = receive_event(&socket) => { - let _ = receiver_tx.send(event).await; - } - event = sender_rx.recv() => { - let Some((event, addr)) = event else { - break; - }; - if let Err(e) = send_event(&socket, event, addr) { - log::warn!("udp send failed: {e}"); - }; - } + _ = udp_receiver => { } + _ = udp_sender => { } port = port_rx.recv() => { let Some(port) = port else { break; @@ -67,7 +62,6 @@ pub async fn new( )).await; } } - } } } @@ -75,7 +69,37 @@ pub async fn new( Ok((udp_task, sender_tx, receiver_rx, port_tx)) } -async fn receive_event(socket: &UdpSocket) -> Result<(Event, SocketAddr)> { +async fn udp_receiver( + socket: &UdpSocket, + receiver_tx: &Sender>, +) { + loop { + let event = receive_event(&socket).await; + let _ = receiver_tx.send(event).await; + } +} + +async fn udp_sender(socket: &UdpSocket, rx: &mut Receiver<(Event, SocketAddr)>) { + loop { + let (event, addr) = match rx.recv().await { + Some(e) => e, + None => return, + }; + if let Err(e) = send_event(&socket, event, addr) { + log::warn!("udp send failed: {e}"); + }; + } +} + +#[derive(Debug, Error)] +pub(crate) enum NetworkError { + #[error(transparent)] + Protocol(#[from] ProtocolError), + #[error("network error: `{0}`")] + Io(#[from] io::Error), +} + +async fn receive_event(socket: &UdpSocket) -> Result<(Event, SocketAddr), NetworkError> { let mut buf = vec![0u8; 22]; let (_amt, src) = socket.recv_from(&mut buf).await?; Ok((Event::try_from(buf)?, src))