From a57d1394dcd3e13a24b044f913797ee10df8efdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 25 Feb 2025 18:56:13 +0400 Subject: [PATCH 01/10] refactor(server)!: drop support for pixelOrder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dealing with multiple formats is sufficiently annoying, there isn't much need for awkward image layout. This was done for efficiency reason for bitmap encoding, but bitmap is really inefficient anyway and very few servers will actually provide bottom to top images (except with GL/GPU textures, but this is not in scope yet). Signed-off-by: Marc-André Lureau --- crates/ironrdp-bench/benches/bench.rs | 2 -- crates/ironrdp-server/src/display.rs | 8 ------ crates/ironrdp-server/src/encoder/bitmap.rs | 31 ++++++--------------- crates/ironrdp-server/src/encoder/mod.rs | 30 +++++--------------- crates/ironrdp/examples/server.rs | 5 ++-- 5 files changed, 17 insertions(+), 59 deletions(-) diff --git a/crates/ironrdp-bench/benches/bench.rs b/crates/ironrdp-bench/benches/bench.rs index 428493c5c..5781a6e45 100644 --- a/crates/ironrdp-bench/benches/bench.rs +++ b/crates/ironrdp-bench/benches/bench.rs @@ -16,7 +16,6 @@ pub fn rfx_enc_tile_bench(c: &mut Criterion) { height: NonZero::new(64).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, data: vec![0; 64 * 64 * 4], - order: ironrdp_server::PixelOrder::BottomToTop, stride: 64 * 4, }; c.bench_function("rfx_enc_tile", |b| b.iter(|| rfx_enc_tile(&bitmap, &quant, algo, 0, 0))); @@ -32,7 +31,6 @@ pub fn rfx_enc_bench(c: &mut Criterion) { height: NonZero::new(2048).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, data: vec![0; 2048 * 2048 * 4], - order: ironrdp_server::PixelOrder::BottomToTop, stride: 64 * 4, }; c.bench_function("rfx_enc", |b| b.iter(|| rfx_enc(&bitmap, &quant, algo))); diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index 1f825e5e2..9d4879e6f 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -24,12 +24,6 @@ pub enum DisplayUpdate { DefaultPointer, } -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum PixelOrder { - TopToBottom, - BottomToTop, -} - #[derive(Clone)] pub struct RGBAPointer { pub width: u16, @@ -73,7 +67,6 @@ pub struct BitmapUpdate { pub width: NonZeroU16, pub height: NonZeroU16, pub format: PixelFormat, - pub order: PixelOrder, pub data: Vec, pub stride: usize, } @@ -86,7 +79,6 @@ impl core::fmt::Debug for BitmapUpdate { .field("width", &self.width) .field("height", &self.height) .field("format", &self.format) - .field("order", &self.order) .finish() } } diff --git a/crates/ironrdp-server/src/encoder/bitmap.rs b/crates/ironrdp-server/src/encoder/bitmap.rs index c502cdf34..1476f29c9 100644 --- a/crates/ironrdp-server/src/encoder/bitmap.rs +++ b/crates/ironrdp-server/src/encoder/bitmap.rs @@ -4,7 +4,7 @@ use ironrdp_graphics::rdp6::{ABgrChannels, ARgbChannels, BgrAChannels, BitmapStr use ironrdp_pdu::bitmap::{self, BitmapData, BitmapUpdateData, Compression}; use ironrdp_pdu::geometry::InclusiveRectangle; -use crate::{BitmapUpdate, PixelOrder}; +use crate::BitmapUpdate; // PERF: we could also remove the need for this buffer pub(crate) struct BitmapEncoder { @@ -43,20 +43,14 @@ impl BitmapEncoder { let encoder = BitmapStreamEncoder::new(usize::from(bitmap.width.get()), height); - let len = match bitmap.order { - PixelOrder::BottomToTop => { - Self::encode_slice(encoder, bitmap.format, &chunk[..row_len], self.buffer.as_mut_slice()) - } + let len = { + let pixels = chunk + .chunks(bitmap.stride) + .map(|row| &row[..row_len]) + .rev() + .flat_map(|row| row.chunks(bytes_per_pixel)); - PixelOrder::TopToBottom => { - let pixels = chunk - .chunks(bitmap.stride) - .map(|row| &row[..row_len]) - .rev() - .flat_map(|row| row.chunks(bytes_per_pixel)); - - Self::encode_iter(encoder, bitmap.format, pixels, self.buffer.as_mut_slice()) - } + Self::encode_iter(encoder, bitmap.format, pixels, self.buffer.as_mut_slice()) }; let data = BitmapData { @@ -84,15 +78,6 @@ impl BitmapEncoder { Ok(cursor.pos()) } - fn encode_slice(mut encoder: BitmapStreamEncoder, format: PixelFormat, src: &[u8], dst: &mut [u8]) -> usize { - match format { - PixelFormat::ARgb32 | PixelFormat::XRgb32 => encoder.encode_bitmap::(src, dst, true).unwrap(), - PixelFormat::RgbA32 | PixelFormat::RgbX32 => encoder.encode_bitmap::(src, dst, true).unwrap(), - PixelFormat::ABgr32 | PixelFormat::XBgr32 => encoder.encode_bitmap::(src, dst, true).unwrap(), - PixelFormat::BgrA32 | PixelFormat::BgrX32 => encoder.encode_bitmap::(src, dst, true).unwrap(), - } - } - fn encode_iter<'a, P>(mut encoder: BitmapStreamEncoder, format: PixelFormat, src: P, dst: &mut [u8]) -> usize where P: Iterator + Clone, diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 9fa7f29a7..6ac4079e5 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -1,7 +1,7 @@ mod bitmap; pub(crate) mod rfx; -use core::{cmp, mem}; +use core::cmp; use anyhow::{Context, Result}; use ironrdp_core::{Encode, WriteCursor}; @@ -14,7 +14,7 @@ use ironrdp_pdu::surface_commands::{ExtendedBitmapDataPdu, SurfaceBitsPdu, Surfa use self::bitmap::BitmapEncoder; use self::rfx::RfxEncoder; use super::BitmapUpdate; -use crate::{ColorPointer, PixelOrder, RGBAPointer}; +use crate::{ColorPointer, RGBAPointer}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] @@ -201,28 +201,12 @@ impl UpdateEncoder { self.set_surface(bitmap, codec_id, &buffer[..len]) } - fn none_update(&mut self, mut bitmap: BitmapUpdate) -> Result> { + fn none_update(&mut self, bitmap: BitmapUpdate) -> Result> { let stride = usize::from(bitmap.format.bytes_per_pixel()) * usize::from(bitmap.width.get()); - let data = match bitmap.order { - PixelOrder::BottomToTop => { - if stride == bitmap.stride { - mem::take(&mut bitmap.data) - } else { - let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); - for row in bitmap.data.chunks(bitmap.stride) { - data.extend_from_slice(&row[..stride]); - } - data - } - } - PixelOrder::TopToBottom => { - let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); - for row in bitmap.data.chunks(bitmap.stride).rev() { - data.extend_from_slice(&row[..stride]); - } - data - } - }; + let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); + for row in bitmap.data.chunks(bitmap.stride).rev() { + data.extend_from_slice(&row[..stride]); + } self.set_surface(bitmap, CodecId::None as u8, &data) } diff --git a/crates/ironrdp/examples/server.rs b/crates/ironrdp/examples/server.rs index 7d0453cea..c881e1229 100644 --- a/crates/ironrdp/examples/server.rs +++ b/crates/ironrdp/examples/server.rs @@ -20,8 +20,8 @@ use ironrdp::server::tokio::sync::mpsc::UnboundedSender; use ironrdp::server::tokio::time::{self, sleep, Duration}; use ironrdp::server::{ tokio, BitmapUpdate, CliprdrServerFactory, Credentials, DisplayUpdate, KeyboardEvent, MouseEvent, PixelFormat, - PixelOrder, RdpServer, RdpServerDisplay, RdpServerDisplayUpdates, RdpServerInputHandler, ServerEvent, - ServerEventSender, SoundServerFactory, TlsIdentityCtx, + RdpServer, RdpServerDisplay, RdpServerDisplayUpdates, RdpServerInputHandler, ServerEvent, ServerEventSender, + SoundServerFactory, TlsIdentityCtx, }; use ironrdp_cliprdr_native::StubCliprdrBackend; use rand::prelude::*; @@ -183,7 +183,6 @@ impl RdpServerDisplayUpdates for DisplayUpdates { width, height, format: PixelFormat::BgrA32, - order: PixelOrder::TopToBottom, data, stride: usize::from(width.get()).checked_mul(4).unwrap(), }; From 204ddee88b6fb0f71f5154045166fef5fb265195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 26 Feb 2025 23:11:31 +0400 Subject: [PATCH 02/10] feat(server): add stride debug info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/display.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index 9d4879e6f..4eae9e66f 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -79,6 +79,7 @@ impl core::fmt::Debug for BitmapUpdate { .field("width", &self.width) .field("height", &self.height) .field("format", &self.format) + .field("stride", &self.stride) .finish() } } From f96085a419cbe124bef6403dc46d3fd4968f4b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 25 Feb 2025 19:14:15 +0400 Subject: [PATCH 03/10] refactor(server)!: use bytes, allowing shareable bitmap data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- Cargo.lock | 1 + crates/ironrdp-bench/benches/bench.rs | 4 ++-- crates/ironrdp-server/Cargo.toml | 1 + crates/ironrdp-server/src/display.rs | 3 ++- crates/ironrdp/examples/server.rs | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47c302543..4fa0162a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2673,6 +2673,7 @@ version = "0.5.0" dependencies = [ "anyhow", "async-trait", + "bytes", "ironrdp-acceptor", "ironrdp-ainput", "ironrdp-async", diff --git a/crates/ironrdp-bench/benches/bench.rs b/crates/ironrdp-bench/benches/bench.rs index 5781a6e45..8fabf29e0 100644 --- a/crates/ironrdp-bench/benches/bench.rs +++ b/crates/ironrdp-bench/benches/bench.rs @@ -15,7 +15,7 @@ pub fn rfx_enc_tile_bench(c: &mut Criterion) { width: NonZero::new(64).unwrap(), height: NonZero::new(64).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, - data: vec![0; 64 * 64 * 4], + data: vec![0; 64 * 64 * 4].into(), stride: 64 * 4, }; c.bench_function("rfx_enc_tile", |b| b.iter(|| rfx_enc_tile(&bitmap, &quant, algo, 0, 0))); @@ -30,7 +30,7 @@ pub fn rfx_enc_bench(c: &mut Criterion) { width: NonZero::new(2048).unwrap(), height: NonZero::new(2048).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, - data: vec![0; 2048 * 2048 * 4], + data: vec![0; 2048 * 2048 * 4].into(), stride: 64 * 4, }; c.bench_function("rfx_enc", |b| b.iter(|| rfx_enc(&bitmap, &quant, algo))); diff --git a/crates/ironrdp-server/Cargo.toml b/crates/ironrdp-server/Cargo.toml index aba6e3d33..e2a26728e 100644 --- a/crates/ironrdp-server/Cargo.toml +++ b/crates/ironrdp-server/Cargo.toml @@ -45,6 +45,7 @@ tracing = { version = "0.1", features = ["log"] } x509-cert = { version = "0.2.5", optional = true } rustls-pemfile = { version = "2.2.0", optional = true } rayon = { version = "1.10.0", optional = true } +bytes = "1" [dev-dependencies] tokio = { version = "1", features = ["sync"] } diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index 4eae9e66f..5f0912f3d 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -1,6 +1,7 @@ use core::num::NonZeroU16; use anyhow::Result; +use bytes::Bytes; use ironrdp_displaycontrol::pdu::DisplayControlMonitorLayout; use ironrdp_pdu::pointer::PointerPositionAttribute; @@ -67,7 +68,7 @@ pub struct BitmapUpdate { pub width: NonZeroU16, pub height: NonZeroU16, pub format: PixelFormat, - pub data: Vec, + pub data: Bytes, pub stride: usize, } diff --git a/crates/ironrdp/examples/server.rs b/crates/ironrdp/examples/server.rs index c881e1229..30729686a 100644 --- a/crates/ironrdp/examples/server.rs +++ b/crates/ironrdp/examples/server.rs @@ -183,7 +183,7 @@ impl RdpServerDisplayUpdates for DisplayUpdates { width, height, format: PixelFormat::BgrA32, - data, + data: data.into(), stride: usize::from(width.get()).checked_mul(4).unwrap(), }; Some(DisplayUpdate::Bitmap(bitmap)) From bba517c01bd2ffacdcff1b40ddda5f5584f7191a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 17 Mar 2025 14:07:23 +0400 Subject: [PATCH 04/10] feat(server): add Framebuffer helper struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will hold the updated bitmap data for the whole framebuffer. Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/display.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index 5f0912f3d..22d502a6b 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -1,7 +1,7 @@ use core::num::NonZeroU16; use anyhow::Result; -use bytes::Bytes; +use bytes::{Bytes, BytesMut}; use ironrdp_displaycontrol::pdu::DisplayControlMonitorLayout; use ironrdp_pdu::pointer::PointerPositionAttribute; @@ -56,6 +56,30 @@ pub struct ColorPointer { pub xor_mask: Vec, } +pub struct Framebuffer { + pub width: NonZeroU16, + pub height: NonZeroU16, + pub format: PixelFormat, + pub data: BytesMut, + pub stride: usize, +} + +impl TryInto for BitmapUpdate { + type Error = &'static str; + + fn try_into(self) -> Result { + assert_eq!(self.top, 0); + assert_eq!(self.left, 0); + Ok(Framebuffer { + width: self.width, + height: self.height, + format: self.format, + data: self.data.try_into_mut().map_err(|_| "BitmapUpdate is shared")?, + stride: self.stride, + }) + } +} + /// Bitmap Display Update /// /// Bitmap updates are encoded using RDP 6.0 compression, fragmented and sent using From 171d3fee2e3e335f28b9b4a27509a65fa165ccb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 25 Feb 2025 23:40:13 +0400 Subject: [PATCH 05/10] refactor(server)!: rename left/top -> x/y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is more idiomatic, and thus less confusing. Signed-off-by: Marc-André Lureau --- crates/ironrdp-bench/benches/bench.rs | 8 ++++---- crates/ironrdp-server/src/display.rs | 12 ++++++------ crates/ironrdp-server/src/encoder/bitmap.rs | 6 +++--- crates/ironrdp-server/src/encoder/mod.rs | 8 ++++---- crates/ironrdp/examples/server.rs | 14 +++++++------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/ironrdp-bench/benches/bench.rs b/crates/ironrdp-bench/benches/bench.rs index 8fabf29e0..1d8a38306 100644 --- a/crates/ironrdp-bench/benches/bench.rs +++ b/crates/ironrdp-bench/benches/bench.rs @@ -10,8 +10,8 @@ pub fn rfx_enc_tile_bench(c: &mut Criterion) { let quant = rfx::Quant::default(); let algo = rfx::EntropyAlgorithm::Rlgr3; let bitmap = BitmapUpdate { - top: 0, - left: 0, + x: 0, + y: 0, width: NonZero::new(64).unwrap(), height: NonZero::new(64).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, @@ -25,8 +25,8 @@ pub fn rfx_enc_bench(c: &mut Criterion) { let quant = rfx::Quant::default(); let algo = rfx::EntropyAlgorithm::Rlgr3; let bitmap = BitmapUpdate { - top: 0, - left: 0, + x: 0, + y: 0, width: NonZero::new(2048).unwrap(), height: NonZero::new(2048).unwrap(), format: ironrdp_server::PixelFormat::ARgb32, diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index 22d502a6b..e70e4e190 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -68,8 +68,8 @@ impl TryInto for BitmapUpdate { type Error = &'static str; fn try_into(self) -> Result { - assert_eq!(self.top, 0); - assert_eq!(self.left, 0); + assert_eq!(self.x, 0); + assert_eq!(self.y, 0); Ok(Framebuffer { width: self.width, height: self.height, @@ -87,8 +87,8 @@ impl TryInto for BitmapUpdate { /// #[derive(Clone)] pub struct BitmapUpdate { - pub top: u16, - pub left: u16, + pub x: u16, + pub y: u16, pub width: NonZeroU16, pub height: NonZeroU16, pub format: PixelFormat, @@ -99,8 +99,8 @@ pub struct BitmapUpdate { impl core::fmt::Debug for BitmapUpdate { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("BitmapUpdate") - .field("top", &self.top) - .field("left", &self.left) + .field("x", &self.x) + .field("y", &self.y) .field("width", &self.width) .field("height", &self.height) .field("format", &self.format) diff --git a/crates/ironrdp-server/src/encoder/bitmap.rs b/crates/ironrdp-server/src/encoder/bitmap.rs index 1476f29c9..71a1ec17a 100644 --- a/crates/ironrdp-server/src/encoder/bitmap.rs +++ b/crates/ironrdp-server/src/encoder/bitmap.rs @@ -39,7 +39,7 @@ impl BitmapEncoder { for (i, chunk) in chunks.enumerate() { let height = chunk.len() / bitmap.stride; - let top = usize::from(bitmap.top) + i * chunk_height; + let top = usize::from(bitmap.y) + i * chunk_height; let encoder = BitmapStreamEncoder::new(usize::from(bitmap.width.get()), height); @@ -55,9 +55,9 @@ impl BitmapEncoder { let data = BitmapData { rectangle: InclusiveRectangle { - left: bitmap.left, + left: bitmap.x, top: u16::try_from(top).unwrap(), - right: bitmap.left + bitmap.width.get() - 1, + right: bitmap.x + bitmap.width.get() - 1, bottom: u16::try_from(top + height - 1).unwrap(), }, width: u16::from(bitmap.width), diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 6ac4079e5..0aa675f9f 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -158,10 +158,10 @@ impl UpdateEncoder { fn set_surface(&mut self, bitmap: BitmapUpdate, codec_id: u8, data: &[u8]) -> Result> { let destination = ExclusiveRectangle { - left: bitmap.left, - top: bitmap.top, - right: bitmap.left + bitmap.width.get(), - bottom: bitmap.top + bitmap.height.get(), + left: bitmap.x, + top: bitmap.y, + right: bitmap.x + bitmap.width.get(), + bottom: bitmap.y + bitmap.height.get(), }; let extended_bitmap_data = ExtendedBitmapDataPdu { bpp: bitmap.format.bytes_per_pixel() * 8, diff --git a/crates/ironrdp/examples/server.rs b/crates/ironrdp/examples/server.rs index 30729686a..873158b58 100644 --- a/crates/ironrdp/examples/server.rs +++ b/crates/ironrdp/examples/server.rs @@ -159,10 +159,10 @@ impl RdpServerDisplayUpdates for DisplayUpdates { sleep(Duration::from_millis(100)).await; let mut rng = thread_rng(); - let top: u16 = rng.gen_range(0..HEIGHT); - let height = NonZeroU16::new(rng.gen_range(1..=HEIGHT.checked_sub(top).unwrap())).unwrap(); - let left: u16 = rng.gen_range(0..WIDTH); - let width = NonZeroU16::new(rng.gen_range(1..=WIDTH.checked_sub(left).unwrap())).unwrap(); + let y: u16 = rng.gen_range(0..HEIGHT); + let height = NonZeroU16::new(rng.gen_range(1..=HEIGHT.checked_sub(y).unwrap())).unwrap(); + let x: u16 = rng.gen_range(0..WIDTH); + let width = NonZeroU16::new(rng.gen_range(1..=WIDTH.checked_sub(x).unwrap())).unwrap(); let capacity = usize::from(width.get()) .checked_mul(usize::from(height.get())) .unwrap() @@ -176,10 +176,10 @@ impl RdpServerDisplayUpdates for DisplayUpdates { data.push(255); } - info!("get_update +{left}+{top} {width}x{height}"); + info!("get_update +{x}+{y} {width}x{height}"); let bitmap = BitmapUpdate { - top, - left, + x, + y, width, height, format: PixelFormat::BgrA32, From 45722f29e5862a1a48aad18f947debccd58d55b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 26 Feb 2025 00:01:20 +0400 Subject: [PATCH 06/10] feat(server): add BitmapUpdate::sub() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/display.rs | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/crates/ironrdp-server/src/display.rs b/crates/ironrdp-server/src/display.rs index e70e4e190..4101aedc0 100644 --- a/crates/ironrdp-server/src/display.rs +++ b/crates/ironrdp-server/src/display.rs @@ -96,6 +96,62 @@ pub struct BitmapUpdate { pub stride: usize, } +impl BitmapUpdate { + /// Extracts a sub-region of the bitmap update. + /// + /// # Parameters + /// + /// - `x`: The x-coordinate of the top-left corner of the sub-region. + /// - `y`: The y-coordinate of the top-left corner of the sub-region. + /// - `width`: The width of the sub-region. + /// - `height`: The height of the sub-region. + /// + /// # Returns + /// + /// An `Option` containing a new `BitmapUpdate` representing the sub-region if the specified + /// dimensions are within the bounds of the original bitmap update, otherwise `None`. + /// + /// # Example + /// + /// ``` + /// # use core::num::NonZeroU16; + /// # use bytes::Bytes; + /// # use ironrdp_graphics::image_processing::PixelFormat; + /// # use ironrdp_server::BitmapUpdate; + /// let original = BitmapUpdate { + /// x: 0, + /// y: 0, + /// width: NonZeroU16::new(100).unwrap(), + /// height: NonZeroU16::new(100).unwrap(), + /// format: PixelFormat::ARgb32, + /// data: Bytes::from(vec![0; 40000]), + /// stride: 400, + /// }; + /// + /// let sub_region = original.sub(10, 10, NonZeroU16::new(50).unwrap(), NonZeroU16::new(50).unwrap()); + /// assert!(sub_region.is_some()); + /// ``` + #[must_use] + pub fn sub(&self, x: u16, y: u16, width: NonZeroU16, height: NonZeroU16) -> Option { + if x + width.get() > self.width.get() || y + height.get() > self.height.get() { + None + } else { + let bpp = usize::from(self.format.bytes_per_pixel()); + let start = usize::from(y) * self.stride + usize::from(x) * bpp; + let end = start + usize::from(height.get() - 1) * self.stride + usize::from(width.get()) * bpp; + Some(Self { + x: self.x + x, + y: self.y + y, + width, + height, + format: self.format, + data: self.data.slice(start..end), + stride: self.stride, + }) + } + } +} + impl core::fmt::Debug for BitmapUpdate { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("BitmapUpdate") From cc2619bbb6a9957d63a7ef255fbc327d8964bda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 3 Feb 2025 22:55:00 +0400 Subject: [PATCH 07/10] refactor(server): split UpdateEncoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bitmap encoder dispatching code was becoming convoluted and the same struct was handling PduEncoding and various bitmap encoding handling. Instead, split UpdateEncoder in different types and concerns. Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/encoder/mod.rs | 214 +++++++++++++++-------- 1 file changed, 137 insertions(+), 77 deletions(-) diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 0aa675f9f..f662f9b2b 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -28,44 +28,25 @@ const MAX_FASTPATH_UPDATE_SIZE: usize = 16_374; const FASTPATH_HEADER_SIZE: usize = 6; pub(crate) struct UpdateEncoder { - buffer: Vec, - bitmap: BitmapEncoder, - remotefx: Option<(RfxEncoder, u8)>, - update: for<'a> fn(&'a mut UpdateEncoder, BitmapUpdate) -> Result>, + pdu_encoder: PduEncoder, + bitmap_updater: BitmapUpdater, } impl UpdateEncoder { pub(crate) fn new(surface_flags: CmdFlags, remotefx: Option<(EntropyBits, u8)>) -> Self { - let update = if !surface_flags.contains(CmdFlags::SET_SURFACE_BITS) { - Self::bitmap_update + let pdu_encoder = PduEncoder::new(); + let bitmap_updater = if !surface_flags.contains(CmdFlags::SET_SURFACE_BITS) { + BitmapUpdater::Bitmap(BitmapHandler::new()) } else if remotefx.is_some() { - Self::remotefx_update + let (algo, id) = remotefx.unwrap(); + BitmapUpdater::RemoteFx(RemoteFxHandler::new(algo, id)) } else { - Self::none_update + BitmapUpdater::None(NoneHandler) }; Self { - buffer: vec![0; 16384], - bitmap: BitmapEncoder::new(), - remotefx: remotefx.map(|(algo, id)| (RfxEncoder::new(algo), id)), - update, - } - } - - fn encode_pdu(&mut self, pdu: impl Encode) -> Result { - loop { - let mut cursor = WriteCursor::new(self.buffer.as_mut_slice()); - match pdu.encode(&mut cursor) { - Err(e) => match e.kind() { - ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { - self.buffer.resize(self.buffer.len() * 2, 0); - debug!("encoder buffer resized to: {}", self.buffer.len() * 2); - } - - _ => Err(e).context("PDU encode error")?, - }, - Ok(()) => return Ok(cursor.pos()), - } + pdu_encoder, + bitmap_updater, } } @@ -88,8 +69,8 @@ impl UpdateEncoder { xor_bpp: 32, color_pointer, }; - let len = self.encode_pdu(ptr)?; - Ok(UpdateFragmenter::new(UpdateCode::NewPointer, &self.buffer[..len])) + let buf = self.pdu_encoder.encode(ptr)?; + Ok(UpdateFragmenter::new(UpdateCode::NewPointer, buf)) } pub(crate) fn color_pointer(&mut self, ptr: ColorPointer) -> Result> { @@ -105,8 +86,8 @@ impl UpdateEncoder { xor_mask: &ptr.xor_mask, and_mask: &ptr.and_mask, }; - let len = self.encode_pdu(ptr)?; - Ok(UpdateFragmenter::new(UpdateCode::ColorPointer, &self.buffer[..len])) + let buf = self.pdu_encoder.encode(ptr)?; + Ok(UpdateFragmenter::new(UpdateCode::ColorPointer, buf)) } #[allow(clippy::unused_self)] @@ -120,31 +101,77 @@ impl UpdateEncoder { } pub(crate) fn pointer_position(&mut self, pos: PointerPositionAttribute) -> Result> { - let len = self.encode_pdu(pos)?; - Ok(UpdateFragmenter::new(UpdateCode::PositionPointer, &self.buffer[..len])) + let buf = self.pdu_encoder.encode(pos)?; + Ok(UpdateFragmenter::new(UpdateCode::PositionPointer, buf)) } pub(crate) fn bitmap(&mut self, bitmap: BitmapUpdate) -> Result> { - let update = self.update; - - update(self, bitmap) + self.bitmap_updater.handle(bitmap, &mut self.pdu_encoder) } pub(crate) fn fragmenter_from_owned(&self, res: UpdateFragmenterOwned) -> UpdateFragmenter<'_> { UpdateFragmenter { code: res.code, index: res.index, - data: &self.buffer[0..res.len], + data: &self.pdu_encoder.buffer[0..res.len], } } +} - fn bitmap_update(&mut self, bitmap: BitmapUpdate) -> Result> { +enum BitmapUpdater { + None(NoneHandler), + Bitmap(BitmapHandler), + RemoteFx(RemoteFxHandler), +} + +impl BitmapUpdater { + fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + match self { + Self::None(up) => up.handle(bitmap, encoder), + Self::Bitmap(up) => up.handle(bitmap, encoder), + Self::RemoteFx(up) => up.handle(bitmap, encoder), + } + } +} + +trait BitmapUpdateHandler { + fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result>; +} + +struct NoneHandler; + +impl BitmapUpdateHandler for NoneHandler { + fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + let stride = usize::from(bitmap.format.bytes_per_pixel()) * usize::from(bitmap.width.get()); + let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); + for row in bitmap.data.chunks(bitmap.stride).rev() { + data.extend_from_slice(&row[..stride]); + } + + encoder.set_surface(bitmap, CodecId::None as u8, &data) + } +} + +struct BitmapHandler { + bitmap: BitmapEncoder, +} + +impl BitmapHandler { + fn new() -> Self { + Self { + bitmap: BitmapEncoder::new(), + } + } +} + +impl BitmapUpdateHandler for BitmapHandler { + fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { let len = loop { - match self.bitmap.encode(&bitmap, self.buffer.as_mut_slice()) { + match self.bitmap.encode(&bitmap, encoder.buffer.as_mut_slice()) { Err(e) => match e.kind() { ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { - self.buffer.resize(self.buffer.len() * 2, 0); - debug!("encoder buffer resized to: {}", self.buffer.len() * 2); + encoder.buffer.resize(encoder.buffer.len() * 2, 0); + debug!("encoder buffer resized to: {}", encoder.buffer.len() * 2); } _ => Err(e).context("bitmap encode error")?, @@ -153,7 +180,71 @@ impl UpdateEncoder { } }; - Ok(UpdateFragmenter::new(UpdateCode::Bitmap, &self.buffer[..len])) + Ok(UpdateFragmenter::new(UpdateCode::Bitmap, &encoder.buffer[..len])) + } +} + +struct RemoteFxHandler { + remotefx: RfxEncoder, + codec_id: u8, +} + +impl RemoteFxHandler { + fn new(algo: EntropyBits, codec_id: u8) -> Self { + Self { + remotefx: RfxEncoder::new(algo), + codec_id, + } + } +} + +impl BitmapUpdateHandler for RemoteFxHandler { + fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + let mut buffer = vec![0; bitmap.data.len()]; + let len = loop { + match self.remotefx.encode(&bitmap, buffer.as_mut_slice()) { + Err(e) => match e.kind() { + ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { + buffer.resize(buffer.len() * 2, 0); + debug!("encoder buffer resized to: {}", buffer.len() * 2); + } + + _ => Err(e).context("RemoteFX encode error")?, + }, + Ok(len) => break len, + } + }; + + encoder.set_surface(bitmap, self.codec_id, &buffer[..len]) + } +} + +struct PduEncoder { + buffer: Vec, +} + +impl PduEncoder { + fn new() -> Self { + Self { buffer: vec![0; 16384] } + } + + fn encode(&mut self, pdu: impl Encode) -> Result<&[u8]> { + let pos = loop { + let mut cursor = WriteCursor::new(self.buffer.as_mut_slice()); + match pdu.encode(&mut cursor) { + Err(e) => match e.kind() { + ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { + self.buffer.resize(self.buffer.len() * 2, 0); + debug!("encoder buffer resized to: {}", self.buffer.len() * 2); + } + + _ => Err(e).context("PDU encode error")?, + }, + Ok(()) => break cursor.pos(), + } + }; + + Ok(&self.buffer[..pos]) } fn set_surface(&mut self, bitmap: BitmapUpdate, codec_id: u8, data: &[u8]) -> Result> { @@ -176,39 +267,8 @@ impl UpdateEncoder { extended_bitmap_data, }; let cmd = SurfaceCommand::SetSurfaceBits(pdu); - let len = self.encode_pdu(cmd)?; - Ok(UpdateFragmenter::new(UpdateCode::SurfaceCommands, &self.buffer[..len])) - } - - fn remotefx_update(&mut self, bitmap: BitmapUpdate) -> Result> { - let (remotefx, codec_id) = self.remotefx.as_mut().unwrap(); - let codec_id = *codec_id; - let mut buffer = vec![0; bitmap.data.len()]; - let len = loop { - match remotefx.encode(&bitmap, buffer.as_mut_slice()) { - Err(e) => match e.kind() { - ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { - buffer.resize(buffer.len() * 2, 0); - debug!("encoder buffer resized to: {}", self.buffer.len() * 2); - } - - _ => Err(e).context("RemoteFX encode error")?, - }, - Ok(len) => break len, - } - }; - - self.set_surface(bitmap, codec_id, &buffer[..len]) - } - - fn none_update(&mut self, bitmap: BitmapUpdate) -> Result> { - let stride = usize::from(bitmap.format.bytes_per_pixel()) * usize::from(bitmap.width.get()); - let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); - for row in bitmap.data.chunks(bitmap.stride).rev() { - data.extend_from_slice(&row[..stride]); - } - - self.set_surface(bitmap, CodecId::None as u8, &data) + let buf = self.encode(cmd)?; + Ok(UpdateFragmenter::new(UpdateCode::SurfaceCommands, buf)) } } From fa89695b71856cde3a12ba2d9ad0abc448ce77c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 12 Feb 2025 11:51:10 +0400 Subject: [PATCH 08/10] feat(server): implement some Encoder Debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/encoder/mod.rs | 27 +++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index f662f9b2b..926d6c9b8 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -1,7 +1,7 @@ mod bitmap; pub(crate) mod rfx; -use core::cmp; +use core::{cmp, fmt}; use anyhow::{Context, Result}; use ironrdp_core::{Encode, WriteCursor}; @@ -32,6 +32,14 @@ pub(crate) struct UpdateEncoder { bitmap_updater: BitmapUpdater, } +impl fmt::Debug for UpdateEncoder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UpdateEncoder") + .field("bitmap_update", &self.bitmap_updater) + .finish() + } +} + impl UpdateEncoder { pub(crate) fn new(surface_flags: CmdFlags, remotefx: Option<(EntropyBits, u8)>) -> Self { let pdu_encoder = PduEncoder::new(); @@ -118,6 +126,7 @@ impl UpdateEncoder { } } +#[derive(Debug)] enum BitmapUpdater { None(NoneHandler), Bitmap(BitmapHandler), @@ -138,6 +147,7 @@ trait BitmapUpdateHandler { fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result>; } +#[derive(Debug)] struct NoneHandler; impl BitmapUpdateHandler for NoneHandler { @@ -156,6 +166,12 @@ struct BitmapHandler { bitmap: BitmapEncoder, } +impl fmt::Debug for BitmapHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BitmapHandler").finish() + } +} + impl BitmapHandler { fn new() -> Self { Self { @@ -184,6 +200,7 @@ impl BitmapUpdateHandler for BitmapHandler { } } +#[derive(Debug)] struct RemoteFxHandler { remotefx: RfxEncoder, codec_id: u8, @@ -284,6 +301,14 @@ pub(crate) struct UpdateFragmenter<'a> { data: &'a [u8], } +impl fmt::Debug for UpdateFragmenter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UpdateFragmenter") + .field("len", &self.data.len()) + .finish() + } +} + impl<'a> UpdateFragmenter<'a> { pub(crate) fn new(code: UpdateCode, data: &'a [u8]) -> Self { Self { code, index: 0, data } From 17a19c715d4ab0b66a2ed228750cf56616d8212f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 19 Feb 2025 17:28:40 +0400 Subject: [PATCH 09/10] refactor(server): pass bitmapUpdate by ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/encoder/mod.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 926d6c9b8..26d8e424b 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -114,7 +114,7 @@ impl UpdateEncoder { } pub(crate) fn bitmap(&mut self, bitmap: BitmapUpdate) -> Result> { - self.bitmap_updater.handle(bitmap, &mut self.pdu_encoder) + self.bitmap_updater.handle(&bitmap, &mut self.pdu_encoder) } pub(crate) fn fragmenter_from_owned(&self, res: UpdateFragmenterOwned) -> UpdateFragmenter<'_> { @@ -134,7 +134,7 @@ enum BitmapUpdater { } impl BitmapUpdater { - fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + fn handle<'a>(&mut self, bitmap: &BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { match self { Self::None(up) => up.handle(bitmap, encoder), Self::Bitmap(up) => up.handle(bitmap, encoder), @@ -144,20 +144,19 @@ impl BitmapUpdater { } trait BitmapUpdateHandler { - fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result>; + fn handle<'a>(&mut self, bitmap: &BitmapUpdate, encoder: &'a mut PduEncoder) -> Result>; } #[derive(Debug)] struct NoneHandler; impl BitmapUpdateHandler for NoneHandler { - fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + fn handle<'a>(&mut self, bitmap: &BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { let stride = usize::from(bitmap.format.bytes_per_pixel()) * usize::from(bitmap.width.get()); let mut data = Vec::with_capacity(stride * usize::from(bitmap.height.get())); for row in bitmap.data.chunks(bitmap.stride).rev() { data.extend_from_slice(&row[..stride]); } - encoder.set_surface(bitmap, CodecId::None as u8, &data) } } @@ -181,9 +180,9 @@ impl BitmapHandler { } impl BitmapUpdateHandler for BitmapHandler { - fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + fn handle<'a>(&mut self, bitmap: &BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { let len = loop { - match self.bitmap.encode(&bitmap, encoder.buffer.as_mut_slice()) { + match self.bitmap.encode(bitmap, encoder.buffer.as_mut_slice()) { Err(e) => match e.kind() { ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { encoder.buffer.resize(encoder.buffer.len() * 2, 0); @@ -216,10 +215,10 @@ impl RemoteFxHandler { } impl BitmapUpdateHandler for RemoteFxHandler { - fn handle<'a>(&mut self, bitmap: BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { + fn handle<'a>(&mut self, bitmap: &BitmapUpdate, encoder: &'a mut PduEncoder) -> Result> { let mut buffer = vec![0; bitmap.data.len()]; let len = loop { - match self.remotefx.encode(&bitmap, buffer.as_mut_slice()) { + match self.remotefx.encode(bitmap, buffer.as_mut_slice()) { Err(e) => match e.kind() { ironrdp_core::EncodeErrorKind::NotEnoughBytes { .. } => { buffer.resize(buffer.len() * 2, 0); @@ -264,7 +263,7 @@ impl PduEncoder { Ok(&self.buffer[..pos]) } - fn set_surface(&mut self, bitmap: BitmapUpdate, codec_id: u8, data: &[u8]) -> Result> { + fn set_surface(&mut self, bitmap: &BitmapUpdate, codec_id: u8, data: &[u8]) -> Result> { let destination = ExclusiveRectangle { left: bitmap.x, top: bitmap.y, From a48191655db6390c0a347a9e6f96e85916f65808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 20 Feb 2025 13:42:06 +0400 Subject: [PATCH 10/10] feat(server): keep last full-frame/desktop update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It should reflect client drawing state. In following changes, we will fix it to draw bitmap updates on it, to keep it up to date. Signed-off-by: Marc-André Lureau --- crates/ironrdp-server/src/encoder/mod.rs | 27 +++++++++++++++++++++--- crates/ironrdp-server/src/server.rs | 4 +++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 26d8e424b..fffddea3f 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod rfx; use core::{cmp, fmt}; use anyhow::{Context, Result}; +use ironrdp_acceptor::DesktopSize; use ironrdp_core::{Encode, WriteCursor}; use ironrdp_pdu::fast_path::{EncryptionFlags, FastPathHeader, FastPathUpdatePdu, Fragmentation, UpdateCode}; use ironrdp_pdu::geometry::ExclusiveRectangle; @@ -14,7 +15,7 @@ use ironrdp_pdu::surface_commands::{ExtendedBitmapDataPdu, SurfaceBitsPdu, Surfa use self::bitmap::BitmapEncoder; use self::rfx::RfxEncoder; use super::BitmapUpdate; -use crate::{ColorPointer, RGBAPointer}; +use crate::{ColorPointer, Framebuffer, RGBAPointer}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] @@ -28,6 +29,9 @@ const MAX_FASTPATH_UPDATE_SIZE: usize = 16_374; const FASTPATH_HEADER_SIZE: usize = 6; pub(crate) struct UpdateEncoder { + desktop_size: DesktopSize, + // FIXME: draw updates on the framebuffer + framebuffer: Option, pdu_encoder: PduEncoder, bitmap_updater: BitmapUpdater, } @@ -41,7 +45,7 @@ impl fmt::Debug for UpdateEncoder { } impl UpdateEncoder { - pub(crate) fn new(surface_flags: CmdFlags, remotefx: Option<(EntropyBits, u8)>) -> Self { + pub(crate) fn new(desktop_size: DesktopSize, surface_flags: CmdFlags, remotefx: Option<(EntropyBits, u8)>) -> Self { let pdu_encoder = PduEncoder::new(); let bitmap_updater = if !surface_flags.contains(CmdFlags::SET_SURFACE_BITS) { BitmapUpdater::Bitmap(BitmapHandler::new()) @@ -53,11 +57,17 @@ impl UpdateEncoder { }; Self { + desktop_size, + framebuffer: None, pdu_encoder, bitmap_updater, } } + pub(crate) fn set_desktop_size(&mut self, size: DesktopSize) { + self.desktop_size = size; + } + pub(crate) fn rgba_pointer(&mut self, ptr: RGBAPointer) -> Result> { let xor_mask = ptr.data; @@ -114,7 +124,18 @@ impl UpdateEncoder { } pub(crate) fn bitmap(&mut self, bitmap: BitmapUpdate) -> Result> { - self.bitmap_updater.handle(&bitmap, &mut self.pdu_encoder) + let res = self.bitmap_updater.handle(&bitmap, &mut self.pdu_encoder); + if bitmap.x == 0 + && bitmap.y == 0 + && bitmap.width.get() == self.desktop_size.width + && bitmap.height.get() == self.desktop_size.height + { + match bitmap.try_into() { + Ok(framebuffer) => self.framebuffer = Some(framebuffer), + Err(err) => warn!("Failed to convert bitmap to framebuffer: {}", err), + } + } + res } pub(crate) fn fragmenter_from_owned(&self, res: UpdateFragmenterOwned) -> UpdateFragmenter<'_> { diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index 854b0923f..402dfa35b 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -436,6 +436,7 @@ impl RdpServer { DisplayUpdate::PointerPosition(pos) => encoder.pointer_position(pos), DisplayUpdate::Resize(desktop_size) => { debug!(?desktop_size, "Display resize"); + encoder.set_desktop_size(desktop_size); deactivate_all(io_channel_id, user_channel_id, writer).await?; return Ok((RunState::DeactivationReactivation { desktop_size }, encoder)); } @@ -741,7 +742,8 @@ impl RdpServer { } } - let encoder = UpdateEncoder::new(surface_flags, rfxcodec); + let desktop_size = self.display.lock().await.size().await; + let encoder = UpdateEncoder::new(desktop_size, surface_flags, rfxcodec); let state = self .client_loop(reader, writer, result.io_channel_id, result.user_channel_id, encoder)