ironrdp_server/
handler.rs

1use ironrdp_ainput as ainput;
2use ironrdp_pdu::input::fast_path::{self, SynchronizeFlags};
3use ironrdp_pdu::input::mouse::PointerFlags;
4use ironrdp_pdu::input::mouse_rel::PointerRelFlags;
5use ironrdp_pdu::input::mouse_x::PointerXFlags;
6use ironrdp_pdu::input::sync::SyncToggleFlags;
7use ironrdp_pdu::input::{scan_code, unicode, MousePdu, MouseRelPdu, MouseXPdu};
8
9/// Keyboard Event
10///
11/// Describes a keyboard event received from the client
12///
13#[derive(Debug)]
14pub enum KeyboardEvent {
15    Pressed { code: u8, extended: bool },
16    Released { code: u8, extended: bool },
17    UnicodePressed(u16),
18    UnicodeReleased(u16),
19    Synchronize(SynchronizeFlags),
20}
21
22/// Mouse Event
23///
24/// Describes a mouse event received from the client
25///
26#[derive(Debug)]
27pub enum MouseEvent {
28    Move { x: u16, y: u16 },
29    RightPressed,
30    RightReleased,
31    LeftPressed,
32    LeftReleased,
33    MiddlePressed,
34    MiddleReleased,
35    Button4Pressed,
36    Button4Released,
37    Button5Pressed,
38    Button5Released,
39    VerticalScroll { value: i16 },
40    Scroll { x: i32, y: i32 },
41    RelMove { x: i32, y: i32 },
42}
43
44/// Input Event Handler for an RDP server
45///
46/// Whenever the RDP server will receive an input event from a client, the relevant callback from
47/// this handler will be called
48///
49/// # Example
50///
51/// ```
52/// use ironrdp_server::{KeyboardEvent, MouseEvent, RdpServerInputHandler};
53///
54/// pub struct InputHandler;
55///
56/// impl RdpServerInputHandler for InputHandler {
57///     fn keyboard(&mut self, event: KeyboardEvent) {
58///         match event {
59///             KeyboardEvent::Pressed { code, .. } => println!("Pressed {}", code),
60///             KeyboardEvent::Released { code, .. } => println!("Released {}", code),
61///             other => println!("unhandled event: {:?}", other),
62///         };
63///     }
64///
65///     fn mouse(&mut self, event: MouseEvent) {
66///         let result = match event {
67///             MouseEvent::Move { x, y } => println!("Moved mouse to {} {}", x, y),
68///             other => println!("unhandled event: {:?}", other),
69///         };
70///     }
71/// }
72/// ```
73pub trait RdpServerInputHandler: Send {
74    fn keyboard(&mut self, event: KeyboardEvent);
75    fn mouse(&mut self, event: MouseEvent);
76}
77
78impl From<(u8, fast_path::KeyboardFlags)> for KeyboardEvent {
79    fn from((key, flags): (u8, fast_path::KeyboardFlags)) -> Self {
80        let extended = flags.contains(fast_path::KeyboardFlags::EXTENDED);
81        if flags.contains(fast_path::KeyboardFlags::RELEASE) {
82            KeyboardEvent::Released { code: key, extended }
83        } else {
84            KeyboardEvent::Pressed { code: key, extended }
85        }
86    }
87}
88
89impl From<(u16, fast_path::KeyboardFlags)> for KeyboardEvent {
90    fn from((key, flags): (u16, fast_path::KeyboardFlags)) -> Self {
91        if flags.contains(fast_path::KeyboardFlags::RELEASE) {
92            KeyboardEvent::UnicodeReleased(key)
93        } else {
94            KeyboardEvent::UnicodePressed(key)
95        }
96    }
97}
98
99impl From<(u16, scan_code::KeyboardFlags)> for KeyboardEvent {
100    #[expect(
101        clippy::as_conversions,
102        clippy::cast_possible_truncation,
103        reason = "we are truncating the value on purpose"
104    )]
105    fn from((key, flags): (u16, scan_code::KeyboardFlags)) -> Self {
106        let extended = flags.contains(scan_code::KeyboardFlags::EXTENDED);
107
108        if flags.contains(scan_code::KeyboardFlags::RELEASE) {
109            KeyboardEvent::Released {
110                code: key as u8,
111                extended,
112            }
113        } else {
114            KeyboardEvent::Pressed {
115                code: key as u8,
116                extended,
117            }
118        }
119    }
120}
121
122impl From<(u16, unicode::KeyboardFlags)> for KeyboardEvent {
123    fn from((key, flags): (u16, unicode::KeyboardFlags)) -> Self {
124        if flags.contains(unicode::KeyboardFlags::RELEASE) {
125            KeyboardEvent::UnicodeReleased(key)
126        } else {
127            KeyboardEvent::UnicodePressed(key)
128        }
129    }
130}
131
132impl From<SynchronizeFlags> for KeyboardEvent {
133    fn from(value: SynchronizeFlags) -> Self {
134        KeyboardEvent::Synchronize(value)
135    }
136}
137
138impl From<SyncToggleFlags> for KeyboardEvent {
139    #[expect(
140        clippy::as_conversions,
141        clippy::cast_possible_truncation,
142        reason = "we are truncating the value on purpose"
143    )]
144    fn from(value: SyncToggleFlags) -> Self {
145        KeyboardEvent::Synchronize(SynchronizeFlags::from_bits_truncate(value.bits() as u8))
146    }
147}
148
149impl From<MousePdu> for MouseEvent {
150    fn from(value: MousePdu) -> Self {
151        if value.flags.contains(PointerFlags::LEFT_BUTTON) {
152            if value.flags.contains(PointerFlags::DOWN) {
153                MouseEvent::LeftPressed
154            } else {
155                MouseEvent::LeftReleased
156            }
157        } else if value.flags.contains(PointerFlags::RIGHT_BUTTON) {
158            if value.flags.contains(PointerFlags::DOWN) {
159                MouseEvent::RightPressed
160            } else {
161                MouseEvent::RightReleased
162            }
163        } else if value.flags.contains(PointerFlags::VERTICAL_WHEEL) {
164            MouseEvent::VerticalScroll {
165                value: value.number_of_wheel_rotation_units,
166            }
167        } else {
168            MouseEvent::Move {
169                x: value.x_position,
170                y: value.y_position,
171            }
172        }
173    }
174}
175
176impl From<MouseXPdu> for MouseEvent {
177    fn from(value: MouseXPdu) -> Self {
178        if value.flags.contains(PointerXFlags::BUTTON1) {
179            if value.flags.contains(PointerXFlags::DOWN) {
180                MouseEvent::LeftPressed
181            } else {
182                MouseEvent::LeftReleased
183            }
184        } else if value.flags.contains(PointerXFlags::BUTTON2) {
185            if value.flags.contains(PointerXFlags::DOWN) {
186                MouseEvent::RightPressed
187            } else {
188                MouseEvent::RightReleased
189            }
190        } else {
191            MouseEvent::Move {
192                x: value.x_position,
193                y: value.y_position,
194            }
195        }
196    }
197}
198
199impl From<MouseRelPdu> for MouseEvent {
200    fn from(value: MouseRelPdu) -> Self {
201        if value.flags.contains(PointerRelFlags::BUTTON1) {
202            if value.flags.contains(PointerRelFlags::DOWN) {
203                MouseEvent::LeftPressed
204            } else {
205                MouseEvent::LeftReleased
206            }
207        } else if value.flags.contains(PointerRelFlags::BUTTON2) {
208            if value.flags.contains(PointerRelFlags::DOWN) {
209                MouseEvent::RightPressed
210            } else {
211                MouseEvent::RightReleased
212            }
213        } else if value.flags.contains(PointerRelFlags::BUTTON3) {
214            if value.flags.contains(PointerRelFlags::DOWN) {
215                MouseEvent::MiddlePressed
216            } else {
217                MouseEvent::MiddleReleased
218            }
219        } else if value.flags.contains(PointerRelFlags::XBUTTON1) {
220            if value.flags.contains(PointerRelFlags::DOWN) {
221                MouseEvent::Button4Pressed
222            } else {
223                MouseEvent::Button4Released
224            }
225        } else if value.flags.contains(PointerRelFlags::XBUTTON2) {
226            if value.flags.contains(PointerRelFlags::DOWN) {
227                MouseEvent::Button5Pressed
228            } else {
229                MouseEvent::Button5Released
230            }
231        } else {
232            MouseEvent::RelMove {
233                x: value.x_delta.into(),
234                y: value.y_delta.into(),
235            }
236        }
237    }
238}
239
240impl From<ainput::MousePdu> for MouseEvent {
241    fn from(value: ainput::MousePdu) -> Self {
242        use ainput::MouseEventFlags;
243
244        if value.flags.contains(MouseEventFlags::BUTTON1) {
245            if value.flags.contains(MouseEventFlags::DOWN) {
246                MouseEvent::LeftPressed
247            } else {
248                MouseEvent::LeftReleased
249            }
250        } else if value.flags.contains(MouseEventFlags::BUTTON2) {
251            if value.flags.contains(MouseEventFlags::DOWN) {
252                MouseEvent::RightPressed
253            } else {
254                MouseEvent::RightReleased
255            }
256        } else if value.flags.contains(MouseEventFlags::BUTTON3) {
257            if value.flags.contains(MouseEventFlags::DOWN) {
258                MouseEvent::MiddlePressed
259            } else {
260                MouseEvent::MiddleReleased
261            }
262        } else if value.flags.contains(MouseEventFlags::WHEEL) {
263            MouseEvent::Scroll { x: value.x, y: value.y }
264        } else if value.flags.contains(MouseEventFlags::REL) {
265            MouseEvent::RelMove { x: value.x, y: value.y }
266        } else if value.flags.contains(MouseEventFlags::MOVE) {
267            // assume moves are 0 <= u16::MAX
268            MouseEvent::Move {
269                x: value.x.try_into().unwrap_or(0),
270                y: value.y.try_into().unwrap_or(0),
271            }
272        } else {
273            MouseEvent::Move { x: 0, y: 0 }
274        }
275    }
276}