Skip to main content

lan_mouse_proto/
lib.rs

1use input_event::{Event as InputEvent, KeyboardEvent, PointerEvent};
2use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
3use paste::paste;
4use std::{
5    fmt::{Debug, Display, Formatter},
6    mem::size_of,
7};
8use thiserror::Error;
9
10/// defines the maximum size an encoded event can take up
11/// this is currently the pointer motion event
12/// type: u8, time: u32, dx: f64, dy: f64
13pub const MAX_EVENT_SIZE: usize = size_of::<u8>() + size_of::<u32>() + 2 * size_of::<f64>();
14
15/// error type for protocol violations
16#[derive(Debug, Error)]
17pub enum ProtocolError {
18    /// event type does not exist
19    #[error("invalid event id: `{0}`")]
20    InvalidEventId(#[from] TryFromPrimitiveError<EventType>),
21    /// position type does not exist
22    #[error("invalid event id: `{0}`")]
23    InvalidPosition(#[from] TryFromPrimitiveError<Position>),
24}
25
26/// Position of a client
27#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
28#[repr(u8)]
29pub enum Position {
30    Left,
31    Right,
32    Top,
33    Bottom,
34}
35
36impl Display for Position {
37    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38        let pos = match self {
39            Position::Left => "left",
40            Position::Right => "right",
41            Position::Top => "top",
42            Position::Bottom => "bottom",
43        };
44        write!(f, "{pos}")
45    }
46}
47
48/// main lan-mouse protocol event type
49#[derive(Clone, Copy, Debug)]
50pub enum ProtoEvent {
51    /// notify a client that the cursor entered its region at the given position
52    /// [`ProtoEvent::Ack`] with the same serial is used for synchronization between devices
53    Enter(Position),
54    /// notify a client that the cursor left its region
55    /// [`ProtoEvent::Ack`] with the same serial is used for synchronization between devices
56    Leave(u32),
57    /// acknowledge of an [`ProtoEvent::Enter`] or [`ProtoEvent::Leave`] event
58    Ack(u32),
59    /// Input event
60    Input(InputEvent),
61    /// Ping event for tracking unresponsive clients.
62    /// A client has to respond with [`ProtoEvent::Pong`].
63    Ping,
64    /// Response to [`ProtoEvent::Ping`], true if emulation is enabled / available
65    Pong(bool),
66}
67
68impl Display for ProtoEvent {
69    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70        match self {
71            ProtoEvent::Enter(s) => write!(f, "Enter({s})"),
72            ProtoEvent::Leave(s) => write!(f, "Leave({s})"),
73            ProtoEvent::Ack(s) => write!(f, "Ack({s})"),
74            ProtoEvent::Input(e) => write!(f, "{e}"),
75            ProtoEvent::Ping => write!(f, "ping"),
76            ProtoEvent::Pong(alive) => {
77                write!(
78                    f,
79                    "pong: {}",
80                    if *alive { "alive" } else { "not available" }
81                )
82            }
83        }
84    }
85}
86
87#[derive(TryFromPrimitive, IntoPrimitive)]
88#[repr(u8)]
89pub enum EventType {
90    PointerMotion,
91    PointerButton,
92    PointerAxis,
93    PointerAxisValue120,
94    KeyboardKey,
95    KeyboardModifiers,
96    Ping,
97    Pong,
98    Enter,
99    Leave,
100    Ack,
101}
102
103impl ProtoEvent {
104    fn event_type(&self) -> EventType {
105        match self {
106            ProtoEvent::Input(e) => match e {
107                InputEvent::Pointer(p) => match p {
108                    PointerEvent::Motion { .. } => EventType::PointerMotion,
109                    PointerEvent::Button { .. } => EventType::PointerButton,
110                    PointerEvent::Axis { .. } => EventType::PointerAxis,
111                    PointerEvent::AxisDiscrete120 { .. } => EventType::PointerAxisValue120,
112                },
113                InputEvent::Keyboard(k) => match k {
114                    KeyboardEvent::Key { .. } => EventType::KeyboardKey,
115                    KeyboardEvent::Modifiers { .. } => EventType::KeyboardModifiers,
116                },
117            },
118            ProtoEvent::Ping => EventType::Ping,
119            ProtoEvent::Pong(_) => EventType::Pong,
120            ProtoEvent::Enter(_) => EventType::Enter,
121            ProtoEvent::Leave(_) => EventType::Leave,
122            ProtoEvent::Ack(_) => EventType::Ack,
123        }
124    }
125}
126
127impl TryFrom<[u8; MAX_EVENT_SIZE]> for ProtoEvent {
128    type Error = ProtocolError;
129
130    fn try_from(buf: [u8; MAX_EVENT_SIZE]) -> Result<Self, Self::Error> {
131        let mut buf = &buf[..];
132        let event_type = decode_u8(&mut buf)?;
133        match EventType::try_from(event_type)? {
134            EventType::PointerMotion => {
135                Ok(Self::Input(InputEvent::Pointer(PointerEvent::Motion {
136                    time: decode_u32(&mut buf)?,
137                    dx: decode_f64(&mut buf)?,
138                    dy: decode_f64(&mut buf)?,
139                })))
140            }
141            EventType::PointerButton => {
142                Ok(Self::Input(InputEvent::Pointer(PointerEvent::Button {
143                    time: decode_u32(&mut buf)?,
144                    button: decode_u32(&mut buf)?,
145                    state: decode_u32(&mut buf)?,
146                })))
147            }
148            EventType::PointerAxis => Ok(Self::Input(InputEvent::Pointer(PointerEvent::Axis {
149                time: decode_u32(&mut buf)?,
150                axis: decode_u8(&mut buf)?,
151                value: decode_f64(&mut buf)?,
152            }))),
153            EventType::PointerAxisValue120 => Ok(Self::Input(InputEvent::Pointer(
154                PointerEvent::AxisDiscrete120 {
155                    axis: decode_u8(&mut buf)?,
156                    value: decode_i32(&mut buf)?,
157                },
158            ))),
159            EventType::KeyboardKey => Ok(Self::Input(InputEvent::Keyboard(KeyboardEvent::Key {
160                time: decode_u32(&mut buf)?,
161                key: decode_u32(&mut buf)?,
162                state: decode_u8(&mut buf)?,
163            }))),
164            EventType::KeyboardModifiers => Ok(Self::Input(InputEvent::Keyboard(
165                KeyboardEvent::Modifiers {
166                    depressed: decode_u32(&mut buf)?,
167                    latched: decode_u32(&mut buf)?,
168                    locked: decode_u32(&mut buf)?,
169                    group: decode_u32(&mut buf)?,
170                },
171            ))),
172            EventType::Ping => Ok(Self::Ping),
173            EventType::Pong => Ok(Self::Pong(decode_u8(&mut buf)? != 0)),
174            EventType::Enter => Ok(Self::Enter(decode_u8(&mut buf)?.try_into()?)),
175            EventType::Leave => Ok(Self::Leave(decode_u32(&mut buf)?)),
176            EventType::Ack => Ok(Self::Ack(decode_u32(&mut buf)?)),
177        }
178    }
179}
180
181impl From<ProtoEvent> for ([u8; MAX_EVENT_SIZE], usize) {
182    fn from(event: ProtoEvent) -> Self {
183        let mut buf = [0u8; MAX_EVENT_SIZE];
184        let mut len = 0usize;
185        {
186            let mut buf = &mut buf[..];
187            let buf = &mut buf;
188            let len = &mut len;
189            encode_u8(buf, len, event.event_type() as u8);
190            match event {
191                ProtoEvent::Input(event) => match event {
192                    InputEvent::Pointer(p) => match p {
193                        PointerEvent::Motion { time, dx, dy } => {
194                            encode_u32(buf, len, time);
195                            encode_f64(buf, len, dx);
196                            encode_f64(buf, len, dy);
197                        }
198                        PointerEvent::Button {
199                            time,
200                            button,
201                            state,
202                        } => {
203                            encode_u32(buf, len, time);
204                            encode_u32(buf, len, button);
205                            encode_u32(buf, len, state);
206                        }
207                        PointerEvent::Axis { time, axis, value } => {
208                            encode_u32(buf, len, time);
209                            encode_u8(buf, len, axis);
210                            encode_f64(buf, len, value);
211                        }
212                        PointerEvent::AxisDiscrete120 { axis, value } => {
213                            encode_u8(buf, len, axis);
214                            encode_i32(buf, len, value);
215                        }
216                    },
217                    InputEvent::Keyboard(k) => match k {
218                        KeyboardEvent::Key { time, key, state } => {
219                            encode_u32(buf, len, time);
220                            encode_u32(buf, len, key);
221                            encode_u8(buf, len, state);
222                        }
223                        KeyboardEvent::Modifiers {
224                            depressed,
225                            latched,
226                            locked,
227                            group,
228                        } => {
229                            encode_u32(buf, len, depressed);
230                            encode_u32(buf, len, latched);
231                            encode_u32(buf, len, locked);
232                            encode_u32(buf, len, group);
233                        }
234                    },
235                },
236                ProtoEvent::Ping => {}
237                ProtoEvent::Pong(alive) => encode_u8(buf, len, alive as u8),
238                ProtoEvent::Enter(pos) => encode_u8(buf, len, pos as u8),
239                ProtoEvent::Leave(serial) => encode_u32(buf, len, serial),
240                ProtoEvent::Ack(serial) => encode_u32(buf, len, serial),
241            }
242        }
243        (buf, len)
244    }
245}
246
247macro_rules! decode_impl {
248    ($t:ty) => {
249        paste! {
250            fn [<decode_ $t>](data: &mut &[u8]) -> Result<$t, ProtocolError> {
251                let (int_bytes, rest) = data.split_at(size_of::<$t>());
252                *data = rest;
253                Ok($t::from_be_bytes(int_bytes.try_into().unwrap()))
254            }
255        }
256    };
257}
258
259decode_impl!(u8);
260decode_impl!(u32);
261decode_impl!(i32);
262decode_impl!(f64);
263
264macro_rules! encode_impl {
265    ($t:ty) => {
266        paste! {
267            fn [<encode_ $t>](buf: &mut &mut [u8], amt: &mut usize, n: $t) {
268                let src = n.to_be_bytes();
269                let data = std::mem::take(buf);
270                let (int_bytes, rest) = data.split_at_mut(size_of::<$t>());
271                int_bytes.copy_from_slice(&src);
272                *amt += size_of::<$t>();
273                *buf = rest
274            }
275        }
276    };
277}
278
279encode_impl!(u8);
280encode_impl!(u32);
281encode_impl!(i32);
282encode_impl!(f64);