ironrdp_server/
display.rs

1use core::num::{NonZeroU16, NonZeroUsize};
2
3use anyhow::Result;
4use bytes::{Bytes, BytesMut};
5use ironrdp_displaycontrol::pdu::DisplayControlMonitorLayout;
6use ironrdp_graphics::diff;
7use ironrdp_pdu::pointer::PointerPositionAttribute;
8use tracing::{debug, warn};
9
10#[rustfmt::skip]
11pub use ironrdp_acceptor::DesktopSize;
12pub use ironrdp_graphics::image_processing::PixelFormat;
13
14/// Display Update
15///
16/// Contains all types of display updates currently supported by the server implementation
17/// and the RDP spec
18///
19#[derive(Debug, Clone)]
20pub enum DisplayUpdate {
21    Resize(DesktopSize),
22    Bitmap(BitmapUpdate),
23    PointerPosition(PointerPositionAttribute),
24    ColorPointer(ColorPointer),
25    RGBAPointer(RGBAPointer),
26    HidePointer,
27    DefaultPointer,
28}
29
30#[derive(Clone)]
31pub struct RGBAPointer {
32    pub width: u16,
33    pub height: u16,
34    pub hot_x: u16,
35    pub hot_y: u16,
36    pub data: Vec<u8>,
37}
38
39impl core::fmt::Debug for RGBAPointer {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        f.debug_struct("RGBAPointer")
42            .field("with", &self.width)
43            .field("height", &self.height)
44            .field("hot_x", &self.hot_x)
45            .field("hot_y", &self.hot_y)
46            .field("data_len", &self.data.len())
47            .finish()
48    }
49}
50
51#[derive(Debug, Clone)]
52pub struct ColorPointer {
53    pub width: u16,
54    pub height: u16,
55    pub hot_x: u16,
56    pub hot_y: u16,
57    pub and_mask: Vec<u8>,
58    pub xor_mask: Vec<u8>,
59}
60
61pub struct Framebuffer {
62    pub width: NonZeroU16,
63    pub height: NonZeroU16,
64    pub format: PixelFormat,
65    pub data: BytesMut,
66    pub stride: usize,
67}
68
69impl TryInto<Framebuffer> for BitmapUpdate {
70    type Error = &'static str;
71
72    fn try_into(self) -> Result<Framebuffer, Self::Error> {
73        assert_eq!(self.x, 0);
74        assert_eq!(self.y, 0);
75        Ok(Framebuffer {
76            width: self.width,
77            height: self.height,
78            format: self.format,
79            data: self.data.into(),
80            stride: self.stride.get(),
81        })
82    }
83}
84
85impl Framebuffer {
86    pub fn new(width: NonZeroU16, height: NonZeroU16, format: PixelFormat) -> Self {
87        let mut data = BytesMut::new();
88        let w = NonZeroUsize::from(width).get();
89        let h = NonZeroUsize::from(height).get();
90        let bpp = usize::from(format.bytes_per_pixel());
91        data.resize(bpp * w * h, 0);
92
93        Self {
94            width,
95            height,
96            format,
97            data,
98            stride: bpp * w,
99        }
100    }
101
102    pub fn update(&mut self, bitmap: &BitmapUpdate) {
103        if self.format != bitmap.format {
104            warn!("Bitmap format mismatch, unsupported");
105            return;
106        }
107        let bpp = usize::from(self.format.bytes_per_pixel());
108        let x = usize::from(bitmap.x);
109        let y = usize::from(bitmap.y);
110        let width = NonZeroUsize::from(bitmap.width).get();
111        let height = NonZeroUsize::from(bitmap.height).get();
112
113        let data = &mut self.data;
114        let start = y * self.stride + x * bpp;
115        let end = start + (height - 1) * self.stride + width * bpp;
116        let dst = &mut data[start..end];
117
118        for y in 0..height {
119            let start = y * bitmap.stride.get();
120            let end = start + width * bpp;
121
122            let src = bitmap.data.slice(start..end);
123
124            let start = y * self.stride;
125            let end = start + width * bpp;
126            let dst = &mut dst[start..end];
127
128            dst.copy_from_slice(&src);
129        }
130    }
131
132    pub(crate) fn update_diffs(&mut self, bitmap: &BitmapUpdate, diffs: &[diff::Rect]) {
133        diffs
134            .iter()
135            .filter_map(|diff| {
136                let x = u16::try_from(diff.x).ok()?;
137                let y = u16::try_from(diff.y).ok()?;
138                let width = u16::try_from(diff.width).ok().and_then(NonZeroU16::new)?;
139                let height = u16::try_from(diff.height).ok().and_then(NonZeroU16::new)?;
140
141                bitmap.sub(x, y, width, height)
142            })
143            .for_each(|sub| self.update(&sub));
144    }
145}
146
147/// Bitmap Display Update
148///
149/// Bitmap updates are encoded using RDP 6.0 compression, fragmented and sent using
150/// Fastpath Server Updates
151///
152#[derive(Clone)]
153pub struct BitmapUpdate {
154    pub x: u16,
155    pub y: u16,
156    pub width: NonZeroU16,
157    pub height: NonZeroU16,
158    pub format: PixelFormat,
159    pub data: Bytes,
160    pub stride: NonZeroUsize,
161}
162
163impl BitmapUpdate {
164    /// Extracts a sub-region of the bitmap update.
165    ///
166    /// # Parameters
167    ///
168    /// - `x`: The x-coordinate of the top-left corner of the sub-region.
169    /// - `y`: The y-coordinate of the top-left corner of the sub-region.
170    /// - `width`: The width of the sub-region.
171    /// - `height`: The height of the sub-region.
172    ///
173    /// # Returns
174    ///
175    /// An `Option` containing a new `BitmapUpdate` representing the sub-region if the specified
176    /// dimensions are within the bounds of the original bitmap update, otherwise `None`.
177    ///
178    /// # Example
179    ///
180    /// ```
181    /// # use core::num::NonZeroU16;
182    /// # use std::num::NonZeroUsize;
183    /// # use bytes::Bytes;
184    /// # use ironrdp_graphics::image_processing::PixelFormat;
185    /// # use ironrdp_server::BitmapUpdate;
186    /// let original = BitmapUpdate {
187    ///     x: 0,
188    ///     y: 0,
189    ///     width: NonZeroU16::new(100).unwrap(),
190    ///     height: NonZeroU16::new(100).unwrap(),
191    ///     format: PixelFormat::ARgb32,
192    ///     data: Bytes::from(vec![0; 40000]),
193    ///     stride: NonZeroUsize::new(400).unwrap(),
194    /// };
195    ///
196    /// let sub_region = original.sub(10, 10, NonZeroU16::new(50).unwrap(), NonZeroU16::new(50).unwrap());
197    /// assert!(sub_region.is_some());
198    /// ```
199    #[must_use]
200    pub fn sub(&self, x: u16, y: u16, width: NonZeroU16, height: NonZeroU16) -> Option<Self> {
201        if x + width.get() > self.width.get() || y + height.get() > self.height.get() {
202            None
203        } else {
204            let bpp = usize::from(self.format.bytes_per_pixel());
205            let start = usize::from(y) * self.stride.get() + usize::from(x) * bpp;
206            let end = start + usize::from(height.get() - 1) * self.stride.get() + usize::from(width.get()) * bpp;
207            Some(Self {
208                x: self.x + x,
209                y: self.y + y,
210                width,
211                height,
212                format: self.format,
213                data: self.data.slice(start..end),
214                stride: self.stride,
215            })
216        }
217    }
218}
219
220impl core::fmt::Debug for BitmapUpdate {
221    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
222        f.debug_struct("BitmapUpdate")
223            .field("x", &self.x)
224            .field("y", &self.y)
225            .field("width", &self.width)
226            .field("height", &self.height)
227            .field("format", &self.format)
228            .field("stride", &self.stride)
229            .finish()
230    }
231}
232
233/// Display Updates receiver for an RDP server
234///
235/// The RDP server will repeatedly call the `next_update` method to receive
236/// display updates which will then be encoded and sent to the client
237///
238/// See [`RdpServerDisplay`] example.
239#[async_trait::async_trait]
240pub trait RdpServerDisplayUpdates {
241    /// # Cancel safety
242    ///
243    /// This method MUST be cancellation safe because it is used in a
244    /// `tokio::select!` statement. If some other branch completes first, it
245    /// MUST be guaranteed that no data is lost.
246    async fn next_update(&mut self) -> Result<Option<DisplayUpdate>>;
247}
248
249/// Display for an RDP server
250///
251/// # Example
252///
253/// ```
254///# use anyhow::Result;
255/// use ironrdp_server::{DesktopSize, DisplayUpdate, RdpServerDisplay, RdpServerDisplayUpdates};
256///
257/// pub struct DisplayUpdates {
258///     receiver: tokio::sync::mpsc::Receiver<DisplayUpdate>,
259/// }
260///
261/// #[async_trait::async_trait]
262/// impl RdpServerDisplayUpdates for DisplayUpdates {
263///     async fn next_update(&mut self) -> anyhow::Result<Option<DisplayUpdate>> {
264///         Ok(self.receiver.recv().await)
265///     }
266/// }
267///
268/// pub struct DisplayHandler {
269///     width: u16,
270///     height: u16,
271/// }
272///
273/// #[async_trait::async_trait]
274/// impl RdpServerDisplay for DisplayHandler {
275///     async fn size(&mut self) -> DesktopSize {
276///         DesktopSize { width: self.width, height: self.height }
277///     }
278///
279///     async fn updates(&mut self) -> Result<Box<dyn RdpServerDisplayUpdates>> {
280///         Ok(Box::new(DisplayUpdates { receiver: todo!() }))
281///     }
282/// }
283/// ```
284#[async_trait::async_trait]
285pub trait RdpServerDisplay: Send {
286    /// This method should return the current size of the display.
287    /// Currently, there is no way for the client to negotiate resolution,
288    /// so the size returned by this method will be enforced.
289    async fn size(&mut self) -> DesktopSize;
290
291    /// Return a display updates receiver
292    async fn updates(&mut self) -> Result<Box<dyn RdpServerDisplayUpdates>>;
293
294    /// Request a new size for the display
295    fn request_layout(&mut self, layout: DisplayControlMonitorLayout) {
296        debug!(?layout, "Requesting layout")
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use core::num::{NonZeroU16, NonZeroUsize};
303
304    use ironrdp_graphics::diff::Rect;
305    use ironrdp_graphics::image_processing::PixelFormat;
306
307    use super::{BitmapUpdate, Framebuffer};
308
309    #[test]
310    fn framebuffer_update() {
311        let width = NonZeroU16::new(800).unwrap();
312        let height = NonZeroU16::new(600).unwrap();
313        let fmt = PixelFormat::ABgr32;
314        let bpp = usize::from(fmt.bytes_per_pixel());
315        let mut fb = Framebuffer::new(width, height, fmt);
316
317        let width = 15;
318        let stride = NonZeroUsize::new(width * bpp).unwrap();
319        let height = 20;
320        let data = vec![1u8; height * stride.get()];
321        let update = BitmapUpdate {
322            x: 1,
323            y: 2,
324            width: NonZeroU16::new(15).unwrap(),
325            height: NonZeroU16::new(20).unwrap(),
326            format: fmt,
327            data: data.into(),
328            stride,
329        };
330        let diffs = vec![Rect::new(2, 3, 4, 5)];
331        fb.update_diffs(&update, &diffs);
332        let data = fb.data;
333        for y in 5..10 {
334            for x in 3..7 {
335                for b in 0..bpp {
336                    assert_eq!(data[y * fb.stride + x * bpp + b], 1);
337                }
338            }
339        }
340    }
341}