use input_event::{Event as InputEvent, KeyboardEvent, PointerEvent}; use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError}; use paste::paste; use std::{ fmt::{Debug, Display, Formatter}, mem::size_of, }; use thiserror::Error; /// defines the maximum size an encoded event can take up /// this is currently the pointer motion event /// type: u8, time: u32, dx: f64, dy: f64 pub const MAX_EVENT_SIZE: usize = size_of::() + size_of::() + 2 * size_of::(); /// error type for protocol violations #[derive(Debug, Error)] pub enum ProtocolError { /// event type does not exist #[error("invalid event id: `{0}`")] InvalidEventId(#[from] TryFromPrimitiveError), /// position type does not exist #[error("invalid event id: `{0}`")] InvalidPosition(#[from] TryFromPrimitiveError), } /// Position of a client #[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum Position { Left, Right, Top, Bottom, } impl Display for Position { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let pos = match self { Position::Left => "left", Position::Right => "right", Position::Top => "top", Position::Bottom => "bottom", }; write!(f, "{pos}") } } /// main lan-mouse protocol event type #[derive(Clone, Copy, Debug)] pub enum ProtoEvent { /// notify a client that the cursor entered its region at the given position /// [`ProtoEvent::Ack`] with the same serial is used for synchronization between devices Enter(Position), /// notify a client that the cursor left its region /// [`ProtoEvent::Ack`] with the same serial is used for synchronization between devices Leave(u32), /// acknowledge of an [`ProtoEvent::Enter`] or [`ProtoEvent::Leave`] event Ack(u32), /// Input event Input(InputEvent), /// Ping event for tracking unresponsive clients. /// A client has to respond with [`ProtoEvent::Pong`]. Ping, /// Response to [`ProtoEvent::Ping`], true if emulation is enabled / available Pong(bool), } impl Display for ProtoEvent { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { ProtoEvent::Enter(s) => write!(f, "Enter({s})"), ProtoEvent::Leave(s) => write!(f, "Leave({s})"), ProtoEvent::Ack(s) => write!(f, "Ack({s})"), ProtoEvent::Input(e) => write!(f, "{e}"), ProtoEvent::Ping => write!(f, "ping"), ProtoEvent::Pong(alive) => { write!( f, "pong: {}", if *alive { "alive" } else { "not available" } ) } } } } #[derive(TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum EventType { PointerMotion, PointerButton, PointerAxis, PointerAxisValue120, KeyboardKey, KeyboardModifiers, Ping, Pong, Enter, Leave, Ack, } impl ProtoEvent { fn event_type(&self) -> EventType { match self { ProtoEvent::Input(e) => match e { InputEvent::Pointer(p) => match p { PointerEvent::Motion { .. } => EventType::PointerMotion, PointerEvent::Button { .. } => EventType::PointerButton, PointerEvent::Axis { .. } => EventType::PointerAxis, PointerEvent::AxisDiscrete120 { .. } => EventType::PointerAxisValue120, }, InputEvent::Keyboard(k) => match k { KeyboardEvent::Key { .. } => EventType::KeyboardKey, KeyboardEvent::Modifiers { .. } => EventType::KeyboardModifiers, }, }, ProtoEvent::Ping => EventType::Ping, ProtoEvent::Pong(_) => EventType::Pong, ProtoEvent::Enter(_) => EventType::Enter, ProtoEvent::Leave(_) => EventType::Leave, ProtoEvent::Ack(_) => EventType::Ack, } } } impl TryFrom<[u8; MAX_EVENT_SIZE]> for ProtoEvent { type Error = ProtocolError; fn try_from(buf: [u8; MAX_EVENT_SIZE]) -> Result { let mut buf = &buf[..]; let event_type = decode_u8(&mut buf)?; match EventType::try_from(event_type)? { EventType::PointerMotion => { Ok(Self::Input(InputEvent::Pointer(PointerEvent::Motion { time: decode_u32(&mut buf)?, dx: decode_f64(&mut buf)?, dy: decode_f64(&mut buf)?, }))) } EventType::PointerButton => { Ok(Self::Input(InputEvent::Pointer(PointerEvent::Button { time: decode_u32(&mut buf)?, button: decode_u32(&mut buf)?, state: decode_u32(&mut buf)?, }))) } EventType::PointerAxis => Ok(Self::Input(InputEvent::Pointer(PointerEvent::Axis { time: decode_u32(&mut buf)?, axis: decode_u8(&mut buf)?, value: decode_f64(&mut buf)?, }))), EventType::PointerAxisValue120 => Ok(Self::Input(InputEvent::Pointer( PointerEvent::AxisDiscrete120 { axis: decode_u8(&mut buf)?, value: decode_i32(&mut buf)?, }, ))), EventType::KeyboardKey => Ok(Self::Input(InputEvent::Keyboard(KeyboardEvent::Key { time: decode_u32(&mut buf)?, key: decode_u32(&mut buf)?, state: decode_u8(&mut buf)?, }))), EventType::KeyboardModifiers => Ok(Self::Input(InputEvent::Keyboard( KeyboardEvent::Modifiers { depressed: decode_u32(&mut buf)?, latched: decode_u32(&mut buf)?, locked: decode_u32(&mut buf)?, group: decode_u32(&mut buf)?, }, ))), EventType::Ping => Ok(Self::Ping), EventType::Pong => Ok(Self::Pong(decode_u8(&mut buf)? != 0)), EventType::Enter => Ok(Self::Enter(decode_u8(&mut buf)?.try_into()?)), EventType::Leave => Ok(Self::Leave(decode_u32(&mut buf)?)), EventType::Ack => Ok(Self::Ack(decode_u32(&mut buf)?)), } } } impl From for ([u8; MAX_EVENT_SIZE], usize) { fn from(event: ProtoEvent) -> Self { let mut buf = [0u8; MAX_EVENT_SIZE]; let mut len = 0usize; { let mut buf = &mut buf[..]; let buf = &mut buf; let len = &mut len; encode_u8(buf, len, event.event_type() as u8); match event { ProtoEvent::Input(event) => match event { InputEvent::Pointer(p) => match p { PointerEvent::Motion { time, dx, dy } => { encode_u32(buf, len, time); encode_f64(buf, len, dx); encode_f64(buf, len, dy); } PointerEvent::Button { time, button, state, } => { encode_u32(buf, len, time); encode_u32(buf, len, button); encode_u32(buf, len, state); } PointerEvent::Axis { time, axis, value } => { encode_u32(buf, len, time); encode_u8(buf, len, axis); encode_f64(buf, len, value); } PointerEvent::AxisDiscrete120 { axis, value } => { encode_u8(buf, len, axis); encode_i32(buf, len, value); } }, InputEvent::Keyboard(k) => match k { KeyboardEvent::Key { time, key, state } => { encode_u32(buf, len, time); encode_u32(buf, len, key); encode_u8(buf, len, state); } KeyboardEvent::Modifiers { depressed, latched, locked, group, } => { encode_u32(buf, len, depressed); encode_u32(buf, len, latched); encode_u32(buf, len, locked); encode_u32(buf, len, group); } }, }, ProtoEvent::Ping => {} ProtoEvent::Pong(alive) => encode_u8(buf, len, alive as u8), ProtoEvent::Enter(pos) => encode_u8(buf, len, pos as u8), ProtoEvent::Leave(serial) => encode_u32(buf, len, serial), ProtoEvent::Ack(serial) => encode_u32(buf, len, serial), } } (buf, len) } } macro_rules! decode_impl { ($t:ty) => { paste! { fn [](data: &mut &[u8]) -> Result<$t, ProtocolError> { let (int_bytes, rest) = data.split_at(size_of::<$t>()); *data = rest; Ok($t::from_be_bytes(int_bytes.try_into().unwrap())) } } }; } decode_impl!(u8); decode_impl!(u32); decode_impl!(i32); decode_impl!(f64); macro_rules! encode_impl { ($t:ty) => { paste! { fn [](buf: &mut &mut [u8], amt: &mut usize, n: $t) { let src = n.to_be_bytes(); let data = std::mem::take(buf); let (int_bytes, rest) = data.split_at_mut(size_of::<$t>()); int_bytes.copy_from_slice(&src); *amt += size_of::<$t>(); *buf = rest } } }; } encode_impl!(u8); encode_impl!(u32); encode_impl!(i32); encode_impl!(f64);