ironrdp_pdu/input/
mod.rs

1use std::io;
2
3use ironrdp_core::{
4    cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, read_padding, write_padding, Decode,
5    DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor,
6};
7use num_derive::FromPrimitive;
8use num_traits::FromPrimitive as _;
9use thiserror::Error;
10
11pub mod fast_path;
12pub mod mouse;
13pub mod mouse_rel;
14pub mod mouse_x;
15pub mod scan_code;
16pub mod sync;
17pub mod unicode;
18pub mod unused;
19
20pub use self::mouse::MousePdu;
21pub use self::mouse_rel::MouseRelPdu;
22pub use self::mouse_x::MouseXPdu;
23pub use self::scan_code::ScanCodePdu;
24pub use self::sync::SyncPdu;
25pub use self::unicode::UnicodePdu;
26pub use self::unused::UnusedPdu;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct InputEventPdu(pub Vec<InputEvent>);
30
31impl InputEventPdu {
32    const NAME: &'static str = "InputEventPdu";
33
34    const FIXED_PART_SIZE: usize = 4 /* nEvents */;
35}
36
37impl Encode for InputEventPdu {
38    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
39        ensure_size!(in: dst, size: self.size());
40
41        dst.write_u16(cast_length!("input events count", self.0.len())?);
42        write_padding!(dst, 2);
43
44        for event in self.0.iter() {
45            event.encode(dst)?;
46        }
47
48        Ok(())
49    }
50
51    fn name(&self) -> &'static str {
52        Self::NAME
53    }
54
55    fn size(&self) -> usize {
56        4 + self.0.iter().map(Encode::size).sum::<usize>()
57    }
58}
59
60impl<'de> Decode<'de> for InputEventPdu {
61    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
62        ensure_fixed_part_size!(in: src);
63
64        let number_of_events = usize::from(src.read_u16());
65        read_padding!(src, 2);
66
67        let events = core::iter::repeat_with(|| InputEvent::decode(src))
68            .take(number_of_events)
69            .collect::<Result<Vec<_>, _>>()?;
70
71        Ok(Self(events))
72    }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum InputEvent {
77    Sync(SyncPdu),
78    Unused(UnusedPdu),
79    ScanCode(ScanCodePdu),
80    Unicode(UnicodePdu),
81    Mouse(MousePdu),
82    MouseX(MouseXPdu),
83    MouseRel(MouseRelPdu),
84}
85
86impl InputEvent {
87    const NAME: &'static str = "InputEvent";
88
89    const FIXED_PART_SIZE: usize = 4 /* eventTime */ + 2 /* eventType */;
90}
91
92impl Encode for InputEvent {
93    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
94        ensure_fixed_part_size!(in: dst);
95
96        dst.write_u32(0); // event time is ignored by a server
97        dst.write_u16(InputEventType::from(self).as_u16());
98
99        match self {
100            Self::Sync(pdu) => pdu.encode(dst),
101            Self::Unused(pdu) => pdu.encode(dst),
102            Self::ScanCode(pdu) => pdu.encode(dst),
103            Self::Unicode(pdu) => pdu.encode(dst),
104            Self::Mouse(pdu) => pdu.encode(dst),
105            Self::MouseX(pdu) => pdu.encode(dst),
106            Self::MouseRel(pdu) => pdu.encode(dst),
107        }
108    }
109
110    fn name(&self) -> &'static str {
111        Self::NAME
112    }
113
114    fn size(&self) -> usize {
115        Self::FIXED_PART_SIZE
116            + match self {
117                Self::Sync(pdu) => pdu.size(),
118                Self::Unused(pdu) => pdu.size(),
119                Self::ScanCode(pdu) => pdu.size(),
120                Self::Unicode(pdu) => pdu.size(),
121                Self::Mouse(pdu) => pdu.size(),
122                Self::MouseX(pdu) => pdu.size(),
123                Self::MouseRel(pdu) => pdu.size(),
124            }
125    }
126}
127
128impl<'de> Decode<'de> for InputEvent {
129    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
130        ensure_fixed_part_size!(in: src);
131
132        let _event_time = src.read_u32(); // ignored by a server
133        let event_type = src.read_u16();
134        let event_type = InputEventType::from_u16(event_type)
135            .ok_or_else(|| invalid_field_err!("eventType", "invalid input event type"))?;
136
137        match event_type {
138            InputEventType::Sync => Ok(Self::Sync(SyncPdu::decode(src)?)),
139            InputEventType::Unused => Ok(Self::Unused(UnusedPdu::decode(src)?)),
140            InputEventType::ScanCode => Ok(Self::ScanCode(ScanCodePdu::decode(src)?)),
141            InputEventType::Unicode => Ok(Self::Unicode(UnicodePdu::decode(src)?)),
142            InputEventType::Mouse => Ok(Self::Mouse(MousePdu::decode(src)?)),
143            InputEventType::MouseX => Ok(Self::MouseX(MouseXPdu::decode(src)?)),
144            InputEventType::MouseRel => Ok(Self::MouseRel(MouseRelPdu::decode(src)?)),
145        }
146    }
147}
148
149#[derive(Debug, Copy, Clone, PartialEq, FromPrimitive)]
150#[repr(u16)]
151enum InputEventType {
152    Sync = 0x0000,
153    Unused = 0x0002,
154    ScanCode = 0x0004,
155    Unicode = 0x0005,
156    Mouse = 0x8001,
157    MouseX = 0x8002,
158    MouseRel = 0x8004,
159}
160
161impl InputEventType {
162    #[expect(
163        clippy::as_conversions,
164        reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
165    )]
166    fn as_u16(self) -> u16 {
167        self as u16
168    }
169}
170
171impl From<&InputEvent> for InputEventType {
172    fn from(event: &InputEvent) -> Self {
173        match event {
174            InputEvent::Sync(_) => Self::Sync,
175            InputEvent::Unused(_) => Self::Unused,
176            InputEvent::ScanCode(_) => Self::ScanCode,
177            InputEvent::Unicode(_) => Self::Unicode,
178            InputEvent::Mouse(_) => Self::Mouse,
179            InputEvent::MouseX(_) => Self::MouseX,
180            InputEvent::MouseRel(_) => Self::MouseRel,
181        }
182    }
183}
184
185#[derive(Debug, Error)]
186pub enum InputEventError {
187    #[error("IO error")]
188    IOError(#[from] io::Error),
189    #[error("invalid Input Event type: {0}")]
190    InvalidInputEventType(u16),
191    #[error("encryption not supported")]
192    EncryptionNotSupported,
193    #[error("event code not supported {0}")]
194    EventCodeUnsupported(u8),
195    #[error("keyboard flags not supported {0}")]
196    KeyboardFlagsUnsupported(u8),
197    #[error("synchronize flags not supported {0}")]
198    SynchronizeFlagsUnsupported(u8),
199    #[error("Fast-Path Input Event PDU is empty")]
200    EmptyFastPathInput,
201}