diff --git a/Cargo.lock b/Cargo.lock index b59651314..2c40702b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2330,6 +2330,7 @@ dependencies = [ "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", + "ironrdp-egfx", "ironrdp-graphics", "ironrdp-input", "ironrdp-pdu", @@ -2513,6 +2514,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "ironrdp-egfx" +version = "0.1.1" +dependencies = [ + "bit_field", + "bitflags 2.6.0", + "ironrdp-core", + "ironrdp-dvc", + "ironrdp-graphics", + "ironrdp-pdu", + "tracing", +] + [[package]] name = "ironrdp-error" version = "0.1.1" @@ -2535,6 +2549,7 @@ dependencies = [ "ironrdp-cliprdr-format", "ironrdp-core", "ironrdp-displaycontrol", + "ironrdp-egfx", "ironrdp-graphics", "ironrdp-pdu", "ironrdp-rdpdr", @@ -2718,6 +2733,7 @@ dependencies = [ "ironrdp-core", "ironrdp-displaycontrol", "ironrdp-dvc", + "ironrdp-egfx", "ironrdp-fuzzing", "ironrdp-graphics", "ironrdp-input", diff --git a/Cargo.toml b/Cargo.toml index b06800108..9a7bd7311 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ ironrdp-core = { version = "0.1", path = "crates/ironrdp-core" } ironrdp-connector = { version = "0.2", path = "crates/ironrdp-connector" } ironrdp-dvc = { version = "0.1", path = "crates/ironrdp-dvc" } ironrdp-displaycontrol = { version = "0.1", path = "crates/ironrdp-displaycontrol" } +ironrdp-egfx = { version = "0.1", path = "crates/ironrdp-egfx" } ironrdp-error = { version = "0.1", path = "crates/ironrdp-error" } ironrdp-futures = { version = "0.1", path = "crates/ironrdp-futures" } ironrdp-fuzzing = { path = "crates/ironrdp-fuzzing" } diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index 4d7005267..e761e6dca 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -41,6 +41,7 @@ ironrdp = { workspace = true, features = [ "rdpsnd", "cliprdr", "displaycontrol", + "egfx", "connector" ] } ironrdp-cliprdr-native.workspace = true diff --git a/crates/ironrdp-client/src/config.rs b/crates/ironrdp-client/src/config.rs index 2595d8e0e..c10fd0656 100644 --- a/crates/ironrdp-client/src/config.rs +++ b/crates/ironrdp-client/src/config.rs @@ -322,6 +322,7 @@ impl Config { request_data: None, pointer_software_rendering: true, performance_flags: PerformanceFlags::default(), + support_gfx: true, }; Ok(Self { diff --git a/crates/ironrdp-client/src/rdp.rs b/crates/ironrdp-client/src/rdp.rs index fedd012eb..ce82bb7ad 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -3,11 +3,13 @@ use ironrdp::connector::connection_activation::ConnectionActivationState; use ironrdp::connector::{ConnectionResult, ConnectorResult}; use ironrdp::displaycontrol::client::DisplayControlClient; use ironrdp::displaycontrol::pdu::MonitorLayoutEntry; +use ironrdp::egfx::client::{GraphicsPipelineClient, GraphicsPipelineHandler}; +use ironrdp::egfx::pdu::{GfxPdu, StartFramePdu}; use ironrdp::graphics::image_processing::PixelFormat; use ironrdp::pdu::input::fast_path::FastPathInputEvent; use ironrdp::session::image::DecodedImage; use ironrdp::session::{fast_path, ActiveStage, ActiveStageOutput, GracefulDisconnectReason, SessionResult}; -use ironrdp::{cliprdr, connector, rdpdr, rdpsnd, session}; +use ironrdp::{cliprdr, connector, egfx, rdpdr, rdpsnd, session}; use ironrdp_core::WriteBuf; use ironrdp_rdpsnd_native::cpal; use ironrdp_tokio::{single_sequence_step_read, split_tokio_framed, FramedWrite}; @@ -92,6 +94,49 @@ impl RdpClient { } } +struct GraphicsPipeline { + caps: Option, + start_frame: Option, +} + +impl GraphicsPipeline { + fn new() -> Self { + Self { + caps: None, + start_frame: None, + } + } +} + +impl GraphicsPipelineHandler for GraphicsPipeline { + fn capabilities(&self) -> Vec { + vec![egfx::pdu::CapabilitySet::V8 { + flags: egfx::pdu::CapabilitiesV8Flags::empty(), + }] + } + + fn handle_pdu(&mut self, pdu: GfxPdu) { + trace!(?pdu); + match pdu { + GfxPdu::CapabilitiesConfirm(pdu) => { + trace!(?pdu); + self.caps = Some(pdu.0); + } + GfxPdu::StartFrame(pdu) => { + trace!(?pdu); + self.start_frame = Some(pdu); + } + GfxPdu::EndFrame(pdu) => { + trace!(?pdu); + self.start_frame = None; + } + pdu => { + debug!(?pdu); + } + } + } +} + enum RdpControlFlow { ReconnectWithNewSize { width: u16, height: u16 }, TerminatedGracefully(GracefulDisconnectReason), @@ -118,7 +163,9 @@ async fn connect( let mut connector = connector::ClientConnector::new(config.connector.clone()) .with_server_addr(server_addr) .with_static_channel( - ironrdp::dvc::DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))), + ironrdp::dvc::DrdynvcClient::new() + .with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))) + .with_dynamic_channel(GraphicsPipelineClient::new(Box::new(GraphicsPipeline::new()))), ) .with_static_channel(rdpsnd::client::Rdpsnd::new(Box::new(cpal::RdpsndBackend::new()))) .with_static_channel(rdpdr::Rdpdr::new(Box::new(NoopRdpdrBackend {}), "IronRDP".to_owned()).with_smartcard(0)); diff --git a/crates/ironrdp-connector/src/connection.rs b/crates/ironrdp-connector/src/connection.rs index c42eabdc5..9703711e0 100644 --- a/crates/ironrdp-connector/src/connection.rs +++ b/crates/ironrdp-connector/src/connection.rs @@ -662,9 +662,12 @@ fn create_gcc_blocks<'a>( | ClientEarlyCapabilityFlags::SUPPORT_ERR_INFO_PDU | ClientEarlyCapabilityFlags::STRONG_ASYMMETRIC_KEYS | ClientEarlyCapabilityFlags::SUPPORT_SKIP_CHANNELJOIN; - // TODO(#136): support for ClientEarlyCapabilityFlags::SUPPORT_STATUS_INFO_PDU + if config.support_gfx { + early_capability_flags |= ClientEarlyCapabilityFlags::SUPPORT_DYN_VC_GFX_PROTOCOL; + } + if max_color_depth == 32 { early_capability_flags |= ClientEarlyCapabilityFlags::WANT_32_BPP_SESSION; } diff --git a/crates/ironrdp-connector/src/lib.rs b/crates/ironrdp-connector/src/lib.rs index f4015d1fb..1d581c726 100644 --- a/crates/ironrdp-connector/src/lib.rs +++ b/crates/ironrdp-connector/src/lib.rs @@ -176,6 +176,8 @@ pub struct Config { /// If true, the INFO_AUTOLOGON flag is set in the [`ClientInfoPdu`](ironrdp_pdu::rdp::ClientInfoPdu) pub autologon: bool, pub license_cache: Option>, + /// If true, the SUPPORT_DYN_VC_GFX_PROTOCOL flag is set + pub support_gfx: bool, // FIXME(@CBenoit): these are client-only options, not part of the connector. pub no_server_pointer: bool, diff --git a/crates/ironrdp-egfx/CHANGELOG.md b/crates/ironrdp-egfx/CHANGELOG.md new file mode 100644 index 000000000..52e80a16a --- /dev/null +++ b/crates/ironrdp-egfx/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + diff --git a/crates/ironrdp-egfx/Cargo.toml b/crates/ironrdp-egfx/Cargo.toml new file mode 100644 index 000000000..a32f42153 --- /dev/null +++ b/crates/ironrdp-egfx/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ironrdp-egfx" +version = "0.1.1" +readme = "README.md" +description = "Graphics pipeline dynamic channel extension implementation" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +authors.workspace = true +keywords.workspace = true +categories.workspace = true + +[lib] +doctest = false +test = false + +[dependencies] +bit_field = "0.10.2" +bitflags.workspace = true +ironrdp-core.workspace = true +ironrdp-dvc.workspace = true +ironrdp-graphics.workspace = true +ironrdp-pdu.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/crates/ironrdp-egfx/LICENSE-APACHE b/crates/ironrdp-egfx/LICENSE-APACHE new file mode 120000 index 000000000..1cd601d0a --- /dev/null +++ b/crates/ironrdp-egfx/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/ironrdp-egfx/LICENSE-MIT b/crates/ironrdp-egfx/LICENSE-MIT new file mode 120000 index 000000000..b2cfbdc7b --- /dev/null +++ b/crates/ironrdp-egfx/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/crates/ironrdp-egfx/README.md b/crates/ironrdp-egfx/README.md new file mode 100644 index 000000000..d5d37c930 --- /dev/null +++ b/crates/ironrdp-egfx/README.md @@ -0,0 +1,9 @@ +# IronRDP Graphics Pipeline Virtual Channel Extension [MS-RDPEGFX][1] implementation. + +Display pipeline Virtual Channel Extension [MS-RDPEGFX][1] implementation. + +This library includes: +- Display pipeline DVC PDUs parsing +- Display pipeline DVC processing (TODO) + +[1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0 diff --git a/crates/ironrdp-egfx/src/client.rs b/crates/ironrdp-egfx/src/client.rs new file mode 100644 index 000000000..a0e1a6fea --- /dev/null +++ b/crates/ironrdp-egfx/src/client.rs @@ -0,0 +1,70 @@ +use ironrdp_core::{impl_as_any, ReadCursor}; +use ironrdp_dvc::{DvcClientProcessor, DvcMessage, DvcProcessor}; +use ironrdp_graphics::zgfx; +use ironrdp_pdu::{decode_cursor, decode_err, PduResult}; +use tracing::trace; + +use crate::{ + pdu::{CapabilitiesAdvertisePdu, CapabilitiesV8Flags, CapabilitySet, GfxPdu}, + CHANNEL_NAME, +}; + +pub trait GraphicsPipelineHandler: Send { + fn capabilities(&self) -> Vec { + vec![CapabilitySet::V8 { + flags: CapabilitiesV8Flags::empty(), + }] + } + + fn handle_pdu(&mut self, pdu: GfxPdu) { + trace!(?pdu); + } +} + +/// A client for the Graphics Pipeline Virtual Channel. +pub struct GraphicsPipelineClient { + handler: Box, + decompressor: zgfx::Decompressor, + decompressed_buffer: Vec, +} + +impl GraphicsPipelineClient { + pub fn new(handler: Box) -> Self { + Self { + handler, + decompressor: zgfx::Decompressor::new(), + decompressed_buffer: Vec::with_capacity(1024 * 16), + } + } +} + +impl_as_any!(GraphicsPipelineClient); + +impl DvcProcessor for GraphicsPipelineClient { + fn channel_name(&self) -> &str { + CHANNEL_NAME + } + + fn start(&mut self, _channel_id: u32) -> PduResult> { + let pdu = GfxPdu::CapabilitiesAdvertise(CapabilitiesAdvertisePdu(self.handler.capabilities())); + + Ok(vec![Box::new(pdu)]) + } + + fn process(&mut self, _channel_id: u32, payload: &[u8]) -> PduResult> { + self.decompressed_buffer.clear(); + self.decompressor + .decompress(payload, &mut self.decompressed_buffer) + .map_err(|e| decode_err!(e))?; + + let mut cursor = ReadCursor::new(self.decompressed_buffer.as_slice()); + while !cursor.is_empty() { + let pdu = decode_cursor(&mut cursor).map_err(|e| decode_err!(e))?; + self.handler.handle_pdu(pdu); + } + + Ok(vec![]) + } +} + +impl DvcClientProcessor for GraphicsPipelineClient {} diff --git a/crates/ironrdp-egfx/src/lib.rs b/crates/ironrdp-egfx/src/lib.rs new file mode 100644 index 000000000..c09d5df7f --- /dev/null +++ b/crates/ironrdp-egfx/src/lib.rs @@ -0,0 +1,8 @@ +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] + +pub const CHANNEL_NAME: &str = "Microsoft::Windows::RDS::Graphics"; + +pub mod client; +pub mod pdu; +pub mod server; diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs b/crates/ironrdp-egfx/src/pdu/avc.rs similarity index 93% rename from crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs rename to crates/ironrdp-egfx/src/pdu/avc.rs index ace2d9a8c..bdeb3e494 100644 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs +++ b/crates/ironrdp-egfx/src/pdu/avc.rs @@ -1,13 +1,12 @@ -use core::fmt::Debug; +use core::fmt; -use bit_field::BitField; -use bitflags::bitflags; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, +use ironrdp_pdu::{ + cast_length, ensure_fixed_part_size, ensure_size, geometry::InclusiveRectangle, invalid_field_err, Decode, + DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor, }; -use crate::geometry::InclusiveRectangle; +use bit_field::BitField; +use bitflags::bitflags; #[derive(Debug, Clone, PartialEq, Eq)] pub struct QuantQuality { @@ -66,8 +65,8 @@ pub struct Avc420BitmapStream<'a> { pub data: &'a [u8], } -impl Debug for Avc420BitmapStream<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +impl fmt::Debug for Avc420BitmapStream<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Avc420BitmapStream") .field("rectangles", &self.rectangles) .field("quant_qual_vals", &self.quant_qual_vals) @@ -158,7 +157,7 @@ impl Encode for Avc444BitmapStream<'_> { let mut stream_info = 0u32; stream_info.set_bits(0..30, cast_length!("stream1size", self.stream1.size())?); - stream_info.set_bits(30..32, self.encoding.bits() as u32); + stream_info.set_bits(30..32, self.encoding.bits().into()); dst.write_u32(stream_info); self.stream1.encode(dst)?; if let Some(stream) = self.stream2.as_ref() { @@ -188,7 +187,7 @@ impl<'de> Decode<'de> for Avc444BitmapStream<'de> { let stream_info = src.read_u32(); let stream_len = stream_info.get_bits(0..30); - let encoding = Encoding::from_bits_truncate(stream_info.get_bits(30..32) as u8); + let encoding = Encoding::from_bits_truncate(stream_info.get_bits(30..32).try_into().unwrap()); if stream_len == 0 { if encoding == Encoding::LUMA_AND_CHROMA { diff --git a/crates/ironrdp-egfx/src/pdu/cmd.rs b/crates/ironrdp-egfx/src/pdu/cmd.rs new file mode 100644 index 000000000..d6ff9f49d --- /dev/null +++ b/crates/ironrdp-egfx/src/pdu/cmd.rs @@ -0,0 +1,2067 @@ +use core::fmt; + +use ironrdp_core::{ + ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor, +}; +use ironrdp_dvc::DvcEncode; +use ironrdp_pdu::{ + cast_length, ensure_size, gcc::Monitor, geometry::InclusiveRectangle, read_padding, write_padding, DecodeError, +}; + +use bit_field::BitField; +use bitflags::bitflags; +use tracing::warn; + +use super::{Color, PixelFormat, Point}; + +const RDPGFX_CMDID_WIRETOSURFACE_1: u16 = 0x0001; +const RDPGFX_CMDID_WIRETOSURFACE_2: u16 = 0x0002; +const RDPGFX_CMDID_DELETEENCODINGCONTEXT: u16 = 0x0003; +const RDPGFX_CMDID_SOLIDFILL: u16 = 0x0004; +const RDPGFX_CMDID_SURFACETOSURFACE: u16 = 0x0005; +const RDPGFX_CMDID_SURFACETOCACHE: u16 = 0x0006; +const RDPGFX_CMDID_CACHETOSURFACE: u16 = 0x0007; +const RDPGFX_CMDID_EVICTCACHEENTRY: u16 = 0x0008; +const RDPGFX_CMDID_CREATESURFACE: u16 = 0x0009; +const RDPGFX_CMDID_DELETESURFACE: u16 = 0x000a; +const RDPGFX_CMDID_STARTFRAME: u16 = 0x000b; +const RDPGFX_CMDID_ENDFRAME: u16 = 0x000c; +const RDPGFX_CMDID_FRAMEACKNOWLEDGE: u16 = 0x000d; +const RDPGFX_CMDID_RESETGRAPHICS: u16 = 0x000e; +const RDPGFX_CMDID_MAPSURFACETOOUTPUT: u16 = 0x000f; +const RDPGFX_CMDID_CACHEIMPORTOFFER: u16 = 0x0010; +const RDPGFX_CMDID_CACHEIMPORTREPLY: u16 = 0x0011; +const RDPGFX_CMDID_CAPSADVERTISE: u16 = 0x0012; +const RDPGFX_CMDID_CAPSCONFIRM: u16 = 0x0013; +const RDPGFX_CMDID_MAPSURFACETOWINDOW: u16 = 0x0015; +const RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE: u16 = 0x0016; +const RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT: u16 = 0x0017; +const RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW: u16 = 0x0018; + +const MAX_RESET_GRAPHICS_WIDTH_HEIGHT: u32 = 32_766; +const MONITOR_COUNT_MAX: u32 = 16; +const RESET_GRAPHICS_PDU_SIZE: usize = 340 - GfxPdu::FIXED_PART_SIZE; + +/// Display Pipeline Virtual Channel message (PDU prefixed with `RDPGFX_HEADER`) +/// +/// INVARIANTS: size of encoded inner PDU is always less than `u32::MAX - Self::FIXED_PART_SIZE` +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GfxPdu { + WireToSurface1(WireToSurface1Pdu), + WireToSurface2(WireToSurface2Pdu), + DeleteEncodingContext(DeleteEncodingContextPdu), + SolidFill(SolidFillPdu), + SurfaceToSurface(SurfaceToSurfacePdu), + SurfaceToCache(SurfaceToCachePdu), + CacheToSurface(CacheToSurfacePdu), + EvictCacheEntry(EvictCacheEntryPdu), + CreateSurface(CreateSurfacePdu), + DeleteSurface(DeleteSurfacePdu), + StartFrame(StartFramePdu), + EndFrame(EndFramePdu), + FrameAcknowledge(FrameAcknowledgePdu), + ResetGraphics(ResetGraphicsPdu), + MapSurfaceToOutput(MapSurfaceToOutputPdu), + CacheImportOffer(CacheImportOfferPdu), + CacheImportReply(CacheImportReplyPdu), + CapabilitiesAdvertise(CapabilitiesAdvertisePdu), + CapabilitiesConfirm(CapabilitiesConfirmPdu), + MapSurfaceToWindow(MapSurfaceToWindowPdu), + QoeFrameAcknowledge(QoeFrameAcknowledgePdu), + MapSurfaceToScaledOutput(MapSurfaceToScaledOutputPdu), + MapSurfaceToScaledWindow(MapSurfaceToScaledWindowPdu), +} + +/// 2.2.1.5 RDPGFX_HEADER +/// +/// [2.2.1.5] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/ed075b10-168d-4f56-8348-4029940d7959 +impl GfxPdu { + const NAME: &'static str = "RDPGFX_HEADER"; + + const FIXED_PART_SIZE: usize = 2 /* CmdId */ + 2 /* flags */ + 4 /* Length */; +} + +impl Encode for GfxPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + let (cmdid, payload_length) = match self { + GfxPdu::WireToSurface1(pdu) => (RDPGFX_CMDID_WIRETOSURFACE_1, pdu.size()), + GfxPdu::WireToSurface2(pdu) => (RDPGFX_CMDID_WIRETOSURFACE_2, pdu.size()), + GfxPdu::DeleteEncodingContext(pdu) => (RDPGFX_CMDID_DELETEENCODINGCONTEXT, pdu.size()), + GfxPdu::SolidFill(pdu) => (RDPGFX_CMDID_SOLIDFILL, pdu.size()), + GfxPdu::SurfaceToSurface(pdu) => (RDPGFX_CMDID_SURFACETOSURFACE, pdu.size()), + GfxPdu::SurfaceToCache(pdu) => (RDPGFX_CMDID_SURFACETOCACHE, pdu.size()), + GfxPdu::CacheToSurface(pdu) => (RDPGFX_CMDID_CACHETOSURFACE, pdu.size()), + GfxPdu::EvictCacheEntry(pdu) => (RDPGFX_CMDID_EVICTCACHEENTRY, pdu.size()), + GfxPdu::CreateSurface(pdu) => (RDPGFX_CMDID_CREATESURFACE, pdu.size()), + GfxPdu::DeleteSurface(pdu) => (RDPGFX_CMDID_DELETESURFACE, pdu.size()), + GfxPdu::StartFrame(pdu) => (RDPGFX_CMDID_STARTFRAME, pdu.size()), + GfxPdu::EndFrame(pdu) => (RDPGFX_CMDID_ENDFRAME, pdu.size()), + GfxPdu::FrameAcknowledge(pdu) => (RDPGFX_CMDID_FRAMEACKNOWLEDGE, pdu.size()), + GfxPdu::ResetGraphics(pdu) => (RDPGFX_CMDID_RESETGRAPHICS, pdu.size()), + GfxPdu::MapSurfaceToOutput(pdu) => (RDPGFX_CMDID_MAPSURFACETOOUTPUT, pdu.size()), + GfxPdu::CacheImportOffer(pdu) => (RDPGFX_CMDID_CACHEIMPORTOFFER, pdu.size()), + GfxPdu::CacheImportReply(pdu) => (RDPGFX_CMDID_CACHEIMPORTREPLY, pdu.size()), + GfxPdu::CapabilitiesAdvertise(pdu) => (RDPGFX_CMDID_CAPSADVERTISE, pdu.size()), + GfxPdu::CapabilitiesConfirm(pdu) => (RDPGFX_CMDID_CAPSCONFIRM, pdu.size()), + GfxPdu::MapSurfaceToWindow(pdu) => (RDPGFX_CMDID_MAPSURFACETOWINDOW, pdu.size()), + GfxPdu::QoeFrameAcknowledge(pdu) => (RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE, pdu.size()), + GfxPdu::MapSurfaceToScaledOutput(pdu) => (RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT, pdu.size()), + GfxPdu::MapSurfaceToScaledWindow(pdu) => (RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW, pdu.size()), + }; + + // This will never overflow as per invariants. + #[allow(clippy::arithmetic_side_effects)] + let pdu_size = payload_length + Self::FIXED_PART_SIZE; + + // Write `RDPGFX_HEADER` fields. + dst.write_u16(cmdid); + dst.write_u16(0); /* flags */ + dst.write_u32(pdu_size.try_into().unwrap()); + + match self { + GfxPdu::WireToSurface1(pdu) => pdu.encode(dst), + GfxPdu::WireToSurface2(pdu) => pdu.encode(dst), + GfxPdu::DeleteEncodingContext(pdu) => pdu.encode(dst), + GfxPdu::SolidFill(pdu) => pdu.encode(dst), + GfxPdu::SurfaceToSurface(pdu) => pdu.encode(dst), + GfxPdu::SurfaceToCache(pdu) => pdu.encode(dst), + GfxPdu::CacheToSurface(pdu) => pdu.encode(dst), + GfxPdu::EvictCacheEntry(pdu) => pdu.encode(dst), + GfxPdu::CreateSurface(pdu) => pdu.encode(dst), + GfxPdu::DeleteSurface(pdu) => pdu.encode(dst), + GfxPdu::StartFrame(pdu) => pdu.encode(dst), + GfxPdu::EndFrame(pdu) => pdu.encode(dst), + GfxPdu::FrameAcknowledge(pdu) => pdu.encode(dst), + GfxPdu::ResetGraphics(pdu) => pdu.encode(dst), + GfxPdu::MapSurfaceToOutput(pdu) => pdu.encode(dst), + GfxPdu::CacheImportOffer(pdu) => pdu.encode(dst), + GfxPdu::CacheImportReply(pdu) => pdu.encode(dst), + GfxPdu::CapabilitiesAdvertise(pdu) => pdu.encode(dst), + GfxPdu::CapabilitiesConfirm(pdu) => pdu.encode(dst), + GfxPdu::MapSurfaceToWindow(pdu) => pdu.encode(dst), + GfxPdu::QoeFrameAcknowledge(pdu) => pdu.encode(dst), + GfxPdu::MapSurfaceToScaledOutput(pdu) => pdu.encode(dst), + GfxPdu::MapSurfaceToScaledWindow(pdu) => pdu.encode(dst), + }?; + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + // As per invariants: This will never overflow. + #[allow(clippy::arithmetic_side_effects)] + let size = Self::FIXED_PART_SIZE + + match self { + GfxPdu::WireToSurface1(pdu) => pdu.size(), + GfxPdu::WireToSurface2(pdu) => pdu.size(), + GfxPdu::DeleteEncodingContext(pdu) => pdu.size(), + GfxPdu::SolidFill(pdu) => pdu.size(), + GfxPdu::SurfaceToSurface(pdu) => pdu.size(), + GfxPdu::SurfaceToCache(pdu) => pdu.size(), + GfxPdu::CacheToSurface(pdu) => pdu.size(), + GfxPdu::EvictCacheEntry(pdu) => pdu.size(), + GfxPdu::CreateSurface(pdu) => pdu.size(), + GfxPdu::DeleteSurface(pdu) => pdu.size(), + GfxPdu::StartFrame(pdu) => pdu.size(), + GfxPdu::EndFrame(pdu) => pdu.size(), + GfxPdu::FrameAcknowledge(pdu) => pdu.size(), + GfxPdu::ResetGraphics(pdu) => pdu.size(), + GfxPdu::MapSurfaceToOutput(pdu) => pdu.size(), + GfxPdu::CacheImportOffer(pdu) => pdu.size(), + GfxPdu::CacheImportReply(pdu) => pdu.size(), + GfxPdu::CapabilitiesAdvertise(pdu) => pdu.size(), + GfxPdu::CapabilitiesConfirm(pdu) => pdu.size(), + GfxPdu::MapSurfaceToWindow(pdu) => pdu.size(), + GfxPdu::QoeFrameAcknowledge(pdu) => pdu.size(), + GfxPdu::MapSurfaceToScaledOutput(pdu) => pdu.size(), + GfxPdu::MapSurfaceToScaledWindow(pdu) => pdu.size(), + }; + + size + } +} + +impl DvcEncode for GfxPdu {} + +impl<'de> Decode<'de> for GfxPdu { + fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + // Read `RDPGFX_HEADER` fields. + let cmdid = src.read_u16(); + let flags = src.read_u16(); /* flags */ + if flags != 0 { + warn!(?flags, "invalid GFX flag"); + } + let pdu_length = src.read_u32(); + + let _payload_length = pdu_length + .checked_sub(Self::FIXED_PART_SIZE.try_into().unwrap()) + .ok_or_else(|| invalid_field_err!("Length", "GFX PDU length is too small"))?; + + match cmdid { + RDPGFX_CMDID_WIRETOSURFACE_1 => { + let pdu = WireToSurface1Pdu::decode(src)?; + Ok(GfxPdu::WireToSurface1(pdu)) + } + RDPGFX_CMDID_WIRETOSURFACE_2 => { + let pdu = WireToSurface2Pdu::decode(src)?; + Ok(GfxPdu::WireToSurface2(pdu)) + } + RDPGFX_CMDID_DELETEENCODINGCONTEXT => { + let pdu = DeleteEncodingContextPdu::decode(src)?; + Ok(GfxPdu::DeleteEncodingContext(pdu)) + } + RDPGFX_CMDID_SOLIDFILL => { + let pdu = SolidFillPdu::decode(src)?; + Ok(GfxPdu::SolidFill(pdu)) + } + RDPGFX_CMDID_SURFACETOSURFACE => { + let pdu = SurfaceToSurfacePdu::decode(src)?; + Ok(GfxPdu::SurfaceToSurface(pdu)) + } + RDPGFX_CMDID_SURFACETOCACHE => { + let pdu = SurfaceToCachePdu::decode(src)?; + Ok(GfxPdu::SurfaceToCache(pdu)) + } + RDPGFX_CMDID_CACHETOSURFACE => { + let pdu = CacheToSurfacePdu::decode(src)?; + Ok(GfxPdu::CacheToSurface(pdu)) + } + RDPGFX_CMDID_EVICTCACHEENTRY => { + let pdu = EvictCacheEntryPdu::decode(src)?; + Ok(GfxPdu::EvictCacheEntry(pdu)) + } + RDPGFX_CMDID_CREATESURFACE => { + let pdu = CreateSurfacePdu::decode(src)?; + Ok(GfxPdu::CreateSurface(pdu)) + } + RDPGFX_CMDID_DELETESURFACE => { + let pdu = DeleteSurfacePdu::decode(src)?; + Ok(GfxPdu::DeleteSurface(pdu)) + } + RDPGFX_CMDID_STARTFRAME => { + let pdu = StartFramePdu::decode(src)?; + Ok(GfxPdu::StartFrame(pdu)) + } + RDPGFX_CMDID_ENDFRAME => { + let pdu = EndFramePdu::decode(src)?; + Ok(GfxPdu::EndFrame(pdu)) + } + RDPGFX_CMDID_FRAMEACKNOWLEDGE => { + let pdu = FrameAcknowledgePdu::decode(src)?; + Ok(GfxPdu::FrameAcknowledge(pdu)) + } + RDPGFX_CMDID_RESETGRAPHICS => { + let pdu = ResetGraphicsPdu::decode(src)?; + Ok(GfxPdu::ResetGraphics(pdu)) + } + RDPGFX_CMDID_MAPSURFACETOOUTPUT => { + let pdu = MapSurfaceToOutputPdu::decode(src)?; + Ok(GfxPdu::MapSurfaceToOutput(pdu)) + } + RDPGFX_CMDID_CACHEIMPORTOFFER => { + let pdu = CacheImportOfferPdu::decode(src)?; + Ok(GfxPdu::CacheImportOffer(pdu)) + } + RDPGFX_CMDID_CACHEIMPORTREPLY => { + let pdu = CacheImportReplyPdu::decode(src)?; + Ok(GfxPdu::CacheImportReply(pdu)) + } + RDPGFX_CMDID_CAPSADVERTISE => { + let pdu = CapabilitiesAdvertisePdu::decode(src)?; + Ok(GfxPdu::CapabilitiesAdvertise(pdu)) + } + RDPGFX_CMDID_CAPSCONFIRM => { + let pdu = CapabilitiesConfirmPdu::decode(src)?; + Ok(GfxPdu::CapabilitiesConfirm(pdu)) + } + RDPGFX_CMDID_MAPSURFACETOWINDOW => { + let pdu = MapSurfaceToWindowPdu::decode(src)?; + Ok(GfxPdu::MapSurfaceToWindow(pdu)) + } + RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE => { + let pdu = QoeFrameAcknowledgePdu::decode(src)?; + Ok(GfxPdu::QoeFrameAcknowledge(pdu)) + } + RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT => { + let pdu = MapSurfaceToScaledOutputPdu::decode(src)?; + Ok(GfxPdu::MapSurfaceToScaledOutput(pdu)) + } + RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW => { + let pdu = MapSurfaceToScaledWindowPdu::decode(src)?; + Ok(GfxPdu::MapSurfaceToScaledWindow(pdu)) + } + _ => Err(invalid_field_err!("Type", "Unknown GFX PDU type")), + } + } +} + +/// 2.2.2.1 RDPGFX_WIRE_TO_SURFACE_PDU_1 +/// +/// [2.2.2.1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/fb919fce-cc97-4d2b-8cf5-a737a00ef1a6 +#[derive(Clone, PartialEq, Eq)] +pub struct WireToSurface1Pdu { + pub surface_id: u16, + pub codec_id: Codec1Type, + pub pixel_format: PixelFormat, + pub destination_rectangle: InclusiveRectangle, + pub bitmap_data: Vec, +} + +impl fmt::Debug for WireToSurface1Pdu { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WireToSurface1Pdu") + .field("surface_id", &self.surface_id) + .field("codec_id", &self.codec_id) + .field("pixel_format", &self.pixel_format) + .field("destination_rectangle", &self.destination_rectangle) + .field("bitmap_data_length", &self.bitmap_data.len()) + .finish() + } +} + +impl WireToSurface1Pdu { + const NAME: &'static str = "WireToSurface1Pdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* CodecId */ + 1 /* PixelFormat */ + InclusiveRectangle::FIXED_PART_SIZE /* Dest */ + 4 /* BitmapDataLen */; +} + +impl Encode for WireToSurface1Pdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(self.surface_id); + dst.write_u16(self.codec_id.into()); + dst.write_u8(self.pixel_format.into()); + self.destination_rectangle.encode(dst)?; + dst.write_u32(cast_length!("BitmapDataLen", self.bitmap_data.len())?); + dst.write_slice(&self.bitmap_data); + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.bitmap_data.len() + } +} + +impl<'a> Decode<'a> for WireToSurface1Pdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let codec_id = Codec1Type::try_from(src.read_u16())?; + let pixel_format = PixelFormat::try_from(src.read_u8())?; + let destination_rectangle = InclusiveRectangle::decode(src)?; + let bitmap_data_length = cast_length!("BitmapDataLen", src.read_u32())?; + + ensure_size!(in: src, size: bitmap_data_length); + let bitmap_data = src.read_slice(bitmap_data_length).to_vec(); + + Ok(Self { + surface_id, + codec_id, + pixel_format, + destination_rectangle, + bitmap_data, + }) + } +} + +/// 2.2.2.2 RDPGFX_WIRE_TO_SURFACE_PDU_2 +/// +/// [2.2.2.2] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/49ccafc7-e025-4293-9650-dcae1b7b9e84 +#[derive(Clone, PartialEq, Eq)] +pub struct WireToSurface2Pdu { + pub surface_id: u16, + pub codec_id: Codec2Type, + pub codec_context_id: u32, + pub pixel_format: PixelFormat, + pub bitmap_data: Vec, +} + +impl fmt::Debug for WireToSurface2Pdu { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WireToSurface2Pdu") + .field("surface_id", &self.surface_id) + .field("codec_id", &self.codec_id) + .field("codec_context_id", &self.codec_context_id) + .field("pixel_format", &self.pixel_format) + .field("bitmap_data_length", &self.bitmap_data.len()) + .finish() + } +} + +impl WireToSurface2Pdu { + const NAME: &'static str = "WireToSurface2Pdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* CodecId */ + 4 /* ContextId */ + 1 /* PixelFormat */ + 4 /* BitmapDataLen */; +} + +impl Encode for WireToSurface2Pdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(self.surface_id); + dst.write_u16(self.codec_id.into()); + dst.write_u32(self.codec_context_id); + dst.write_u8(self.pixel_format.into()); + dst.write_u32(cast_length!("BitmapDataLen", self.bitmap_data.len())?); + dst.write_slice(&self.bitmap_data); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.bitmap_data.len() + } +} + +impl<'a> Decode<'a> for WireToSurface2Pdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let codec_id = Codec2Type::try_from(src.read_u16())?; + let codec_context_id = src.read_u32(); + let pixel_format = PixelFormat::try_from(src.read_u8())?; + let bitmap_data_length = cast_length!("BitmapDataLen", src.read_u32())?; + + ensure_size!(in: src, size: bitmap_data_length); + let bitmap_data = src.read_slice(bitmap_data_length).to_vec(); + + Ok(Self { + surface_id, + codec_id, + codec_context_id, + pixel_format, + bitmap_data, + }) + } +} + +/// 2.2.2.3 RDPGFX_DELETE_ENCODING_CONTEXT_PDU +/// +/// [2.2.2.3] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/0dfc9708-847a-4bf0-829a-481e7b826d6d +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeleteEncodingContextPdu { + pub surface_id: u16, + pub codec_context_id: u32, +} + +impl DeleteEncodingContextPdu { + const NAME: &'static str = "DeleteEncodingContextPdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 4 /* CodecContextId */; +} + +impl Encode for DeleteEncodingContextPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.surface_id); + dst.write_u32(self.codec_context_id); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for DeleteEncodingContextPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let codec_context_id = src.read_u32(); + + Ok(Self { + surface_id, + codec_context_id, + }) + } +} + +/// 2.2.2.4 RDPGFX_SOLID_FILL_PDU +/// +/// [2.2.2.4] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/d696ab07-fd47-42f6-a601-c8b6fae26577 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SolidFillPdu { + pub surface_id: u16, + pub fill_pixel: Color, + pub rectangles: Vec, +} + +impl SolidFillPdu { + const NAME: &'static str = "SolidFillPdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + Color::FIXED_PART_SIZE /* Color */ + 2 /* RectCount */; +} + +impl Encode for SolidFillPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(self.surface_id); + self.fill_pixel.encode(dst)?; + dst.write_u16(cast_length!("nRect", self.rectangles.len())?); + + for rectangle in self.rectangles.iter() { + rectangle.encode(dst)?; + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.rectangles.iter().map(|r| r.size()).sum::() + } +} + +impl<'a> Decode<'a> for SolidFillPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let fill_pixel = Color::decode(src)?; + let rectangles_count = src.read_u16(); + + ensure_size!(in: src, size: usize::from(rectangles_count) * InclusiveRectangle::FIXED_PART_SIZE); + let rectangles = (0..rectangles_count) + .map(|_| InclusiveRectangle::decode(src)) + .collect::>()?; + + Ok(Self { + surface_id, + fill_pixel, + rectangles, + }) + } +} + +/// 2.2.2.5 RDPGFX_SURFACE_TO_SURFACE_PDU +/// +/// [2.2.2.5] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/0b19d058-fff0-43e5-8671-8c4186d60529 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SurfaceToSurfacePdu { + pub source_surface_id: u16, + pub destination_surface_id: u16, + pub source_rectangle: InclusiveRectangle, + pub destination_points: Vec, +} + +impl SurfaceToSurfacePdu { + const NAME: &'static str = "SurfaceToSurfacePdu"; + + const FIXED_PART_SIZE: usize = 2 /* SourceId */ + 2 /* DestId */ + InclusiveRectangle::FIXED_PART_SIZE /* SourceRect */ + 2 /* DestPointsCount */; +} + +impl Encode for SurfaceToSurfacePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(self.source_surface_id); + dst.write_u16(self.destination_surface_id); + self.source_rectangle.encode(dst)?; + + dst.write_u16(cast_length!("DestinationPoints", self.destination_points.len())?); + for rectangle in self.destination_points.iter() { + rectangle.encode(dst)?; + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.destination_points.iter().map(|r| r.size()).sum::() + } +} + +impl<'a> Decode<'a> for SurfaceToSurfacePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let source_surface_id = src.read_u16(); + let destination_surface_id = src.read_u16(); + let source_rectangle = InclusiveRectangle::decode(src)?; + let destination_points_count = src.read_u16(); + + let destination_points = (0..destination_points_count) + .map(|_| Point::decode(src)) + .collect::>()?; + + Ok(Self { + source_surface_id, + destination_surface_id, + source_rectangle, + destination_points, + }) + } +} + +/// 2.2.2.6 RDPGFX_SURFACE_TO_CACHE_PDU +/// +/// [2.2.2.6] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/01108b9f-a888-4e5c-b790-42d5c5985998 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SurfaceToCachePdu { + pub surface_id: u16, + pub cache_key: u64, + pub cache_slot: u16, + pub source_rectangle: InclusiveRectangle, +} + +impl SurfaceToCachePdu { + const NAME: &'static str = "SurfaceToCachePdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 8 /* CacheKey */ + 2 /* CacheSlot */ + InclusiveRectangle::FIXED_PART_SIZE /* SourceRect */; +} + +impl Encode for SurfaceToCachePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.surface_id); + dst.write_u64(self.cache_key); + dst.write_u16(self.cache_slot); + self.source_rectangle.encode(dst)?; + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for SurfaceToCachePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let cache_key = src.read_u64(); + let cache_slot = src.read_u16(); + let source_rectangle = InclusiveRectangle::decode(src)?; + + Ok(Self { + surface_id, + cache_key, + cache_slot, + source_rectangle, + }) + } +} + +/// 2.2.2.7 RDPGFX_CACHE_TO_SURFACE_PDU +/// +/// [2.2.2.7] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/78c00bcd-f5cb-4c33-8d6c-f4cd50facfab +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CacheToSurfacePdu { + pub cache_slot: u16, + pub surface_id: u16, + pub destination_points: Vec, +} + +impl CacheToSurfacePdu { + const NAME: &'static str = "CacheToSurfacePdu"; + + const FIXED_PART_SIZE: usize = 2 /* cache_slot */ + 2 /* surface_id */ + 2 /* npoints */; +} + +impl Encode for CacheToSurfacePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(self.cache_slot); + dst.write_u16(self.surface_id); + dst.write_u16(cast_length!("npoints", self.destination_points.len())?); + for point in self.destination_points.iter() { + point.encode(dst)?; + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.destination_points.iter().map(|p| p.size()).sum::() + } +} + +impl<'de> Decode<'de> for CacheToSurfacePdu { + fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let cache_slot = src.read_u16(); + let surface_id = src.read_u16(); + let destination_points_count = src.read_u16(); + + let destination_points = (0..destination_points_count) + .map(|_| Point::decode(src)) + .collect::>()?; + + Ok(Self { + cache_slot, + surface_id, + destination_points, + }) + } +} + +/// 2.2.2.8 RDPGFX_EVICT_CACHE_ENTRY_PDU +/// +/// [2.2.2.8] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/9dd32c5c-fabc-497b-81be-776fa581a4f6 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvictCacheEntryPdu { + pub cache_slot: u16, +} + +impl EvictCacheEntryPdu { + const NAME: &'static str = "EvictCacheEntryPdu"; + + const FIXED_PART_SIZE: usize = 2; +} + +impl Encode for EvictCacheEntryPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.cache_slot); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for EvictCacheEntryPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let cache_slot = src.read_u16(); + + Ok(Self { cache_slot }) + } +} + +/// 2.2.2.9 RDPGFX_CREATE_SURFACE_PDU +/// +/// [2.2.2.9] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/9dd32c5c-fabc-497b-81be-776fa581a4f6 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CreateSurfacePdu { + pub surface_id: u16, + pub width: u16, + pub height: u16, + pub pixel_format: PixelFormat, +} + +impl CreateSurfacePdu { + const NAME: &'static str = "CreateSurfacePdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* Width */ + 2 /* Height */ + 1 /* PixelFormat */; +} + +impl Encode for CreateSurfacePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.surface_id); + dst.write_u16(self.width); + dst.write_u16(self.height); + dst.write_u8(self.pixel_format.into()); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for CreateSurfacePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let width = src.read_u16(); + let height = src.read_u16(); + let pixel_format = PixelFormat::try_from(src.read_u8())?; + + Ok(Self { + surface_id, + width, + height, + pixel_format, + }) + } +} + +/// 2.2.2.10 RDPGFX_DELETE_SURFACE_PDU +/// +/// [2.2.2.10] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/8079ae0e-8775-4525-aaf5-ebeef913402c +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeleteSurfacePdu { + pub surface_id: u16, +} + +impl DeleteSurfacePdu { + const NAME: &'static str = "DeleteSurfacePdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */; +} + +impl Encode for DeleteSurfacePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.surface_id); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for DeleteSurfacePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + + Ok(Self { surface_id }) + } +} + +/// 2.2.2.11 RDPGFX_START_FRAME_PDU +/// +/// [2.2.2.11] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/9849fa1a-f896-4abe-9fd4-b7761f56b42c +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StartFramePdu { + pub timestamp: Timestamp, + pub frame_id: u32, +} + +impl StartFramePdu { + const NAME: &'static str = "StartFramePdu"; + + const FIXED_PART_SIZE: usize = Timestamp::FIXED_PART_SIZE + 4 /* FrameId */; +} + +impl Encode for StartFramePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + self.timestamp.encode(dst)?; + dst.write_u32(self.frame_id); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for StartFramePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let timestamp = Timestamp::decode(src)?; + let frame_id = src.read_u32(); + + Ok(Self { timestamp, frame_id }) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Timestamp { + pub milliseconds: u16, + pub seconds: u8, + pub minutes: u8, + pub hours: u16, +} + +impl Timestamp { + const NAME: &'static str = "GfxTimestamp"; + + const FIXED_PART_SIZE: usize = 4; +} + +impl Encode for Timestamp { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + let mut timestamp: u32 = 0; + + timestamp.set_bits(..10, u32::from(self.milliseconds)); + timestamp.set_bits(10..16, u32::from(self.seconds)); + timestamp.set_bits(16..22, u32::from(self.minutes)); + timestamp.set_bits(22.., u32::from(self.hours)); + + dst.write_u32(timestamp); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for Timestamp { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let timestamp = src.read_u32(); + + let milliseconds = timestamp.get_bits(..10).try_into().unwrap(); + let seconds = timestamp.get_bits(10..16).try_into().unwrap(); + let minutes = timestamp.get_bits(16..22).try_into().unwrap(); + let hours = timestamp.get_bits(22..).try_into().unwrap(); + + Ok(Self { + milliseconds, + seconds, + minutes, + hours, + }) + } +} + +/// 2.2.2.12 RDPGFX_END_FRAME_PDU +/// +/// [2.2.2.12] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/413b5449-efc7-429c-8764-fa8d005800d3 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EndFramePdu { + pub frame_id: u32, +} + +impl EndFramePdu { + const NAME: &'static str = "EndFramePdu"; + + const FIXED_PART_SIZE: usize = 4; +} + +impl Encode for EndFramePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u32(self.frame_id); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for EndFramePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let frame_id = src.read_u32(); + + Ok(Self { frame_id }) + } +} + +/// 2.2.2.13 RDPGFX_FRAME_ACKNOWLEDGE_PDU +/// +/// [2.2.2.13] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/0241e258-77ef-4a58-b426-5039ed6296ce +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FrameAcknowledgePdu { + pub queue_depth: QueueDepth, + pub frame_id: u32, + pub total_frames_decoded: u32, +} + +impl FrameAcknowledgePdu { + const NAME: &'static str = "FrameAcknowledgePdu"; + + const FIXED_PART_SIZE: usize = 4 /* QueueDepth */ + 4 /* FrameId */ + 4 /* TotalFramesDecoded */; +} + +impl Encode for FrameAcknowledgePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u32(self.queue_depth.to_u32()); + dst.write_u32(self.frame_id); + dst.write_u32(self.total_frames_decoded); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for FrameAcknowledgePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let queue_depth = QueueDepth::from_u32(src.read_u32()); + let frame_id = src.read_u32(); + let total_frames_decoded = src.read_u32(); + + Ok(Self { + queue_depth, + frame_id, + total_frames_decoded, + }) + } +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum QueueDepth { + Unavailable, + AvailableBytes(u32), + Suspend, +} + +impl QueueDepth { + pub fn from_u32(v: u32) -> Self { + match v { + 0x0000_0000 => Self::Unavailable, + 0x0000_0001..=0xFFFF_FFFE => Self::AvailableBytes(v), + 0xFFFF_FFFF => Self::Suspend, + } + } + + pub fn to_u32(self) -> u32 { + match self { + Self::Unavailable => 0x0000_0000, + Self::AvailableBytes(v) => v, + Self::Suspend => 0xFFFF_FFFF, + } + } +} + +/// 2.2.2.14 RDPGFX_RESET_GRAPHICS_PDU +/// +/// [2.2.2.14] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/60c8841c-3288-473b-82c3-340e24f51f98 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResetGraphicsPdu { + pub width: u32, + pub height: u32, + pub monitors: Vec, +} + +impl ResetGraphicsPdu { + const NAME: &'static str = "ResetGraphicsPdu"; + + const FIXED_PART_SIZE: usize = 4 /* Width */ + 4 /* Height */ + 4 /* nMonitors */; +} + +impl Encode for ResetGraphicsPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u32(self.width); + dst.write_u32(self.height); + dst.write_u32(cast_length!("nMonitors", self.monitors.len())?); + + for monitor in self.monitors.iter() { + monitor.encode(dst)?; + } + + write_padding!(dst, self.padding_size()); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.monitors.iter().map(|m| m.size()).sum::() + self.padding_size() + } +} + +impl<'a> Decode<'a> for ResetGraphicsPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let width = src.read_u32(); + if width > MAX_RESET_GRAPHICS_WIDTH_HEIGHT { + return Err(invalid_field_err!("width", "invalid reset graphics width")); + } + + let height = src.read_u32(); + if height > MAX_RESET_GRAPHICS_WIDTH_HEIGHT { + return Err(invalid_field_err!("height", "invalid reset graphics height")); + } + + let monitor_count = src.read_u32(); + if monitor_count > MONITOR_COUNT_MAX { + return Err(invalid_field_err!("height", "invalid reset graphics monitor count")); + } + + let monitors = (0..monitor_count) + .map(|_| Monitor::decode(src)) + .collect::, _>>()?; + + let pdu = Self { + width, + height, + monitors, + }; + + read_padding!(src, pdu.padding_size()); + + Ok(pdu) + } +} + +impl ResetGraphicsPdu { + fn padding_size(&self) -> usize { + RESET_GRAPHICS_PDU_SIZE - Self::FIXED_PART_SIZE - self.monitors.iter().map(|m| m.size()).sum::() + } +} + +/// 2.2.2.15 RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU +/// +/// [2.2.2.15] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/a1c6ff83-c385-4ad6-9437-f17697cc001c +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapSurfaceToOutputPdu { + pub surface_id: u16, + pub output_origin_x: u32, + pub output_origin_y: u32, +} + +impl MapSurfaceToOutputPdu { + const NAME: &'static str = "MapSurfaceToOutputPdu"; + + const FIXED_PART_SIZE: usize = 2 /* surfaceId */ + 2 /* reserved */ + 4 /* OutOriginX */ + 4 /* OutOriginY */; +} + +impl Encode for MapSurfaceToOutputPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.surface_id); + dst.write_u16(0); // reserved + dst.write_u32(self.output_origin_x); + dst.write_u32(self.output_origin_y); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for MapSurfaceToOutputPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let _reserved = src.read_u16(); + let output_origin_x = src.read_u32(); + let output_origin_y = src.read_u32(); + + Ok(Self { + surface_id, + output_origin_x, + output_origin_y, + }) + } +} + +/// 2.2.2.16 RDPGFX_CACHE_IMPORT_OFFER_PDU +/// +/// [2.2.2.16] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/890f0077-dedb-4b22-8b20-ea69b9cfcacd +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CacheImportOfferPdu { + pub cache_entries: Vec, +} + +impl CacheImportOfferPdu { + const NAME: &'static str = "CacheImportOfferPdu"; + + const FIXED_PART_SIZE: usize = 2 /* Count */; +} + +impl Encode for CacheImportOfferPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(cast_length!("Count", self.cache_entries.len())?); + + for e in self.cache_entries.iter() { + e.encode(dst)?; + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.cache_entries.iter().map(|e| e.size()).sum::() + } +} + +impl<'a> Decode<'a> for CacheImportOfferPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let entries_count = src.read_u16(); + + let cache_entries = (0..entries_count) + .map(|_| CacheEntryMetadata::decode(src)) + .collect::, _>>()?; + + Ok(Self { cache_entries }) + } +} + +/// 2.2.2.17 RDPGFX_CACHE_IMPORT_REPLY_PDU +/// +/// [2.2.2.17] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/0c4d88f8-50dc-465a-ab00-88a3fe0ec3c5 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CacheImportReplyPdu { + pub cache_slots: Vec, +} + +impl CacheImportReplyPdu { + const NAME: &'static str = "CacheImportReplyPdu"; + + const FIXED_PART_SIZE: usize = 2 /* Count */; +} + +impl Encode for CacheImportReplyPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(cast_length!("Count", self.cache_slots.len())?); + + for cache_slot in self.cache_slots.iter() { + dst.write_u16(*cache_slot); + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.cache_slots.iter().map(|_| 2).sum::() + } +} + +impl<'a> Decode<'a> for CacheImportReplyPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let entries_count = src.read_u16(); + ensure_size!(in: src, size: 2 * usize::from(entries_count)); + + let cache_slots = (0..entries_count).map(|_| src.read_u16()).collect(); + + Ok(Self { cache_slots }) + } +} + +/// 2.2.2.16.1 RDPGFX_CACHE_ENTRY_METADATA +/// +/// [2.2.2.16.1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/486dc290-96f9-4219-98c2-e371e23fa0d6 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CacheEntryMetadata { + pub cache_key: u64, + pub bitmap_len: u32, +} + +impl CacheEntryMetadata { + const NAME: &'static str = "CacheEntryMetadata"; + + const FIXED_PART_SIZE: usize = 8 /* cache_key */ + 4 /* bitmap_len */; +} + +impl Encode for CacheEntryMetadata { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u64(self.cache_key); + dst.write_u32(self.bitmap_len); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for CacheEntryMetadata { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let cache_key = src.read_u64(); + let bitmap_len = src.read_u32(); + + Ok(Self { cache_key, bitmap_len }) + } +} + +/// 2.2.2.18 RDPGFX_CAPS_ADVERTISE_PDU +/// +/// [2.2.2.18] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/9cc3cf56-148d-44bf-9dea-5f5e6970c00f +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CapabilitiesAdvertisePdu(pub Vec); + +impl CapabilitiesAdvertisePdu { + const NAME: &'static str = "CapabilitiesAdvertisePdu"; + + const FIXED_PART_SIZE: usize = 2 /* Count */; +} + +impl Encode for CapabilitiesAdvertisePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u16(cast_length!("Count", self.0.len())?); + + for capability_set in self.0.iter() { + capability_set.encode(dst)?; + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + self.0.iter().map(|c| c.size()).sum::() + } +} + +impl<'a> Decode<'a> for CapabilitiesAdvertisePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let capabilities_count = cast_length!("Count", src.read_u16())?; + + ensure_size!(in: src, size: capabilities_count * CapabilitySet::FIXED_PART_SIZE); + + let capabilities = (0..capabilities_count) + .map(|_| CapabilitySet::decode(src)) + .collect::>()?; + + Ok(Self(capabilities)) + } +} + +/// 2.2.2.19 RDPGFX_CAPS_CONFIRM_PDU +/// +/// [2.2.2.19] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/4d1ced69-49ea-47dd-98d6-4b220f30db36 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CapabilitiesConfirmPdu(pub CapabilitySet); + +impl CapabilitiesConfirmPdu { + const NAME: &'static str = "CapabilitiesConfirmPdu"; + + const FIXED_PART_SIZE: usize = 0; +} + +impl Encode for CapabilitiesConfirmPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + self.0.encode(dst)?; + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + self.0.size() + } +} + +impl<'a> Decode<'a> for CapabilitiesConfirmPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let cap = CapabilitySet::decode(src)?; + + Ok(Self(cap)) + } +} + +/// 2.2.1.6 RDPGFX_CAPSET +/// +/// [2.2.1.6] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/82e6dd00-914d-4dcc-bd17-985e1268ffb7 +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CapabilitySet { + V8 { flags: CapabilitiesV8Flags }, + V8_1 { flags: CapabilitiesV81Flags }, + V10 { flags: CapabilitiesV10Flags }, + V10_1, + V10_2 { flags: CapabilitiesV10Flags }, + V10_3 { flags: CapabilitiesV103Flags }, + V10_4 { flags: CapabilitiesV104Flags }, + V10_5 { flags: CapabilitiesV104Flags }, + V10_6 { flags: CapabilitiesV104Flags }, + V10_6Err { flags: CapabilitiesV104Flags }, + V10_7 { flags: CapabilitiesV107Flags }, + Unknown(Vec), +} + +impl CapabilitySet { + fn version(&self) -> CapabilityVersion { + match self { + CapabilitySet::V8 { .. } => CapabilityVersion::V8, + CapabilitySet::V8_1 { .. } => CapabilityVersion::V8_1, + CapabilitySet::V10 { .. } => CapabilityVersion::V10, + CapabilitySet::V10_1 { .. } => CapabilityVersion::V10_1, + CapabilitySet::V10_2 { .. } => CapabilityVersion::V10_2, + CapabilitySet::V10_3 { .. } => CapabilityVersion::V10_3, + CapabilitySet::V10_4 { .. } => CapabilityVersion::V10_4, + CapabilitySet::V10_5 { .. } => CapabilityVersion::V10_5, + CapabilitySet::V10_6 { .. } => CapabilityVersion::V10_6, + CapabilitySet::V10_6Err { .. } => CapabilityVersion::V10_6Err, + CapabilitySet::V10_7 { .. } => CapabilityVersion::V10_7, + CapabilitySet::Unknown { .. } => CapabilityVersion::Unknown, + } + } +} + +impl CapabilitySet { + const NAME: &'static str = "GfxCapabilitySet"; + + const FIXED_PART_SIZE: usize = 4 /* version */ + 4 /* capsDataLength */; +} + +impl Encode for CapabilitySet { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_size!(in: dst, size: self.size()); + + dst.write_u32(self.version().into()); + dst.write_u32(cast_length!("dataLength", self.size() - Self::FIXED_PART_SIZE)?); + + match self { + CapabilitySet::V8 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V8_1 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_1 => dst.write_u128(0), + CapabilitySet::V10_2 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_3 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_4 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_5 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_6 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_6Err { flags } => dst.write_u32(flags.bits()), + CapabilitySet::V10_7 { flags } => dst.write_u32(flags.bits()), + CapabilitySet::Unknown(data) => dst.write_slice(data), + } + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + + match self { + CapabilitySet::V8 { .. } + | CapabilitySet::V8_1 { .. } + | CapabilitySet::V10 { .. } + | CapabilitySet::V10_2 { .. } + | CapabilitySet::V10_3 { .. } + | CapabilitySet::V10_4 { .. } + | CapabilitySet::V10_5 { .. } + | CapabilitySet::V10_6 { .. } + | CapabilitySet::V10_6Err { .. } + | CapabilitySet::V10_7 { .. } => 4, + CapabilitySet::V10_1 { .. } => 16, + CapabilitySet::Unknown(data) => data.len(), + } + } +} + +impl<'de> Decode<'de> for CapabilitySet { + fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let version = CapabilityVersion::try_from(src.read_u32())?; + let data_length: usize = cast_length!("dataLength", src.read_u32())?; + + ensure_size!(in: src, size: data_length); + let data = src.read_slice(data_length); + let mut cur = ReadCursor::new(data); + + let size = match version { + CapabilityVersion::V8 + | CapabilityVersion::V8_1 + | CapabilityVersion::V10 + | CapabilityVersion::V10_2 + | CapabilityVersion::V10_3 + | CapabilityVersion::V10_4 + | CapabilityVersion::V10_5 + | CapabilityVersion::V10_6 + | CapabilityVersion::V10_6Err + | CapabilityVersion::V10_7 => 4, + CapabilityVersion::V10_1 => 16, + CapabilityVersion::Unknown => 0, + }; + + ensure_size!(in: cur, size: size); + match version { + CapabilityVersion::V8 => Ok(CapabilitySet::V8 { + flags: CapabilitiesV8Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V8_1 => Ok(CapabilitySet::V8_1 { + flags: CapabilitiesV81Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10 => Ok(CapabilitySet::V10 { + flags: CapabilitiesV10Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_1 => { + cur.read_u128(); + + Ok(CapabilitySet::V10_1) + } + CapabilityVersion::V10_2 => Ok(CapabilitySet::V10_2 { + flags: CapabilitiesV10Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_3 => Ok(CapabilitySet::V10_3 { + flags: CapabilitiesV103Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_4 => Ok(CapabilitySet::V10_4 { + flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_5 => Ok(CapabilitySet::V10_5 { + flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_6 => Ok(CapabilitySet::V10_6 { + flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_6Err => Ok(CapabilitySet::V10_6Err { + flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::V10_7 => Ok(CapabilitySet::V10_7 { + flags: CapabilitiesV107Flags::from_bits_truncate(cur.read_u32()), + }), + CapabilityVersion::Unknown => Ok(CapabilitySet::Unknown(data.to_vec())), + } + } +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum CapabilityVersion { + V8 = 0x8_0004, + V8_1 = 0x8_0105, + V10 = 0xa_0002, + V10_1 = 0xa_0100, + V10_2 = 0xa_0200, + V10_3 = 0xa_0301, + V10_4 = 0xa_0400, + V10_5 = 0xa_0502, + V10_6 = 0xa_0600, // [MS-RDPEGFX-errata] + V10_6Err = 0xa_0601, // defined similar to FreeRDP to maintain best compatibility + V10_7 = 0xa_0701, + Unknown = 0xa_0702, +} + +impl TryFrom for CapabilityVersion { + type Error = DecodeError; + + fn try_from(value: u32) -> Result { + let res = match value { + 0x8_0004 => CapabilityVersion::V8, + 0x8_0105 => CapabilityVersion::V8_1, + 0xa_0002 => CapabilityVersion::V10, + 0xa_0100 => CapabilityVersion::V10_1, + 0xa_0200 => CapabilityVersion::V10_2, + 0xa_0301 => CapabilityVersion::V10_3, + 0xa_0400 => CapabilityVersion::V10_4, + 0xa_0502 => CapabilityVersion::V10_5, + 0xa_0600 => CapabilityVersion::V10_6, + 0xa_0601 => CapabilityVersion::V10_6Err, + 0xa_0701 => CapabilityVersion::V10_7, + 0xa_0702 => CapabilityVersion::Unknown, + _ => return Err(invalid_field_err!("version", "invalid capability version")), + }; + + Ok(res) + } +} + +impl From for u32 { + fn from(value: CapabilityVersion) -> Self { + value as u32 + } +} + +bitflags! { + /// 2.2.3.1 RDPGFX_CAPSET_VERSION8 + /// + /// [2.2.3.1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/027dd8eb-a066-42e8-ad65-2e0314c4dce5 + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV8Flags: u32 { + const THIN_CLIENT = 0x1; + const SMALL_CACHE = 0x2; + } +} + +bitflags! { + /// 2.2.3.2 RDPGFX_CAPSET_VERSION81 + /// + /// [2.2.3.2] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/487e57cc-cd16-44c4-add8-60b84bf6d9e4 + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV81Flags: u32 { + const THIN_CLIENT = 0x01; + const SMALL_CACHE = 0x02; + const AVC420_ENABLED = 0x10; + } +} + +bitflags! { + /// 2.2.3.3 RDPGFX_CAPSET_VERSION10 + /// + /// [2.2.3.3] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/d1899912-2b84-4e0d-9e6d-da0fd25d14bc + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV10Flags: u32 { + const SMALL_CACHE = 0x02; + const AVC_DISABLED = 0x20; + } +} + +// 2.2.3.4 RDPGFX_CAPSET_VERSION101 +// +// [2.2.3.4] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/5985e67e-4080-49a7-85e3-eb3ba0653ff6 +// reserved + +// 2.2.3.5 RDPGFX_CAPSET_VERSION102 +// +// [2.2.3.5] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/a73e87d5-10c3-4d3f-b00c-fd5579570a0b +//same as v10 + +bitflags! { + /// 2.2.3.6 RDPGFX_CAPSET_VERSION103 + /// + /// [2.2.3.6] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/a73e87d5-10c3-4d3f-b00c-fd5579570a0b + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV103Flags: u32 { + const AVC_DISABLED = 0x20; + const AVC_THIN_CLIENT = 0x40; + } +} + +bitflags! { + /// 2.2.3.7 RDPGFX_CAPSET_VERSION104 + /// + /// [2.2.3.7] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/be5ea8da-44db-478d-b55c-d42d82f11d26 + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV104Flags: u32 { + const SMALL_CACHE = 0x02; + const AVC_DISABLED = 0x20; + const AVC_THIN_CLIENT = 0x40; + } +} + +// 2.2.3.8 RDPGFX_CAPSET_VERSION105 +// +// [2.2.3.8] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/8fc20f1e-e63e-4b13-a546-22fba213ad83 +// same as v104 + +// 2.2.3.9 RDPGFX_CAPSET_VERSION106 +// +// [2.2.3.9] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/8d489900-e903-4778-bb83-691c5ab719d5 +// same as v104 + +bitflags! { + /// 2.2.3.10 RDPGFX_CAPSET_VERSION107 + /// + /// [2.2.3.10] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/ba94595b-04de-4fbd-8ee4-89d8ff8f5cf1 + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CapabilitiesV107Flags: u32 { + const SMALL_CACHE = 0x02; + const AVC_DISABLED = 0x20; + const AVC_THIN_CLIENT = 0x40; + const SCALEDMAP_DISABLE = 0x80; + } +} + +/// 2.2.2.20 RDPGFX_MAP_SURFACE_TO_WINDOW_PDU +/// +/// [2.2.2.20] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/2ec1357c-ee65-4d9b-89f3-8fc49348c92a +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapSurfaceToWindowPdu { + pub surface_id: u16, + pub window_id: u64, + pub mapped_width: u32, + pub mapped_height: u32, +} + +impl MapSurfaceToWindowPdu { + const NAME: &'static str = "MapSurfaceToWindowPdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 8 /* WindowId */ + 4 /* MappedWidth */ + 4 /* MappedHeight */; +} + +impl Encode for MapSurfaceToWindowPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + dst.write_u16(self.surface_id); + dst.write_u64(self.window_id); + dst.write_u32(self.mapped_width); + dst.write_u32(self.mapped_height); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for MapSurfaceToWindowPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let window_id = src.read_u64(); + let mapped_width = src.read_u32(); + let mapped_height = src.read_u32(); + + Ok(Self { + surface_id, + window_id, + mapped_width, + mapped_height, + }) + } +} + +/// 2.2.2.21 RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU +/// +/// [2.2.2.21] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/17aaf205-23fe-467f-a629-447f428fdda0 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QoeFrameAcknowledgePdu { + pub frame_id: u32, + pub timestamp: u32, + pub time_diff_se: u16, + pub time_diff_dr: u16, +} + +impl QoeFrameAcknowledgePdu { + const NAME: &'static str = "QoeFrameAcknowledgePdu"; + + const FIXED_PART_SIZE: usize = 4 /* FrameId */ + 4 /* timestamp */ + 2 /* diffSE */ + 2 /* diffDR */; +} + +impl Encode for QoeFrameAcknowledgePdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u32(self.frame_id); + dst.write_u32(self.timestamp); + dst.write_u16(self.time_diff_se); + dst.write_u16(self.time_diff_dr); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for QoeFrameAcknowledgePdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let frame_id = src.read_u32(); + let timestamp = src.read_u32(); + let time_diff_se = src.read_u16(); + let time_diff_dr = src.read_u16(); + + Ok(Self { + frame_id, + timestamp, + time_diff_se, + time_diff_dr, + }) + } +} + +/// 2.2.2.22 RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU +/// +/// [2.2.2.22] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/6fbddd3f-0a87-4e83-9936-eb3a46fdfdea +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapSurfaceToScaledOutputPdu { + pub surface_id: u16, + pub output_origin_x: u32, + pub output_origin_y: u32, + pub target_width: u32, + pub target_height: u32, +} + +impl MapSurfaceToScaledOutputPdu { + const NAME: &'static str = "MapSurfaceToScaledOutputPdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* reserved */ + 4 /* oox */ + 4 /* ooy */ + 4 /* targetWidth */ + 4 /* targetHeight */; +} + +impl Encode for MapSurfaceToScaledOutputPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + dst.write_u16(self.surface_id); + dst.write_u16(0); // reserved + dst.write_u32(self.output_origin_x); + dst.write_u32(self.output_origin_y); + dst.write_u32(self.target_width); + dst.write_u32(self.target_height); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for MapSurfaceToScaledOutputPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let _reserved = src.read_u16(); + let output_origin_x = src.read_u32(); + let output_origin_y = src.read_u32(); + let target_width = src.read_u32(); + let target_height = src.read_u32(); + + Ok(Self { + surface_id, + output_origin_x, + output_origin_y, + target_width, + target_height, + }) + } +} + +/// 2.2.2.23 RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU +/// +/// [2.2.2.23] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MapSurfaceToScaledWindowPdu { + pub surface_id: u16, + pub window_id: u64, + pub mapped_width: u32, + pub mapped_height: u32, + pub target_width: u32, + pub target_height: u32, +} + +impl MapSurfaceToScaledWindowPdu { + const NAME: &'static str = "MapSurfaceToScaledWindowPdu"; + + const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* reserved */ + 8 /* surfaceId */ + 4 /* mappedWidth */ + 4 /* mappedHeight */ + 4 /* targetWidth */ + 4 /* targetHeight */; +} + +impl Encode for MapSurfaceToScaledWindowPdu { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + dst.write_u16(self.surface_id); + dst.write_u64(self.window_id); + dst.write_u32(self.mapped_width); + dst.write_u32(self.mapped_height); + dst.write_u32(self.target_width); + dst.write_u32(self.target_height); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'a> Decode<'a> for MapSurfaceToScaledWindowPdu { + fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let surface_id = src.read_u16(); + let window_id = src.read_u64(); + let mapped_width = src.read_u32(); + let mapped_height = src.read_u32(); + let target_width = src.read_u32(); + let target_height = src.read_u32(); + + Ok(Self { + surface_id, + window_id, + mapped_width, + mapped_height, + target_width, + target_height, + }) + } +} + +#[repr(u16)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Codec1Type { + Uncompressed = 0x0, + RemoteFx = 0x3, + ClearCodec = 0x8, + Planar = 0xa, + Avc420 = 0xb, + Alpha = 0xc, + Avc444 = 0xe, + Avc444v2 = 0xf, +} + +impl TryFrom for Codec1Type { + type Error = DecodeError; + + fn try_from(value: u16) -> Result { + match value { + 0x0 => Ok(Codec1Type::Uncompressed), + 0x3 => Ok(Codec1Type::RemoteFx), + 0x8 => Ok(Codec1Type::ClearCodec), + 0xa => Ok(Codec1Type::Planar), + 0xb => Ok(Codec1Type::Avc420), + 0xc => Ok(Codec1Type::Alpha), + 0xe => Ok(Codec1Type::Avc444), + 0xf => Ok(Codec1Type::Avc444v2), + _ => Err(invalid_field_err!("Codec1Type", "invalid codec type")), + } + } +} + +impl From for u16 { + fn from(value: Codec1Type) -> Self { + value as u16 + } +} + +#[repr(u16)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Codec2Type { + RemoteFxProgressive = 0x9, +} + +impl TryFrom for Codec2Type { + type Error = DecodeError; + + fn try_from(value: u16) -> Result { + match value { + 0x9 => Ok(Codec2Type::RemoteFxProgressive), + _ => Err(invalid_field_err!("Codec2Type", "invalid codec type")), + } + } +} + +impl From for u16 { + fn from(value: Codec2Type) -> Self { + value as u16 + } +} diff --git a/crates/ironrdp-egfx/src/pdu/common.rs b/crates/ironrdp-egfx/src/pdu/common.rs new file mode 100644 index 000000000..ce3b20f36 --- /dev/null +++ b/crates/ironrdp-egfx/src/pdu/common.rs @@ -0,0 +1,128 @@ +use ironrdp_pdu::{ + ensure_fixed_part_size, invalid_field_err, Decode, DecodeError, DecodeResult, Encode, EncodeResult, ReadCursor, + WriteCursor, +}; + +/// 2.2.1.1 RDPGFX_POINT16 +/// +/// [2.2.1.1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/dd4f5693-e2d1-470e-b3d1-e760a3134876 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Point { + pub x: u16, + pub y: u16, +} + +impl Point { + const NAME: &'static str = "GfxPoint"; + + const FIXED_PART_SIZE: usize = 2 /* X */ + 2 /* Y */; +} + +impl Encode for Point { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u16(self.x); + dst.write_u16(self.y); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'de> Decode<'de> for Point { + fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let x = src.read_u16(); + let y = src.read_u16(); + + Ok(Self { x, y }) + } +} + +/// 2.2.1.3 RDPGFX_COLOR32 +/// +/// [2.2.1.3] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/8ea9699d-d511-4e16-b7d3-74d6fc0e0652 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Color { + pub b: u8, + pub g: u8, + pub r: u8, + pub xa: u8, +} + +impl Color { + const NAME: &'static str = "GfxColor"; + + pub const FIXED_PART_SIZE: usize = 4 /* BGRA */; +} + +impl Encode for Color { + fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { + ensure_fixed_part_size!(in: dst); + + dst.write_u8(self.b); + dst.write_u8(self.g); + dst.write_u8(self.r); + dst.write_u8(self.xa); + + Ok(()) + } + + fn name(&self) -> &'static str { + Self::NAME + } + + fn size(&self) -> usize { + Self::FIXED_PART_SIZE + } +} + +impl<'de> Decode<'de> for Color { + fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { + ensure_fixed_part_size!(in: src); + + let b = src.read_u8(); + let g = src.read_u8(); + let r = src.read_u8(); + let xa = src.read_u8(); + + Ok(Self { b, g, r, xa }) + } +} + +/// 2.2.1.4 RDPGFX_PIXELFORMAT +/// +/// [2.2.1.4] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/80afb419-0cd5-49f8-8256-f77cc1787ec9 +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PixelFormat { + XRgb = 0x20, + ARgb = 0x21, +} + +impl TryFrom for PixelFormat { + type Error = DecodeError; + + fn try_from(value: u8) -> Result { + match value { + 0x20 => Ok(PixelFormat::XRgb), + 0x21 => Ok(PixelFormat::ARgb), + _ => Err(invalid_field_err!("PixelFormat", "invalid pixel format")), + } + } +} + +impl From for u8 { + fn from(value: PixelFormat) -> Self { + value as u8 + } +} diff --git a/crates/ironrdp-egfx/src/pdu/mod.rs b/crates/ironrdp-egfx/src/pdu/mod.rs new file mode 100644 index 000000000..3a285f452 --- /dev/null +++ b/crates/ironrdp-egfx/src/pdu/mod.rs @@ -0,0 +1,12 @@ +//! Display Pipeline Virtual Channel Extension PDUs [MS-RDPEGFX][1] implementation. +//! +//! [1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0 + +mod common; +pub use common::*; + +mod cmd; +pub use cmd::*; + +mod avc; +pub use avc::*; diff --git a/crates/ironrdp-egfx/src/server.rs b/crates/ironrdp-egfx/src/server.rs new file mode 100644 index 000000000..83fb744a4 --- /dev/null +++ b/crates/ironrdp-egfx/src/server.rs @@ -0,0 +1,66 @@ +use ironrdp_core::{decode, impl_as_any}; +use ironrdp_dvc::{DvcMessage, DvcProcessor, DvcServerProcessor}; +use ironrdp_pdu::{decode_err, PduResult}; +use tracing::{trace, warn}; + +use crate::{ + pdu::{CacheImportOfferPdu, CapabilitiesAdvertisePdu, FrameAcknowledgePdu, GfxPdu}, + CHANNEL_NAME, +}; + +pub trait GraphicsPipelineHandler: Send { + fn capabilities_advertise(&mut self, pdu: CapabilitiesAdvertisePdu); + + fn frame_acknowledge(&mut self, pdu: FrameAcknowledgePdu) { + trace!(?pdu); + } + + fn cache_import_offer(&mut self, pdu: CacheImportOfferPdu) { + trace!(?pdu); + } +} + +/// A server for the Display Control Virtual Channel. +pub struct GraphicsPipelineServer { + handler: Box, +} + +impl GraphicsPipelineServer { + /// Create a new GraphicsPipelineServer. + pub fn new(handler: Box) -> Self { + Self { handler } + } +} + +impl_as_any!(GraphicsPipelineServer); + +impl DvcProcessor for GraphicsPipelineServer { + fn channel_name(&self) -> &str { + CHANNEL_NAME + } + + fn start(&mut self, _channel_id: u32) -> PduResult> { + Ok(vec![]) + } + + fn process(&mut self, _channel_id: u32, payload: &[u8]) -> PduResult> { + let pdu = decode(payload).map_err(|e| decode_err!(e))?; + match pdu { + GfxPdu::CapabilitiesAdvertise(pdu) => { + self.handler.capabilities_advertise(pdu); + } + GfxPdu::FrameAcknowledge(pdu) => { + self.handler.frame_acknowledge(pdu); + } + GfxPdu::CacheImportOffer(pdu) => { + self.handler.cache_import_offer(pdu); + } + _ => { + warn!(?pdu, "Unhandled client GFX PDU"); + } + } + Ok(vec![]) + } +} + +impl DvcServerProcessor for GraphicsPipelineServer {} diff --git a/crates/ironrdp-fuzzing/Cargo.toml b/crates/ironrdp-fuzzing/Cargo.toml index 6ffb24165..a9b3ce2c6 100644 --- a/crates/ironrdp-fuzzing/Cargo.toml +++ b/crates/ironrdp-fuzzing/Cargo.toml @@ -19,6 +19,7 @@ ironrdp-rdpdr.workspace = true ironrdp-rdpsnd.workspace = true ironrdp-cliprdr-format.workspace = true ironrdp-displaycontrol.workspace = true +ironrdp-egfx.workspace = true ironrdp-svc.workspace = true [lints] diff --git a/crates/ironrdp-fuzzing/src/oracles/mod.rs b/crates/ironrdp-fuzzing/src/oracles/mod.rs index e6bc62d26..77370879e 100644 --- a/crates/ironrdp-fuzzing/src/oracles/mod.rs +++ b/crates/ironrdp-fuzzing/src/oracles/mod.rs @@ -88,6 +88,8 @@ pub fn pdu_decode(data: &[u8]) { let _ = decode::(data); + let _ = decode::(data); + let _ = decode::>(data); let _ = decode::(data); } diff --git a/crates/ironrdp-pdu/src/lib.rs b/crates/ironrdp-pdu/src/lib.rs index 3cc17e158..212845f37 100644 --- a/crates/ironrdp-pdu/src/lib.rs +++ b/crates/ironrdp-pdu/src/lib.rs @@ -36,7 +36,6 @@ pub(crate) mod crypto; pub(crate) mod per; pub use crate::basic_output::{bitmap, fast_path, pointer, surface_commands}; -pub use crate::rdp::vc::dvc; pub type PduResult = Result; diff --git a/crates/ironrdp-pdu/src/rdp/vc.rs b/crates/ironrdp-pdu/src/rdp/vc.rs index ae4652916..e81685706 100644 --- a/crates/ironrdp-pdu/src/rdp/vc.rs +++ b/crates/ironrdp-pdu/src/rdp/vc.rs @@ -1,5 +1,3 @@ -pub mod dvc; - #[cfg(test)] mod tests; diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc.rs deleted file mode 100644 index 4fcc11bbb..000000000 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod gfx; diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx.rs deleted file mode 100644 index 90afd4a7f..000000000 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx.rs +++ /dev/null @@ -1,287 +0,0 @@ -mod graphics_messages; - -pub use graphics_messages::{ - Avc420BitmapStream, Avc444BitmapStream, CacheImportReplyPdu, CacheToSurfacePdu, CapabilitiesAdvertisePdu, - CapabilitiesConfirmPdu, CapabilitiesV103Flags, CapabilitiesV104Flags, CapabilitiesV107Flags, CapabilitiesV10Flags, - CapabilitiesV81Flags, CapabilitiesV8Flags, CapabilitySet, Codec1Type, Codec2Type, Color, CreateSurfacePdu, - DeleteEncodingContextPdu, DeleteSurfacePdu, Encoding, EndFramePdu, EvictCacheEntryPdu, FrameAcknowledgePdu, - MapSurfaceToOutputPdu, MapSurfaceToScaledOutputPdu, MapSurfaceToScaledWindowPdu, PixelFormat, Point, QuantQuality, - QueueDepth, ResetGraphicsPdu, SolidFillPdu, StartFramePdu, SurfaceToCachePdu, SurfaceToSurfacePdu, Timestamp, - WireToSurface1Pdu, WireToSurface2Pdu, -}; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, -}; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive as _, ToPrimitive as _}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ServerPdu { - WireToSurface1(WireToSurface1Pdu), - WireToSurface2(WireToSurface2Pdu), - DeleteEncodingContext(DeleteEncodingContextPdu), - SolidFill(SolidFillPdu), - SurfaceToSurface(SurfaceToSurfacePdu), - SurfaceToCache(SurfaceToCachePdu), - CacheToSurface(CacheToSurfacePdu), - EvictCacheEntry(EvictCacheEntryPdu), - CreateSurface(CreateSurfacePdu), - DeleteSurface(DeleteSurfacePdu), - StartFrame(StartFramePdu), - EndFrame(EndFramePdu), - ResetGraphics(ResetGraphicsPdu), - MapSurfaceToOutput(MapSurfaceToOutputPdu), - CapabilitiesConfirm(CapabilitiesConfirmPdu), - CacheImportReply(CacheImportReplyPdu), - MapSurfaceToScaledOutput(MapSurfaceToScaledOutputPdu), - MapSurfaceToScaledWindow(MapSurfaceToScaledWindowPdu), -} - -const RDP_GFX_HEADER_SIZE: usize = 2 /* PduType */ + 2 /* flags */ + 4 /* bufferLen */; - -impl ServerPdu { - const NAME: &'static str = "GfxServerPdu"; - - const FIXED_PART_SIZE: usize = RDP_GFX_HEADER_SIZE; -} - -impl Encode for ServerPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - let buffer_length = self.size(); - - dst.write_u16(ServerPduType::from(self).to_u16().unwrap()); - dst.write_u16(0); // flags - dst.write_u32(cast_length!("bufferLen", buffer_length)?); - - match self { - ServerPdu::WireToSurface1(pdu) => pdu.encode(dst), - ServerPdu::WireToSurface2(pdu) => pdu.encode(dst), - ServerPdu::DeleteEncodingContext(pdu) => pdu.encode(dst), - ServerPdu::SolidFill(pdu) => pdu.encode(dst), - ServerPdu::SurfaceToSurface(pdu) => pdu.encode(dst), - ServerPdu::SurfaceToCache(pdu) => pdu.encode(dst), - ServerPdu::CacheToSurface(pdu) => pdu.encode(dst), - ServerPdu::CreateSurface(pdu) => pdu.encode(dst), - ServerPdu::DeleteSurface(pdu) => pdu.encode(dst), - ServerPdu::ResetGraphics(pdu) => pdu.encode(dst), - ServerPdu::MapSurfaceToOutput(pdu) => pdu.encode(dst), - ServerPdu::MapSurfaceToScaledOutput(pdu) => pdu.encode(dst), - ServerPdu::MapSurfaceToScaledWindow(pdu) => pdu.encode(dst), - ServerPdu::StartFrame(pdu) => pdu.encode(dst), - ServerPdu::EndFrame(pdu) => pdu.encode(dst), - ServerPdu::EvictCacheEntry(pdu) => pdu.encode(dst), - ServerPdu::CapabilitiesConfirm(pdu) => pdu.encode(dst), - ServerPdu::CacheImportReply(pdu) => pdu.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - + match self { - ServerPdu::WireToSurface1(pdu) => pdu.size(), - ServerPdu::WireToSurface2(pdu) => pdu.size(), - ServerPdu::DeleteEncodingContext(pdu) => pdu.size(), - ServerPdu::SolidFill(pdu) => pdu.size(), - ServerPdu::SurfaceToSurface(pdu) => pdu.size(), - ServerPdu::SurfaceToCache(pdu) => pdu.size(), - ServerPdu::CacheToSurface(pdu) => pdu.size(), - ServerPdu::CreateSurface(pdu) => pdu.size(), - ServerPdu::DeleteSurface(pdu) => pdu.size(), - ServerPdu::ResetGraphics(pdu) => pdu.size(), - ServerPdu::MapSurfaceToOutput(pdu) => pdu.size(), - ServerPdu::MapSurfaceToScaledOutput(pdu) => pdu.size(), - ServerPdu::MapSurfaceToScaledWindow(pdu) => pdu.size(), - ServerPdu::StartFrame(pdu) => pdu.size(), - ServerPdu::EndFrame(pdu) => pdu.size(), - ServerPdu::EvictCacheEntry(pdu) => pdu.size(), - ServerPdu::CapabilitiesConfirm(pdu) => pdu.size(), - ServerPdu::CacheImportReply(pdu) => pdu.size(), - } - } -} - -impl<'a> Decode<'a> for ServerPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let pdu_type = ServerPduType::from_u16(src.read_u16()) - .ok_or_else(|| invalid_field_err!("serverPduType", "invalid pdu type"))?; - let _flags = src.read_u16(); - let pdu_length = cast_length!("pduLen", src.read_u32())?; - - let (server_pdu, buffer_length) = { - let pdu = match pdu_type { - ServerPduType::DeleteEncodingContext => { - ServerPdu::DeleteEncodingContext(DeleteEncodingContextPdu::decode(src)?) - } - ServerPduType::WireToSurface1 => ServerPdu::WireToSurface1(WireToSurface1Pdu::decode(src)?), - ServerPduType::WireToSurface2 => ServerPdu::WireToSurface2(WireToSurface2Pdu::decode(src)?), - ServerPduType::SolidFill => ServerPdu::SolidFill(SolidFillPdu::decode(src)?), - ServerPduType::SurfaceToSurface => ServerPdu::SurfaceToSurface(SurfaceToSurfacePdu::decode(src)?), - ServerPduType::SurfaceToCache => ServerPdu::SurfaceToCache(SurfaceToCachePdu::decode(src)?), - ServerPduType::CacheToSurface => ServerPdu::CacheToSurface(CacheToSurfacePdu::decode(src)?), - ServerPduType::EvictCacheEntry => ServerPdu::EvictCacheEntry(EvictCacheEntryPdu::decode(src)?), - ServerPduType::CreateSurface => ServerPdu::CreateSurface(CreateSurfacePdu::decode(src)?), - ServerPduType::DeleteSurface => ServerPdu::DeleteSurface(DeleteSurfacePdu::decode(src)?), - ServerPduType::StartFrame => ServerPdu::StartFrame(StartFramePdu::decode(src)?), - ServerPduType::EndFrame => ServerPdu::EndFrame(EndFramePdu::decode(src)?), - ServerPduType::ResetGraphics => ServerPdu::ResetGraphics(ResetGraphicsPdu::decode(src)?), - ServerPduType::MapSurfaceToOutput => ServerPdu::MapSurfaceToOutput(MapSurfaceToOutputPdu::decode(src)?), - ServerPduType::CapabilitiesConfirm => { - ServerPdu::CapabilitiesConfirm(CapabilitiesConfirmPdu::decode(src)?) - } - ServerPduType::CacheImportReply => ServerPdu::CacheImportReply(CacheImportReplyPdu::decode(src)?), - ServerPduType::MapSurfaceToScaledOutput => { - ServerPdu::MapSurfaceToScaledOutput(MapSurfaceToScaledOutputPdu::decode(src)?) - } - ServerPduType::MapSurfaceToScaledWindow => { - ServerPdu::MapSurfaceToScaledWindow(MapSurfaceToScaledWindowPdu::decode(src)?) - } - _ => return Err(invalid_field_err!("pduType", "invalid pdu type")), - }; - let buffer_length = pdu.size(); - - (pdu, buffer_length) - }; - - if buffer_length != pdu_length { - Err(invalid_field_err!("len", "invalid pdu length")) - } else { - Ok(server_pdu) - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ClientPdu { - FrameAcknowledge(FrameAcknowledgePdu), - CapabilitiesAdvertise(CapabilitiesAdvertisePdu), -} - -impl ClientPdu { - const NAME: &'static str = "GfxClientPdu"; - - const FIXED_PART_SIZE: usize = RDP_GFX_HEADER_SIZE; -} - -impl Encode for ClientPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(ClientPduType::from(self).to_u16().unwrap()); - dst.write_u16(0); // flags - dst.write_u32(cast_length!("bufferLen", self.size())?); - - match self { - ClientPdu::FrameAcknowledge(pdu) => pdu.encode(dst), - ClientPdu::CapabilitiesAdvertise(pdu) => pdu.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - + match self { - ClientPdu::FrameAcknowledge(pdu) => pdu.size(), - ClientPdu::CapabilitiesAdvertise(pdu) => pdu.size(), - } - } -} - -impl<'a> Decode<'a> for ClientPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - let pdu_type = ClientPduType::from_u16(src.read_u16()) - .ok_or_else(|| invalid_field_err!("clientPduType", "invalid pdu type"))?; - let _flags = src.read_u16(); - let pdu_length = cast_length!("bufferLen", src.read_u32())?; - - let client_pdu = match pdu_type { - ClientPduType::FrameAcknowledge => ClientPdu::FrameAcknowledge(FrameAcknowledgePdu::decode(src)?), - ClientPduType::CapabilitiesAdvertise => { - ClientPdu::CapabilitiesAdvertise(CapabilitiesAdvertisePdu::decode(src)?) - } - _ => return Err(invalid_field_err!("pduType", "invalid pdu type")), - }; - - if client_pdu.size() != pdu_length { - Err(invalid_field_err!("len", "invalid pdu length")) - } else { - Ok(client_pdu) - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum ClientPduType { - FrameAcknowledge = 0x0d, - CacheImportOffer = 0x10, - CapabilitiesAdvertise = 0x12, - QoeFrameAcknowledge = 0x16, -} - -impl<'a> From<&'a ClientPdu> for ClientPduType { - fn from(c: &'a ClientPdu) -> Self { - match c { - ClientPdu::FrameAcknowledge(_) => Self::FrameAcknowledge, - ClientPdu::CapabilitiesAdvertise(_) => Self::CapabilitiesAdvertise, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum ServerPduType { - WireToSurface1 = 0x01, - WireToSurface2 = 0x02, - DeleteEncodingContext = 0x03, - SolidFill = 0x04, - SurfaceToSurface = 0x05, - SurfaceToCache = 0x06, - CacheToSurface = 0x07, - EvictCacheEntry = 0x08, - CreateSurface = 0x09, - DeleteSurface = 0x0a, - StartFrame = 0x0b, - EndFrame = 0x0c, - ResetGraphics = 0x0e, - MapSurfaceToOutput = 0x0f, - CacheImportReply = 0x11, - CapabilitiesConfirm = 0x13, - MapSurfaceToWindow = 0x15, - MapSurfaceToScaledOutput = 0x17, - MapSurfaceToScaledWindow = 0x18, -} - -impl<'a> From<&'a ServerPdu> for ServerPduType { - fn from(s: &'a ServerPdu) -> Self { - match s { - ServerPdu::WireToSurface1(_) => Self::WireToSurface1, - ServerPdu::WireToSurface2(_) => Self::WireToSurface2, - ServerPdu::DeleteEncodingContext(_) => Self::DeleteEncodingContext, - ServerPdu::SolidFill(_) => Self::SolidFill, - ServerPdu::SurfaceToSurface(_) => Self::SurfaceToSurface, - ServerPdu::SurfaceToCache(_) => Self::SurfaceToCache, - ServerPdu::CacheToSurface(_) => Self::CacheToSurface, - ServerPdu::EvictCacheEntry(_) => Self::EvictCacheEntry, - ServerPdu::CreateSurface(_) => Self::CreateSurface, - ServerPdu::DeleteSurface(_) => Self::DeleteSurface, - ServerPdu::StartFrame(_) => Self::StartFrame, - ServerPdu::EndFrame(_) => Self::EndFrame, - ServerPdu::ResetGraphics(_) => Self::ResetGraphics, - ServerPdu::MapSurfaceToOutput(_) => Self::MapSurfaceToOutput, - ServerPdu::MapSurfaceToScaledOutput(_) => Self::MapSurfaceToScaledOutput, - ServerPdu::MapSurfaceToScaledWindow(_) => Self::MapSurfaceToScaledWindow, - ServerPdu::CapabilitiesConfirm(_) => Self::CapabilitiesConfirm, - ServerPdu::CacheImportReply(_) => Self::CacheImportReply, - } - } -} diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages.rs deleted file mode 100644 index 450dc77f1..000000000 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages.rs +++ /dev/null @@ -1,344 +0,0 @@ -mod client; -mod server; - -mod avc_messages; -use bitflags::bitflags; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive as _, ToPrimitive as _}; - -#[rustfmt::skip] // do not re-order this -pub use avc_messages::{Avc420BitmapStream, Avc444BitmapStream, Encoding, QuantQuality}; -pub use client::{CacheImportReplyPdu, CapabilitiesAdvertisePdu, FrameAcknowledgePdu, QueueDepth}; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, -}; -pub use server::{ - CacheToSurfacePdu, CapabilitiesConfirmPdu, Codec1Type, Codec2Type, CreateSurfacePdu, DeleteEncodingContextPdu, - DeleteSurfacePdu, EndFramePdu, EvictCacheEntryPdu, MapSurfaceToOutputPdu, MapSurfaceToScaledOutputPdu, - MapSurfaceToScaledWindowPdu, PixelFormat, ResetGraphicsPdu, SolidFillPdu, StartFramePdu, SurfaceToCachePdu, - SurfaceToSurfacePdu, Timestamp, WireToSurface1Pdu, WireToSurface2Pdu, -}; - -use super::RDP_GFX_HEADER_SIZE; - -const CAPABILITY_SET_HEADER_SIZE: usize = 8; - -const V10_1_RESERVED: u128 = 0; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CapabilitySet { - V8 { flags: CapabilitiesV8Flags }, - V8_1 { flags: CapabilitiesV81Flags }, - V10 { flags: CapabilitiesV10Flags }, - V10_1, - V10_2 { flags: CapabilitiesV10Flags }, - V10_3 { flags: CapabilitiesV103Flags }, - V10_4 { flags: CapabilitiesV104Flags }, - V10_5 { flags: CapabilitiesV104Flags }, - V10_6 { flags: CapabilitiesV104Flags }, - V10_6Err { flags: CapabilitiesV104Flags }, - V10_7 { flags: CapabilitiesV107Flags }, - Unknown(Vec), -} - -impl CapabilitySet { - fn version(&self) -> CapabilityVersion { - match self { - CapabilitySet::V8 { .. } => CapabilityVersion::V8, - CapabilitySet::V8_1 { .. } => CapabilityVersion::V8_1, - CapabilitySet::V10 { .. } => CapabilityVersion::V10, - CapabilitySet::V10_1 { .. } => CapabilityVersion::V10_1, - CapabilitySet::V10_2 { .. } => CapabilityVersion::V10_2, - CapabilitySet::V10_3 { .. } => CapabilityVersion::V10_3, - CapabilitySet::V10_4 { .. } => CapabilityVersion::V10_4, - CapabilitySet::V10_5 { .. } => CapabilityVersion::V10_5, - CapabilitySet::V10_6 { .. } => CapabilityVersion::V10_6, - CapabilitySet::V10_6Err { .. } => CapabilityVersion::V10_6Err, - CapabilitySet::V10_7 { .. } => CapabilityVersion::V10_7, - CapabilitySet::Unknown { .. } => CapabilityVersion::Unknown, - } - } -} - -impl CapabilitySet { - const NAME: &'static str = "GfxCapabilitySet"; - - const FIXED_PART_SIZE: usize = CAPABILITY_SET_HEADER_SIZE; -} - -impl Encode for CapabilitySet { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u32(self.version().to_u32().unwrap()); - dst.write_u32(cast_length!("dataLength", self.size() - CAPABILITY_SET_HEADER_SIZE)?); - - match self { - CapabilitySet::V8 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V8_1 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_1 => dst.write_u128(V10_1_RESERVED), - CapabilitySet::V10_2 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_3 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_4 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_5 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_6 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_6Err { flags } => dst.write_u32(flags.bits()), - CapabilitySet::V10_7 { flags } => dst.write_u32(flags.bits()), - CapabilitySet::Unknown(data) => dst.write_slice(data), - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - CAPABILITY_SET_HEADER_SIZE - + match self { - CapabilitySet::V8 { .. } - | CapabilitySet::V8_1 { .. } - | CapabilitySet::V10 { .. } - | CapabilitySet::V10_2 { .. } - | CapabilitySet::V10_3 { .. } - | CapabilitySet::V10_4 { .. } - | CapabilitySet::V10_5 { .. } - | CapabilitySet::V10_6 { .. } - | CapabilitySet::V10_6Err { .. } - | CapabilitySet::V10_7 { .. } => 4, - CapabilitySet::V10_1 { .. } => 16, - CapabilitySet::Unknown(data) => data.len(), - } - } -} - -impl<'de> Decode<'de> for CapabilitySet { - fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let version = CapabilityVersion::from_u32(src.read_u32()) - .ok_or_else(|| invalid_field_err!("version", "unhandled version"))?; - let data_length: usize = cast_length!("dataLength", src.read_u32())?; - - ensure_size!(in: src, size: data_length); - let data = src.read_slice(data_length); - let mut cur = ReadCursor::new(data); - - let size = match version { - CapabilityVersion::V8 - | CapabilityVersion::V8_1 - | CapabilityVersion::V10 - | CapabilityVersion::V10_2 - | CapabilityVersion::V10_3 - | CapabilityVersion::V10_4 - | CapabilityVersion::V10_5 - | CapabilityVersion::V10_6 - | CapabilityVersion::V10_6Err - | CapabilityVersion::V10_7 => 4, - CapabilityVersion::V10_1 => 16, - CapabilityVersion::Unknown => 0, - }; - - ensure_size!(in: cur, size: size); - match version { - CapabilityVersion::V8 => Ok(CapabilitySet::V8 { - flags: CapabilitiesV8Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V8_1 => Ok(CapabilitySet::V8_1 { - flags: CapabilitiesV81Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10 => Ok(CapabilitySet::V10 { - flags: CapabilitiesV10Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_1 => { - cur.read_u128(); - - Ok(CapabilitySet::V10_1) - } - CapabilityVersion::V10_2 => Ok(CapabilitySet::V10_2 { - flags: CapabilitiesV10Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_3 => Ok(CapabilitySet::V10_3 { - flags: CapabilitiesV103Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_4 => Ok(CapabilitySet::V10_4 { - flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_5 => Ok(CapabilitySet::V10_5 { - flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_6 => Ok(CapabilitySet::V10_6 { - flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_6Err => Ok(CapabilitySet::V10_6Err { - flags: CapabilitiesV104Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::V10_7 => Ok(CapabilitySet::V10_7 { - flags: CapabilitiesV107Flags::from_bits_truncate(cur.read_u32()), - }), - CapabilityVersion::Unknown => Ok(CapabilitySet::Unknown(data.to_vec())), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Color { - pub b: u8, - pub g: u8, - pub r: u8, - pub xa: u8, -} - -impl Color { - const NAME: &'static str = "GfxColor"; - - const FIXED_PART_SIZE: usize = 4 /* BGRA */; -} - -impl Encode for Color { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u8(self.b); - dst.write_u8(self.g); - dst.write_u8(self.r); - dst.write_u8(self.xa); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'de> Decode<'de> for Color { - fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let b = src.read_u8(); - let g = src.read_u8(); - let r = src.read_u8(); - let xa = src.read_u8(); - - Ok(Self { b, g, r, xa }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Point { - pub x: u16, - pub y: u16, -} - -impl Point { - const NAME: &'static str = "GfxPoint"; - - const FIXED_PART_SIZE: usize = 2 /* X */ + 2 /* Y */; -} - -impl Encode for Point { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.x); - dst.write_u16(self.y); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'de> Decode<'de> for Point { - fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let x = src.read_u16(); - let y = src.read_u16(); - - Ok(Self { x, y }) - } -} - -#[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub(crate) enum CapabilityVersion { - V8 = 0x8_0004, - V8_1 = 0x8_0105, - V10 = 0xa_0002, - V10_1 = 0xa_0100, - V10_2 = 0xa_0200, - V10_3 = 0xa_0301, - V10_4 = 0xa_0400, - V10_5 = 0xa_0502, - V10_6 = 0xa_0600, // [MS-RDPEGFX-errata] - V10_6Err = 0xa_0601, // defined similar to FreeRDP to maintain best compatibility - V10_7 = 0xa_0701, - Unknown = 0xa_0702, -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV8Flags: u32 { - const THIN_CLIENT = 0x1; - const SMALL_CACHE = 0x2; - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV81Flags: u32 { - const THIN_CLIENT = 0x01; - const SMALL_CACHE = 0x02; - const AVC420_ENABLED = 0x10; - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV10Flags: u32 { - const SMALL_CACHE = 0x02; - const AVC_DISABLED = 0x20; - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV103Flags: u32 { - const AVC_DISABLED = 0x20; - const AVC_THIN_CLIENT = 0x40; - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV104Flags: u32 { - const SMALL_CACHE = 0x02; - const AVC_DISABLED = 0x20; - const AVC_THIN_CLIENT = 0x40; - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct CapabilitiesV107Flags: u32 { - const SMALL_CACHE = 0x02; - const AVC_DISABLED = 0x20; - const AVC_THIN_CLIENT = 0x40; - const SCALEDMAP_DISABLE = 0x80; - } -} diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/client.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/client.rs deleted file mode 100644 index 45d1b8022..000000000 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/client.rs +++ /dev/null @@ -1,173 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use super::CapabilitySet; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CapabilitiesAdvertisePdu(pub Vec); - -impl CapabilitiesAdvertisePdu { - const NAME: &'static str = "CapabilitiesAdvertisePdu"; - - const FIXED_PART_SIZE: usize = 2 /* Count */; -} - -impl Encode for CapabilitiesAdvertisePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(cast_length!("Count", self.0.len())?); - - for capability_set in self.0.iter() { - capability_set.encode(dst)?; - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.0.iter().map(|c| c.size()).sum::() - } -} - -impl<'a> Decode<'a> for CapabilitiesAdvertisePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let capabilities_count = cast_length!("Count", src.read_u16())?; - - ensure_size!(in: src, size: capabilities_count * CapabilitySet::FIXED_PART_SIZE); - - let capabilities = (0..capabilities_count) - .map(|_| CapabilitySet::decode(src)) - .collect::>()?; - - Ok(Self(capabilities)) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FrameAcknowledgePdu { - pub queue_depth: QueueDepth, - pub frame_id: u32, - pub total_frames_decoded: u32, -} - -impl FrameAcknowledgePdu { - const NAME: &'static str = "FrameAcknowledgePdu"; - - const FIXED_PART_SIZE: usize = 4 /* QueueDepth */ + 4 /* FrameId */ + 4 /* TotalFramesDecoded */; -} - -impl Encode for FrameAcknowledgePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u32(self.queue_depth.to_u32()); - dst.write_u32(self.frame_id); - dst.write_u32(self.total_frames_decoded); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for FrameAcknowledgePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let queue_depth = QueueDepth::from_u32(src.read_u32()); - let frame_id = src.read_u32(); - let total_frames_decoded = src.read_u32(); - - Ok(Self { - queue_depth, - frame_id, - total_frames_decoded, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CacheImportReplyPdu { - pub cache_slots: Vec, -} - -impl CacheImportReplyPdu { - const NAME: &'static str = "CacheImportReplyPdu"; - - const FIXED_PART_SIZE: usize = 2 /* Count */; -} - -impl Encode for CacheImportReplyPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(cast_length!("Count", self.cache_slots.len())?); - - for cache_slot in self.cache_slots.iter() { - dst.write_u16(*cache_slot); - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.cache_slots.iter().map(|_| 2).sum::() - } -} - -impl<'a> Decode<'a> for CacheImportReplyPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let entries_count = src.read_u16(); - - let cache_slots = (0..entries_count).map(|_| src.read_u16()).collect(); - - Ok(Self { cache_slots }) - } -} - -#[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum QueueDepth { - Unavailable, - AvailableBytes(u32), - Suspend, -} - -impl QueueDepth { - pub fn from_u32(v: u32) -> Self { - match v { - 0x0000_0000 => Self::Unavailable, - 0x0000_0001..=0xFFFF_FFFE => Self::AvailableBytes(v), - 0xFFFF_FFFF => Self::Suspend, - } - } - - pub fn to_u32(self) -> u32 { - match self { - Self::Unavailable => 0x0000_0000, - Self::AvailableBytes(v) => v, - Self::Suspend => 0xFFFF_FFFF, - } - } -} diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs deleted file mode 100644 index 81b2bea51..000000000 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs +++ /dev/null @@ -1,1016 +0,0 @@ -use std::fmt; - -use bit_field::BitField; -use ironrdp_core::{ - cast_length, decode_cursor, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, - EncodeResult, ReadCursor, WriteCursor, -}; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive, ToPrimitive}; - -use super::{CapabilitySet, Color, Point, RDP_GFX_HEADER_SIZE}; -use crate::gcc::Monitor; -use crate::geometry::InclusiveRectangle; - -pub(crate) const RESET_GRAPHICS_PDU_SIZE: usize = 340; - -const MAX_RESET_GRAPHICS_WIDTH_HEIGHT: u32 = 32_766; -const MONITOR_COUNT_MAX: u32 = 16; - -#[derive(Clone, PartialEq, Eq)] -pub struct WireToSurface1Pdu { - pub surface_id: u16, - pub codec_id: Codec1Type, - pub pixel_format: PixelFormat, - pub destination_rectangle: InclusiveRectangle, - pub bitmap_data: Vec, -} - -impl fmt::Debug for WireToSurface1Pdu { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WireToSurface1Pdu") - .field("surface_id", &self.surface_id) - .field("codec_id", &self.codec_id) - .field("pixel_format", &self.pixel_format) - .field("destination_rectangle", &self.destination_rectangle) - .field("bitmap_data_length", &self.bitmap_data.len()) - .finish() - } -} - -impl WireToSurface1Pdu { - const NAME: &'static str = "WireToSurface1Pdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* CodecId */ + 1 /* PixelFormat */ + InclusiveRectangle::FIXED_PART_SIZE /* Dest */ + 4 /* BitmapDataLen */; -} - -impl Encode for WireToSurface1Pdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(self.surface_id); - dst.write_u16(self.codec_id.to_u16().unwrap()); - dst.write_u8(self.pixel_format.to_u8().unwrap()); - self.destination_rectangle.encode(dst)?; - dst.write_u32(cast_length!("BitmapDataLen", self.bitmap_data.len())?); - dst.write_slice(&self.bitmap_data); - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.bitmap_data.len() - } -} - -impl<'a> Decode<'a> for WireToSurface1Pdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let codec_id = - Codec1Type::from_u16(src.read_u16()).ok_or_else(|| invalid_field_err!("CodecId", "invalid codec ID"))?; - let pixel_format = PixelFormat::from_u8(src.read_u8()) - .ok_or_else(|| invalid_field_err!("PixelFormat", "invalid pixel format"))?; - let destination_rectangle = InclusiveRectangle::decode(src)?; - let bitmap_data_length = cast_length!("BitmapDataLen", src.read_u32())?; - - ensure_size!(in: src, size: bitmap_data_length); - let bitmap_data = src.read_slice(bitmap_data_length).to_vec(); - - Ok(Self { - surface_id, - codec_id, - pixel_format, - destination_rectangle, - bitmap_data, - }) - } -} - -#[derive(Clone, PartialEq, Eq)] -pub struct WireToSurface2Pdu { - pub surface_id: u16, - pub codec_id: Codec2Type, - pub codec_context_id: u32, - pub pixel_format: PixelFormat, - pub bitmap_data: Vec, -} - -impl fmt::Debug for WireToSurface2Pdu { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WireToSurface2Pdu") - .field("surface_id", &self.surface_id) - .field("codec_id", &self.codec_id) - .field("codec_context_id", &self.codec_context_id) - .field("pixel_format", &self.pixel_format) - .field("bitmap_data_length", &self.bitmap_data.len()) - .finish() - } -} - -impl WireToSurface2Pdu { - const NAME: &'static str = "WireToSurface2Pdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* CodecId */ + 4 /* ContextId */ + 1 /* PixelFormat */ + 4 /* BitmapDataLen */; -} - -impl Encode for WireToSurface2Pdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(self.surface_id); - dst.write_u16(self.codec_id.to_u16().unwrap()); - dst.write_u32(self.codec_context_id); - dst.write_u8(self.pixel_format.to_u8().unwrap()); - dst.write_u32(cast_length!("BitmapDataLen", self.bitmap_data.len())?); - dst.write_slice(&self.bitmap_data); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.bitmap_data.len() - } -} - -impl<'a> Decode<'a> for WireToSurface2Pdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let codec_id = - Codec2Type::from_u16(src.read_u16()).ok_or_else(|| invalid_field_err!("CodecId", "invalid codec ID"))?; - let codec_context_id = src.read_u32(); - let pixel_format = PixelFormat::from_u8(src.read_u8()) - .ok_or_else(|| invalid_field_err!("PixelFormat", "invalid pixel format"))?; - let bitmap_data_length = cast_length!("BitmapDataLen", src.read_u32())?; - - ensure_size!(in: src, size: bitmap_data_length); - let bitmap_data = src.read_slice(bitmap_data_length).to_vec(); - - Ok(Self { - surface_id, - codec_id, - codec_context_id, - pixel_format, - bitmap_data, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeleteEncodingContextPdu { - pub surface_id: u16, - pub codec_context_id: u32, -} - -impl DeleteEncodingContextPdu { - const NAME: &'static str = "DeleteEncodingContextPdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 4 /* CodecContextId */; -} - -impl Encode for DeleteEncodingContextPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - dst.write_u32(self.codec_context_id); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for DeleteEncodingContextPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let codec_context_id = src.read_u32(); - - Ok(Self { - surface_id, - codec_context_id, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SolidFillPdu { - pub surface_id: u16, - pub fill_pixel: Color, - pub rectangles: Vec, -} - -impl SolidFillPdu { - const NAME: &'static str = "CacheToSurfacePdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + Color::FIXED_PART_SIZE /* Color */ + 2 /* RectCount */; -} - -impl Encode for SolidFillPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(self.surface_id); - self.fill_pixel.encode(dst)?; - dst.write_u16(self.rectangles.len() as u16); - - for rectangle in self.rectangles.iter() { - rectangle.encode(dst)?; - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.rectangles.iter().map(|r| r.size()).sum::() - } -} - -impl<'a> Decode<'a> for SolidFillPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let fill_pixel = Color::decode(src)?; - let rectangles_count = src.read_u16(); - - ensure_size!(in: src, size: usize::from(rectangles_count) * InclusiveRectangle::FIXED_PART_SIZE); - let rectangles = (0..rectangles_count) - .map(|_| InclusiveRectangle::decode(src)) - .collect::>()?; - - Ok(Self { - surface_id, - fill_pixel, - rectangles, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SurfaceToSurfacePdu { - pub source_surface_id: u16, - pub destination_surface_id: u16, - pub source_rectangle: InclusiveRectangle, - pub destination_points: Vec, -} - -impl SurfaceToSurfacePdu { - const NAME: &'static str = "SurfaceToSurfacePdu"; - - const FIXED_PART_SIZE: usize = 2 /* SourceId */ + 2 /* DestId */ + InclusiveRectangle::FIXED_PART_SIZE /* SourceRect */ + 2 /* DestPointsCount */; -} - -impl Encode for SurfaceToSurfacePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(self.source_surface_id); - dst.write_u16(self.destination_surface_id); - self.source_rectangle.encode(dst)?; - - dst.write_u16(cast_length!("DestinationPoints", self.destination_points.len())?); - for rectangle in self.destination_points.iter() { - rectangle.encode(dst)?; - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.destination_points.iter().map(|r| r.size()).sum::() - } -} - -impl<'a> Decode<'a> for SurfaceToSurfacePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let source_surface_id = src.read_u16(); - let destination_surface_id = src.read_u16(); - let source_rectangle = InclusiveRectangle::decode(src)?; - let destination_points_count = src.read_u16(); - - let destination_points = (0..destination_points_count) - .map(|_| Point::decode(src)) - .collect::>()?; - - Ok(Self { - source_surface_id, - destination_surface_id, - source_rectangle, - destination_points, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SurfaceToCachePdu { - pub surface_id: u16, - pub cache_key: u64, - pub cache_slot: u16, - pub source_rectangle: InclusiveRectangle, -} - -impl SurfaceToCachePdu { - const NAME: &'static str = "SurfaceToCachePdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 8 /* CacheKey */ + 2 /* CacheSlot */ + InclusiveRectangle::FIXED_PART_SIZE /* SourceRect */; -} - -impl Encode for SurfaceToCachePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - dst.write_u64(self.cache_key); - dst.write_u16(self.cache_slot); - self.source_rectangle.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for SurfaceToCachePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let cache_key = src.read_u64(); - let cache_slot = src.read_u16(); - let source_rectangle = InclusiveRectangle::decode(src)?; - - Ok(Self { - surface_id, - cache_key, - cache_slot, - source_rectangle, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CacheToSurfacePdu { - pub cache_slot: u16, - pub surface_id: u16, - pub destination_points: Vec, -} - -impl CacheToSurfacePdu { - const NAME: &'static str = "CacheToSurfacePdu"; - - const FIXED_PART_SIZE: usize = 2 /* cache_slot */ + 2 /* surface_id */ + 2 /* npoints */; -} - -impl Encode for CacheToSurfacePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u16(self.cache_slot); - dst.write_u16(self.surface_id); - dst.write_u16(cast_length!("npoints", self.destination_points.len())?); - for point in self.destination_points.iter() { - point.encode(dst)?; - } - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE + self.destination_points.iter().map(|p| p.size()).sum::() - } -} - -impl<'de> Decode<'de> for CacheToSurfacePdu { - fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let cache_slot = src.read_u16(); - let surface_id = src.read_u16(); - let destination_points_count = src.read_u16(); - - let destination_points = (0..destination_points_count) - .map(|_| decode_cursor(src)) - .collect::>()?; - - Ok(Self { - cache_slot, - surface_id, - destination_points, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CreateSurfacePdu { - pub surface_id: u16, - pub width: u16, - pub height: u16, - pub pixel_format: PixelFormat, -} - -impl CreateSurfacePdu { - const NAME: &'static str = "CreateSurfacePdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* Width */ + 2 /* Height */ + 1 /* PixelFormat */; -} - -impl Encode for CreateSurfacePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - dst.write_u16(self.width); - dst.write_u16(self.height); - dst.write_u8(self.pixel_format.to_u8().unwrap()); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for CreateSurfacePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let width = src.read_u16(); - let height = src.read_u16(); - let pixel_format = PixelFormat::from_u8(src.read_u8()) - .ok_or_else(|| invalid_field_err!("pixelFormat", "invalid pixel format"))?; - - Ok(Self { - surface_id, - width, - height, - pixel_format, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeleteSurfacePdu { - pub surface_id: u16, -} - -impl DeleteSurfacePdu { - const NAME: &'static str = "DeleteSurfacePdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */; -} - -impl Encode for DeleteSurfacePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for DeleteSurfacePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - - Ok(Self { surface_id }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ResetGraphicsPdu { - pub width: u32, - pub height: u32, - pub monitors: Vec, -} - -impl ResetGraphicsPdu { - const NAME: &'static str = "ResetGraphicsPdu"; - - const FIXED_PART_SIZE: usize = 4 /* Width */ + 4 /* Height */; -} - -impl Encode for ResetGraphicsPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_size!(in: dst, size: self.size()); - - dst.write_u32(self.width); - dst.write_u32(self.height); - dst.write_u32(cast_length!("nMonitors", self.monitors.len())?); - - for monitor in self.monitors.iter() { - monitor.encode(dst)?; - } - - write_padding!(dst, self.padding_size()); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - RESET_GRAPHICS_PDU_SIZE - RDP_GFX_HEADER_SIZE - } -} - -impl<'a> Decode<'a> for ResetGraphicsPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let width = src.read_u32(); - if width > MAX_RESET_GRAPHICS_WIDTH_HEIGHT { - return Err(invalid_field_err!("width", "invalid reset graphics width")); - } - - let height = src.read_u32(); - if height > MAX_RESET_GRAPHICS_WIDTH_HEIGHT { - return Err(invalid_field_err!("height", "invalid reset graphics height")); - } - - let monitor_count = src.read_u32(); - if monitor_count > MONITOR_COUNT_MAX { - return Err(invalid_field_err!("height", "invalid reset graphics monitor count")); - } - - let monitors = (0..monitor_count) - .map(|_| Monitor::decode(src)) - .collect::, _>>()?; - - let pdu = Self { - width, - height, - monitors, - }; - - read_padding!(src, pdu.padding_size()); - - Ok(pdu) - } -} - -impl ResetGraphicsPdu { - fn padding_size(&self) -> usize { - RESET_GRAPHICS_PDU_SIZE - RDP_GFX_HEADER_SIZE - 12 - self.monitors.iter().map(|m| m.size()).sum::() - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MapSurfaceToOutputPdu { - pub surface_id: u16, - pub output_origin_x: u32, - pub output_origin_y: u32, -} - -impl MapSurfaceToOutputPdu { - const NAME: &'static str = "MapSurfaceToOutputPdu"; - - const FIXED_PART_SIZE: usize = 2 /* surfaceId */ + 2 /* reserved */ + 4 /* OutOriginX */ + 4 /* OutOriginY */; -} - -impl Encode for MapSurfaceToOutputPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - dst.write_u16(0); // reserved - dst.write_u32(self.output_origin_x); - dst.write_u32(self.output_origin_y); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for MapSurfaceToOutputPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let _reserved = src.read_u16(); - let output_origin_x = src.read_u32(); - let output_origin_y = src.read_u32(); - - Ok(Self { - surface_id, - output_origin_x, - output_origin_y, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MapSurfaceToScaledOutputPdu { - pub surface_id: u16, - pub output_origin_x: u32, - pub output_origin_y: u32, - pub target_width: u32, - pub target_height: u32, -} - -impl MapSurfaceToScaledOutputPdu { - const NAME: &'static str = "MapSurfaceToScaledOutputPdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 2 /* reserved */ + 4 /* OutOriginX */ + 4 /* OutOriginY */ + 4 /* TargetWidth */ + 4 /* TargetHeight */; -} - -impl Encode for MapSurfaceToScaledOutputPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.surface_id); - dst.write_u16(0); // reserved - dst.write_u32(self.output_origin_x); - dst.write_u32(self.output_origin_y); - dst.write_u32(self.target_width); - dst.write_u32(self.target_height); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for MapSurfaceToScaledOutputPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let _reserved = src.read_u16(); - let output_origin_x = src.read_u32(); - let output_origin_y = src.read_u32(); - let target_width = src.read_u32(); - let target_height = src.read_u32(); - - Ok(Self { - surface_id, - output_origin_x, - output_origin_y, - target_width, - target_height, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MapSurfaceToScaledWindowPdu { - pub surface_id: u16, - pub window_id: u64, - pub mapped_width: u32, - pub mapped_height: u32, - pub target_width: u32, - pub target_height: u32, -} - -impl MapSurfaceToScaledWindowPdu { - const NAME: &'static str = "MapSurfaceToScaledWindowPdu"; - - const FIXED_PART_SIZE: usize = 2 /* SurfaceId */ + 8 /* WindowId */ + 4 /* MappedWidth */ + 4 /* MappedHeight */ + 4 /* TargetWidth */ + 4 /* TargetHeight */; -} - -impl Encode for MapSurfaceToScaledWindowPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - dst.write_u16(self.surface_id); - dst.write_u64(self.window_id); // reserved - dst.write_u32(self.mapped_width); - dst.write_u32(self.mapped_height); - dst.write_u32(self.target_width); - dst.write_u32(self.target_height); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for MapSurfaceToScaledWindowPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let surface_id = src.read_u16(); - let window_id = src.read_u64(); - let mapped_width = src.read_u32(); - let mapped_height = src.read_u32(); - let target_width = src.read_u32(); - let target_height = src.read_u32(); - - Ok(Self { - surface_id, - window_id, - mapped_width, - mapped_height, - target_width, - target_height, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EvictCacheEntryPdu { - pub cache_slot: u16, -} - -impl EvictCacheEntryPdu { - const NAME: &'static str = "EvictCacheEntryPdu"; - - const FIXED_PART_SIZE: usize = 2; -} - -impl Encode for EvictCacheEntryPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u16(self.cache_slot); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for EvictCacheEntryPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let cache_slot = src.read_u16(); - - Ok(Self { cache_slot }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct StartFramePdu { - pub timestamp: Timestamp, - pub frame_id: u32, -} - -impl StartFramePdu { - const NAME: &'static str = "StartFramePdu"; - - const FIXED_PART_SIZE: usize = Timestamp::FIXED_PART_SIZE + 4 /* FrameId */; -} - -impl Encode for StartFramePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - self.timestamp.encode(dst)?; - dst.write_u32(self.frame_id); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for StartFramePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let timestamp = Timestamp::decode(src)?; - let frame_id = src.read_u32(); - - Ok(Self { timestamp, frame_id }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct EndFramePdu { - pub frame_id: u32, -} - -impl EndFramePdu { - const NAME: &'static str = "EndFramePdu"; - - const FIXED_PART_SIZE: usize = 4; -} - -impl Encode for EndFramePdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u32(self.frame_id); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for EndFramePdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let frame_id = src.read_u32(); - - Ok(Self { frame_id }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CapabilitiesConfirmPdu(pub CapabilitySet); - -impl CapabilitiesConfirmPdu { - const NAME: &'static str = "CapabilitiesConfirmPdu"; -} - -impl Encode for CapabilitiesConfirmPdu { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - self.0.encode(dst) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - self.0.size() - } -} - -impl<'a> Decode<'a> for CapabilitiesConfirmPdu { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - let capability_set = CapabilitySet::decode(src)?; - - Ok(Self(capability_set)) - } -} - -#[repr(u16)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum Codec1Type { - Uncompressed = 0x0, - RemoteFx = 0x3, - ClearCodec = 0x8, - Planar = 0xa, - Avc420 = 0xb, - Alpha = 0xc, - Avc444 = 0xe, - Avc444v2 = 0xf, -} - -#[repr(u16)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum Codec2Type { - RemoteFxProgressive = 0x9, -} - -#[repr(u8)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] -pub enum PixelFormat { - XRgb = 0x20, - ARgb = 0x21, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Timestamp { - pub milliseconds: u16, - pub seconds: u8, - pub minutes: u8, - pub hours: u16, -} - -impl Timestamp { - const NAME: &'static str = "Timestamp"; - - const FIXED_PART_SIZE: usize = 4; -} - -impl Encode for Timestamp { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - let mut timestamp: u32 = 0; - - timestamp.set_bits(..10, u32::from(self.milliseconds)); - timestamp.set_bits(10..16, u32::from(self.seconds)); - timestamp.set_bits(16..22, u32::from(self.minutes)); - timestamp.set_bits(22.., u32::from(self.hours)); - - dst.write_u32(timestamp); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl<'a> Decode<'a> for Timestamp { - fn decode(src: &mut ReadCursor<'a>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let timestamp = src.read_u32(); - - let milliseconds = timestamp.get_bits(..10) as u16; - let seconds = timestamp.get_bits(10..16) as u8; - let minutes = timestamp.get_bits(16..22) as u8; - let hours = timestamp.get_bits(22..) as u16; - - Ok(Self { - milliseconds, - seconds, - minutes, - hours, - }) - } -} diff --git a/crates/ironrdp-testsuite-core/Cargo.toml b/crates/ironrdp-testsuite-core/Cargo.toml index c64172126..78be49fb0 100644 --- a/crates/ironrdp-testsuite-core/Cargo.toml +++ b/crates/ironrdp-testsuite-core/Cargo.toml @@ -32,6 +32,7 @@ ironrdp-cliprdr.workspace = true ironrdp-connector.workspace = true ironrdp-displaycontrol.workspace = true ironrdp-dvc.workspace = true +ironrdp-egfx.workspace = true ironrdp-fuzzing.workspace = true ironrdp-graphics.workspace = true ironrdp-input.workspace = true diff --git a/crates/ironrdp-testsuite-core/src/gfx.rs b/crates/ironrdp-testsuite-core/src/gfx.rs deleted file mode 100644 index d62d8f089..000000000 --- a/crates/ironrdp-testsuite-core/src/gfx.rs +++ /dev/null @@ -1,17 +0,0 @@ -use ironrdp_pdu::rdp::vc::dvc::gfx::*; - -use crate::graphics_messages::{ - FRAME_ACKNOWLEDGE, FRAME_ACKNOWLEDGE_BUFFER, WIRE_TO_SURFACE_1, WIRE_TO_SURFACE_1_BUFFER, -}; - -pub const WIRE_TO_SURFACE_1_HEADER_BUFFER: [u8; 8] = [0x01, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00]; -pub const FRAME_ACKNOWLEDGE_HEADER_BUFFER: [u8; 8] = [0x0d, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00]; - -lazy_static! { - pub static ref HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER: Vec = - [&WIRE_TO_SURFACE_1_HEADER_BUFFER[..], &WIRE_TO_SURFACE_1_BUFFER[..],].concat(); - pub static ref HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER: Vec = - [&FRAME_ACKNOWLEDGE_HEADER_BUFFER[..], &FRAME_ACKNOWLEDGE_BUFFER[..],].concat(); - pub static ref HEADER_WITH_WIRE_TO_SURFACE_1: ServerPdu = ServerPdu::WireToSurface1(WIRE_TO_SURFACE_1.clone()); - pub static ref HEADER_WITH_FRAME_ACKNOWLEDGE: ClientPdu = ClientPdu::FrameAcknowledge(FRAME_ACKNOWLEDGE.clone()); -} diff --git a/crates/ironrdp-testsuite-core/src/lib.rs b/crates/ironrdp-testsuite-core/src/lib.rs index 190f12f36..884fb3881 100644 --- a/crates/ironrdp-testsuite-core/src/lib.rs +++ b/crates/ironrdp-testsuite-core/src/lib.rs @@ -20,8 +20,6 @@ pub mod cluster_data; pub mod conference_create; pub mod core_data; pub mod gcc; -pub mod gfx; -pub mod graphics_messages; pub mod mcs; pub mod message_channel_data; pub mod monitor_data; diff --git a/crates/ironrdp-testsuite-core/src/graphics_messages.rs b/crates/ironrdp-testsuite-core/tests/egfx/mod.rs similarity index 65% rename from crates/ironrdp-testsuite-core/src/graphics_messages.rs rename to crates/ironrdp-testsuite-core/tests/egfx/mod.rs index a7292aa1d..d5abf6b31 100644 --- a/crates/ironrdp-testsuite-core/src/graphics_messages.rs +++ b/crates/ironrdp-testsuite-core/tests/egfx/mod.rs @@ -1,8 +1,23 @@ +use ironrdp_core::{decode, decode_cursor, encode_vec, Encode, ReadCursor}; +use ironrdp_egfx::pdu::*; use ironrdp_pdu::gcc::{Monitor, MonitorFlags}; use ironrdp_pdu::geometry::InclusiveRectangle; -use ironrdp_pdu::rdp::vc::dvc::gfx::*; -pub const WIRE_TO_SURFACE_1_BUFFER: [u8; 218] = [ +use lazy_static::lazy_static; + +const WIRE_TO_SURFACE_1_HEADER_BUFFER: [u8; 8] = [0x01, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00]; +const FRAME_ACKNOWLEDGE_HEADER_BUFFER: [u8; 8] = [0x0d, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00]; + +lazy_static! { + static ref HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER: Vec = + [&WIRE_TO_SURFACE_1_HEADER_BUFFER[..], &WIRE_TO_SURFACE_1_BUFFER[..],].concat(); + static ref HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER: Vec = + [&FRAME_ACKNOWLEDGE_HEADER_BUFFER[..], &FRAME_ACKNOWLEDGE_BUFFER[..],].concat(); + static ref HEADER_WITH_WIRE_TO_SURFACE_1: GfxPdu = GfxPdu::WireToSurface1(WIRE_TO_SURFACE_1.clone()); + static ref HEADER_WITH_FRAME_ACKNOWLEDGE: GfxPdu = GfxPdu::FrameAcknowledge(FRAME_ACKNOWLEDGE.clone()); +} + +const WIRE_TO_SURFACE_1_BUFFER: [u8; 218] = [ 0x00, 0x00, 0x08, 0x00, 0x20, 0xa5, 0x03, 0xde, 0x02, 0xab, 0x03, 0xe7, 0x02, 0xc9, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x3f, 0x27, 0x19, 0x82, 0x72, 0x69, 0x40, 0x28, 0x1a, 0x3f, 0x27, @@ -17,7 +32,7 @@ pub const WIRE_TO_SURFACE_1_BUFFER: [u8; 218] = [ 0x40, 0x28, 0x1a, 0x3f, 0x27, 0x19, 0xc0, 0xb8, 0xb3, ]; -pub const WIRE_TO_SURFACE_2_BUFFER: [u8; 629] = [ +const WIRE_TO_SURFACE_2_BUFFER: [u8; 629] = [ 0x00, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x68, 0x02, 0x00, 0x00, 0xc1, 0xcc, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc4, 0xcc, 0x56, 0x02, 0x00, 0x00, 0x40, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x37, 0x02, 0x00, 0x00, 0x63, 0x02, 0x51, 0x01, 0x45, 0x00, 0x29, 0x00, 0x66, 0x76, 0x88, 0x99, 0xa9, 0xc6, @@ -54,28 +69,28 @@ pub const WIRE_TO_SURFACE_2_BUFFER: [u8; 629] = [ 0x00, 0x00, ]; -pub const DELETE_ENCODING_CONTEXT_BUFFER: [u8; 6] = [0x00, 0x00, 0x01, 0x00, 0x00, 0x00]; +const DELETE_ENCODING_CONTEXT_BUFFER: [u8; 6] = [0x00, 0x00, 0x01, 0x00, 0x00, 0x00]; -pub const SOLID_FILL_BUFFER: [u8; 16] = [ +const SOLID_FILL_BUFFER: [u8; 16] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, ]; -pub const SURFACE_TO_SURFACE_BUFFER: [u8; 18] = [ +const SURFACE_TO_SURFACE_BUFFER: [u8; 18] = [ 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x3c, 0x00, 0xa4, 0x02, 0x94, 0x00, 0x01, 0x00, 0x80, 0x00, 0x3c, 0x00, ]; -pub const SURFACE_TO_CACHE_BUFFER: [u8; 20] = [ +const SURFACE_TO_CACHE_BUFFER: [u8; 20] = [ 0x00, 0x00, 0xb7, 0x7f, 0xa3, 0xa6, 0xda, 0x86, 0x3d, 0x11, 0x0e, 0x00, 0x80, 0x02, 0x00, 0x00, 0xc0, 0x02, 0x40, 0x00, ]; -pub const CACHE_TO_SURFACE_BUFFER: [u8; 10] = [0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x40, 0x01]; +const CACHE_TO_SURFACE_BUFFER: [u8; 10] = [0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x40, 0x01]; -pub const CREATE_SURFACE_BUFFER: [u8; 7] = [0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0x21]; +const CREATE_SURFACE_BUFFER: [u8; 7] = [0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0x21]; -pub const DELETE_SURFACE_BUFFER: [u8; 2] = [0x00, 0x00]; +const DELETE_SURFACE_BUFFER: [u8; 2] = [0x00, 0x00]; -pub const RESET_GRAPHICS_BUFFER: [u8; 332] = [ +const RESET_GRAPHICS_BUFFER: [u8; 332] = [ 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -96,19 +111,17 @@ pub const RESET_GRAPHICS_BUFFER: [u8; 332] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; -pub const MAP_SURFACE_TO_OUTPUT_BUFFER: [u8; 12] = - [0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2, 0x00, 0x00, 0x00]; +const MAP_SURFACE_TO_OUTPUT_BUFFER: [u8; 12] = [0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2, 0x00, 0x00, 0x00]; -pub const EVICT_CACHE_ENTRY_BUFFER: [u8; 2] = [0x00, 0x00]; +const EVICT_CACHE_ENTRY_BUFFER: [u8; 2] = [0x00, 0x00]; -pub const START_FRAME_BUFFER: [u8; 8] = [0xf7, 0xe8, 0x9b, 0x5, 0x05, 0x00, 0x00, 0x00]; +const START_FRAME_BUFFER: [u8; 8] = [0xf7, 0xe8, 0x9b, 0x5, 0x05, 0x00, 0x00, 0x00]; -pub const END_FRAME_BUFFER: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; +const END_FRAME_BUFFER: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; -pub const CAPABILITIES_CONFIRM_BUFFER: [u8; 12] = - [0x02, 0x05, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00]; +const CAPABILITIES_CONFIRM_BUFFER: [u8; 12] = [0x02, 0x05, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00]; -pub const CAPABILITIES_ADVERTISE_BUFFER: [u8; 122] = [ +const CAPABILITIES_ADVERTISE_BUFFER: [u8; 122] = [ 0x9, 0x0, 0x4, 0x0, 0x8, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x1, 0x8, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x1, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xa, 0x0, 0x4, 0x0, @@ -117,9 +130,9 @@ pub const CAPABILITIES_ADVERTISE_BUFFER: [u8; 122] = [ 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, ]; -pub const FRAME_ACKNOWLEDGE_BUFFER: [u8; 12] = [0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0]; +const FRAME_ACKNOWLEDGE_BUFFER: [u8; 12] = [0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0]; -pub const CACHE_IMPORT_REPLY_BUFFER: [u8; 1840] = [ +const CACHE_IMPORT_REPLY_BUFFER: [u8; 1840] = [ 0x97, 0x3, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0, 0x5, 0x0, 0x6, 0x0, 0x7, 0x0, 0x8, 0x0, 0x9, 0x0, 0xa, 0x0, 0xb, 0x0, 0xc, 0x0, 0xd, 0x0, 0xe, 0x0, 0xf, 0x0, 0x10, 0x0, 0x11, 0x0, 0x12, 0x0, 0x13, 0x0, 0x14, 0x0, 0x15, 0x0, 0x16, 0x0, 0x17, 0x0, 0x18, 0x0, 0x19, 0x0, 0x1a, 0x0, 0x1b, 0x0, 0x1c, 0x0, 0x1d, 0x0, 0x1e, 0x0, 0x1f, 0x0, 0x20, 0x0, 0x21, @@ -210,7 +223,7 @@ pub const CACHE_IMPORT_REPLY_BUFFER: [u8; 1840] = [ 0x3, 0x97, 0x3, 0x98, 0x3, ]; -pub const AVC_444_MESSAGE_INCORRECT_LEN: [u8; 88] = [ +const AVC_444_MESSAGE_INCORRECT_LEN: [u8; 88] = [ 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x20, 0x4, 0x10, 0x7, 0x30, 0x4, 0x16, 0x64, 0x0, 0x0, 0x0, 0x1, 0x61, 0x9a, 0x11, 0xda, 0x24, 0xea, 0x25, 0x0, 0x1f, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x61, 0x0, 0x3f, 0xc9, 0xa1, 0x1d, 0xa2, 0x4e, 0xa2, 0x50, 0x1, 0xfe, 0x60, 0x0, 0x0, 0x0, 0x1, 0x61, 0x0, 0x1f, 0xe2, 0x68, 0x47, 0x68, 0x93, 0xa8, @@ -218,7 +231,7 @@ pub const AVC_444_MESSAGE_INCORRECT_LEN: [u8; 88] = [ 0xe7, 0x97, 0xab, 0x80, 0x80, 0x80, ]; -pub const AVC_444_MESSAGE_CORRECT_LEN: [u8; 88] = [ +const AVC_444_MESSAGE_CORRECT_LEN: [u8; 88] = [ 0x54, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x20, 0x4, 0x10, 0x7, 0x30, 0x4, 0x16, 0x64, 0x0, 0x0, 0x0, 0x1, 0x61, 0x9a, 0x11, 0xda, 0x24, 0xea, 0x25, 0x0, 0x1f, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x61, 0x0, 0x3f, 0xc9, 0xa1, 0x1d, 0xa2, 0x4e, 0xa2, 0x50, 0x1, 0xfe, 0x60, 0x0, 0x0, 0x0, 0x1, 0x61, 0x0, 0x1f, 0xe2, 0x68, 0x47, 0x68, 0x93, @@ -227,7 +240,7 @@ pub const AVC_444_MESSAGE_CORRECT_LEN: [u8; 88] = [ ]; lazy_static! { - pub static ref WIRE_TO_SURFACE_1: WireToSurface1Pdu = WireToSurface1Pdu { + static ref WIRE_TO_SURFACE_1: WireToSurface1Pdu = WireToSurface1Pdu { surface_id: 0, codec_id: Codec1Type::ClearCodec, pixel_format: PixelFormat::XRgb, @@ -239,20 +252,20 @@ lazy_static! { }, bitmap_data: WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(), }; - pub static ref WIRE_TO_SURFACE_1_BITMAP_DATA: Vec = WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(); - pub static ref WIRE_TO_SURFACE_2: WireToSurface2Pdu = WireToSurface2Pdu { + static ref WIRE_TO_SURFACE_1_BITMAP_DATA: Vec = WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(); + static ref WIRE_TO_SURFACE_2: WireToSurface2Pdu = WireToSurface2Pdu { surface_id: 0, codec_id: Codec2Type::RemoteFxProgressive, codec_context_id: 4, pixel_format: PixelFormat::XRgb, bitmap_data: WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(), }; - pub static ref WIRE_TO_SURFACE_2_BITMAP_DATA: Vec = WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(); - pub static ref DELETE_ENCODING_CONTEXT: DeleteEncodingContextPdu = DeleteEncodingContextPdu { + static ref WIRE_TO_SURFACE_2_BITMAP_DATA: Vec = WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(); + static ref DELETE_ENCODING_CONTEXT: DeleteEncodingContextPdu = DeleteEncodingContextPdu { surface_id: 0, codec_context_id: 1, }; - pub static ref SOLID_FILL: SolidFillPdu = SolidFillPdu { + static ref SOLID_FILL: SolidFillPdu = SolidFillPdu { surface_id: 0, fill_pixel: Color { b: 0, @@ -267,7 +280,7 @@ lazy_static! { bottom: 64 }], }; - pub static ref SURFACE_TO_SURFACE: SurfaceToSurfacePdu = SurfaceToSurfacePdu { + static ref SURFACE_TO_SURFACE: SurfaceToSurfacePdu = SurfaceToSurfacePdu { source_surface_id: 0, destination_surface_id: 0, source_rectangle: InclusiveRectangle { @@ -278,7 +291,7 @@ lazy_static! { }, destination_points: vec![Point { x: 128, y: 60 }], }; - pub static ref SURFACE_TO_CACHE: SurfaceToCachePdu = SurfaceToCachePdu { + static ref SURFACE_TO_CACHE: SurfaceToCachePdu = SurfaceToCachePdu { surface_id: 0, cache_key: 0x113D_86DA_A6A3_7FB7, cache_slot: 14, @@ -289,19 +302,19 @@ lazy_static! { bottom: 64 }, }; - pub static ref CACHE_TO_SURFACE: CacheToSurfacePdu = CacheToSurfacePdu { + static ref CACHE_TO_SURFACE: CacheToSurfacePdu = CacheToSurfacePdu { cache_slot: 2, surface_id: 0, destination_points: vec![Point { x: 768, y: 320 }], }; - pub static ref CREATE_SURFACE: CreateSurfacePdu = CreateSurfacePdu { + static ref CREATE_SURFACE: CreateSurfacePdu = CreateSurfacePdu { surface_id: 0, width: 1024, height: 768, pixel_format: PixelFormat::ARgb, }; - pub static ref DELETE_SURFACE: DeleteSurfacePdu = DeleteSurfacePdu { surface_id: 0 }; - pub static ref RESET_GRAPHICS: ResetGraphicsPdu = ResetGraphicsPdu { + static ref DELETE_SURFACE: DeleteSurfacePdu = DeleteSurfacePdu { surface_id: 0 }; + static ref RESET_GRAPHICS: ResetGraphicsPdu = ResetGraphicsPdu { width: 1024, height: 768, monitors: vec![Monitor { @@ -312,13 +325,13 @@ lazy_static! { flags: MonitorFlags::PRIMARY, }], }; - pub static ref MAP_SURFACE_TO_OUTPUT: MapSurfaceToOutputPdu = MapSurfaceToOutputPdu { + static ref MAP_SURFACE_TO_OUTPUT: MapSurfaceToOutputPdu = MapSurfaceToOutputPdu { surface_id: 0, output_origin_x: 1, output_origin_y: 2, }; - pub static ref EVICT_CACHE_ENTRY: EvictCacheEntryPdu = EvictCacheEntryPdu { cache_slot: 0 }; - pub static ref START_FRAME: StartFramePdu = StartFramePdu { + static ref EVICT_CACHE_ENTRY: EvictCacheEntryPdu = EvictCacheEntryPdu { cache_slot: 0 }; + static ref START_FRAME: StartFramePdu = StartFramePdu { timestamp: Timestamp { milliseconds: 247, seconds: 58, @@ -327,11 +340,11 @@ lazy_static! { }, frame_id: 5 }; - pub static ref END_FRAME: EndFramePdu = EndFramePdu { frame_id: 1 }; - pub static ref CAPABILITIES_CONFIRM: CapabilitiesConfirmPdu = CapabilitiesConfirmPdu(CapabilitySet::V10_5 { + static ref END_FRAME: EndFramePdu = EndFramePdu { frame_id: 1 }; + static ref CAPABILITIES_CONFIRM: CapabilitiesConfirmPdu = CapabilitiesConfirmPdu(CapabilitySet::V10_5 { flags: CapabilitiesV104Flags::AVC_DISABLED, }); - pub static ref CAPABILITIES_ADVERTISE: CapabilitiesAdvertisePdu = CapabilitiesAdvertisePdu(vec![ + static ref CAPABILITIES_ADVERTISE: CapabilitiesAdvertisePdu = CapabilitiesAdvertisePdu(vec![ CapabilitySet::V8 { flags: CapabilitiesV8Flags::THIN_CLIENT }, @@ -358,12 +371,12 @@ lazy_static! { flags: CapabilitiesV104Flags::AVC_DISABLED } ]); - pub static ref FRAME_ACKNOWLEDGE: FrameAcknowledgePdu = FrameAcknowledgePdu { + static ref FRAME_ACKNOWLEDGE: FrameAcknowledgePdu = FrameAcknowledgePdu { queue_depth: QueueDepth::Unavailable, frame_id: 1, total_frames_decoded: 1 }; - pub static ref CACHE_IMPORT_REPLY: CacheImportReplyPdu = CacheImportReplyPdu { + static ref CACHE_IMPORT_REPLY: CacheImportReplyPdu = CacheImportReplyPdu { cache_slots: vec![ 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, @@ -426,7 +439,7 @@ lazy_static! { 0x394, 0x395, 0x396, 0x397, 0x398 ] }; - pub static ref AVC_444_BITMAP: Avc444BitmapStream<'static> = Avc444BitmapStream { + static ref AVC_444_BITMAP: Avc444BitmapStream<'static> = Avc444BitmapStream { encoding: Encoding::CHROMA, stream1: Avc420BitmapStream { rectangles: vec![InclusiveRectangle { @@ -445,3 +458,446 @@ lazy_static! { stream2: None }; } + +#[test] +fn from_buffer_correctly_parses_server_pdu() { + let buffer = HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.as_ref(); + + assert_eq!(*HEADER_WITH_WIRE_TO_SURFACE_1, decode(buffer).unwrap()); +} + +#[test] +fn to_buffer_correctly_serializes_server_pdu() { + let buffer = encode_vec(&*HEADER_WITH_WIRE_TO_SURFACE_1).unwrap(); + + assert_eq!(buffer, HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.as_slice()); +} + +#[test] +fn buffer_length_is_correct_for_server_pdu() { + assert_eq!( + HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.len(), + HEADER_WITH_WIRE_TO_SURFACE_1.size() + ); +} + +#[test] +fn from_buffer_correctly_parses_client_pdu() { + let buffer = HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.as_ref(); + + assert_eq!(*HEADER_WITH_FRAME_ACKNOWLEDGE, decode(buffer).unwrap()); +} + +#[test] +fn to_buffer_correctly_serializes_client_pdu() { + let buffer = encode_vec(&*HEADER_WITH_FRAME_ACKNOWLEDGE).unwrap(); + + assert_eq!(buffer, HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.as_slice()); +} + +#[test] +fn buffer_length_is_correct_for_client_pdu() { + assert_eq!( + HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.len(), + HEADER_WITH_FRAME_ACKNOWLEDGE.size() + ); +} + +#[test] +fn from_buffer_correctly_parses_wire_to_surface_1_pdu() { + let buffer = WIRE_TO_SURFACE_1_BUFFER.as_ref(); + + assert_eq!(*WIRE_TO_SURFACE_1, decode(buffer).unwrap()); +} + +#[test] +fn to_buffer_correctly_serializes_wire_to_surface_1_pdu() { + let buffer = encode_vec(&*WIRE_TO_SURFACE_1).unwrap(); + + assert_eq!(buffer, WIRE_TO_SURFACE_1_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_wire_to_surface_1_pdu() { + assert_eq!(WIRE_TO_SURFACE_1_BUFFER.len(), WIRE_TO_SURFACE_1.size()); +} + +#[test] +fn from_buffer_correctly_parses_wire_to_surface_2_pdu() { + let buffer = WIRE_TO_SURFACE_2_BUFFER.as_ref(); + + assert_eq!(*WIRE_TO_SURFACE_2, decode(buffer).unwrap()); +} + +#[test] +fn to_buffer_correctly_serializes_wire_to_surface_2_pdu() { + let buffer = encode_vec(&*WIRE_TO_SURFACE_2).unwrap(); + + assert_eq!(buffer, WIRE_TO_SURFACE_2_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_wire_to_surface_2_pdu() { + assert_eq!(WIRE_TO_SURFACE_2_BUFFER.len(), WIRE_TO_SURFACE_2.size()); +} + +#[test] +fn from_buffer_correctly_parses_delete_encoding_context_pdu() { + let buffer = DELETE_ENCODING_CONTEXT_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*DELETE_ENCODING_CONTEXT, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_delete_encoding_context_pdu() { + let buffer = encode_vec(&*DELETE_ENCODING_CONTEXT).unwrap(); + + assert_eq!(buffer, DELETE_ENCODING_CONTEXT_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_delete_encoding_context_pdu() { + assert_eq!(DELETE_ENCODING_CONTEXT_BUFFER.len(), DELETE_ENCODING_CONTEXT.size()); +} + +#[test] +fn from_buffer_correctly_parses_solid_fill_pdu() { + let buffer = SOLID_FILL_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*SOLID_FILL, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_solid_fill_pdu() { + let buffer = encode_vec(&*SOLID_FILL).unwrap(); + assert_eq!(buffer, SOLID_FILL_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_solid_fill_pdu() { + assert_eq!(SOLID_FILL_BUFFER.len(), SOLID_FILL.size()); +} + +#[test] +fn from_buffer_correctly_parses_surface_to_surface_pdu() { + let buffer = SURFACE_TO_SURFACE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*SURFACE_TO_SURFACE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_surface_to_surface_pdu() { + let buffer = encode_vec(&*SURFACE_TO_SURFACE).unwrap(); + + assert_eq!(buffer, SURFACE_TO_SURFACE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_surface_to_surface_pdu() { + assert_eq!(SURFACE_TO_SURFACE_BUFFER.len(), SURFACE_TO_SURFACE.size()); +} + +#[test] +fn from_buffer_correctly_parses_surface_to_cache_pdu() { + let buffer = SURFACE_TO_CACHE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*SURFACE_TO_CACHE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_surface_to_cache_pdu() { + let buffer = encode_vec(&*SURFACE_TO_CACHE).unwrap(); + + assert_eq!(buffer, SURFACE_TO_CACHE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_surface_to_cache_pdu() { + assert_eq!(SURFACE_TO_CACHE_BUFFER.len(), SURFACE_TO_CACHE.size()); +} + +#[test] +fn from_buffer_correctly_parses_cache_to_surface_pdu() { + let buffer = CACHE_TO_SURFACE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*CACHE_TO_SURFACE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_cache_to_surface_pdu() { + let buffer = encode_vec(&*CACHE_TO_SURFACE).unwrap(); + + assert_eq!(buffer, CACHE_TO_SURFACE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_cache_to_surface_pdu() { + assert_eq!(CACHE_TO_SURFACE_BUFFER.len(), CACHE_TO_SURFACE.size()); +} + +#[test] +fn from_buffer_correctly_parses_create_surface_pdu() { + let buffer = CREATE_SURFACE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*CREATE_SURFACE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_create_surface_pdu() { + let buffer = encode_vec(&*CREATE_SURFACE).unwrap(); + + assert_eq!(buffer, CREATE_SURFACE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_create_surface_pdu() { + assert_eq!(CREATE_SURFACE_BUFFER.len(), CREATE_SURFACE.size()); +} + +#[test] +fn from_buffer_correctly_parses_delete_surface_pdu() { + let buffer = DELETE_SURFACE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*DELETE_SURFACE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_delete_surface_pdu() { + let buffer = encode_vec(&*DELETE_SURFACE).unwrap(); + + assert_eq!(buffer, DELETE_SURFACE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_delete_surface_pdu() { + assert_eq!(DELETE_SURFACE_BUFFER.len(), DELETE_SURFACE.size()); +} + +#[test] +fn from_buffer_correctly_parses_reset_graphics() { + let buffer = RESET_GRAPHICS_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*RESET_GRAPHICS, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_reset_graphics() { + let buffer = encode_vec(&*RESET_GRAPHICS).unwrap(); + + assert_eq!(buffer, RESET_GRAPHICS_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_reset_graphics() { + assert_eq!(RESET_GRAPHICS_BUFFER.len(), RESET_GRAPHICS.size()); +} + +#[test] +fn from_buffer_correctly_parses_map_surface_to_output_pdu() { + let buffer = MAP_SURFACE_TO_OUTPUT_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*MAP_SURFACE_TO_OUTPUT, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_map_surface_to_output_pdu() { + let buffer = encode_vec(&*MAP_SURFACE_TO_OUTPUT).unwrap(); + + assert_eq!(buffer, MAP_SURFACE_TO_OUTPUT_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_map_surface_to_output_pdu() { + assert_eq!(MAP_SURFACE_TO_OUTPUT_BUFFER.len(), MAP_SURFACE_TO_OUTPUT.size()); +} + +#[test] +fn from_buffer_correctly_parses_evict_cache_entry_pdu() { + let buffer = EVICT_CACHE_ENTRY_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*EVICT_CACHE_ENTRY, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_evict_cache_entry_pdu() { + let buffer = encode_vec(&*EVICT_CACHE_ENTRY).unwrap(); + + assert_eq!(buffer, EVICT_CACHE_ENTRY_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_evict_cache_entry_pdu() { + assert_eq!(EVICT_CACHE_ENTRY_BUFFER.len(), EVICT_CACHE_ENTRY.size()); +} + +#[test] +fn from_buffer_correctly_parses_start_frame_pdu() { + let buffer = START_FRAME_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*START_FRAME, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_start_frame_pdu() { + let buffer = encode_vec(&*START_FRAME).unwrap(); + + assert_eq!(buffer, START_FRAME_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_start_frame_pdu() { + assert_eq!(START_FRAME_BUFFER.len(), START_FRAME.size()); +} + +#[test] +fn from_buffer_correctly_parses_end_frame_pdu() { + let buffer = END_FRAME_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*END_FRAME, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_end_frame_pdu() { + let buffer = encode_vec(&*END_FRAME).unwrap(); + + assert_eq!(buffer, END_FRAME_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_end_frame_pdu() { + assert_eq!(END_FRAME_BUFFER.len(), END_FRAME.size()); +} + +#[test] +fn from_buffer_correctly_parses_capabilities_confirm_pdu() { + let buffer = CAPABILITIES_CONFIRM_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*CAPABILITIES_CONFIRM, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_capabilities_confirm_pdu() { + let buffer = encode_vec(&*CAPABILITIES_CONFIRM).unwrap(); + + assert_eq!(buffer, CAPABILITIES_CONFIRM_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_capabilities_confirm_pdu() { + assert_eq!(CAPABILITIES_CONFIRM_BUFFER.len(), CAPABILITIES_CONFIRM.size()); +} + +#[test] +fn from_buffer_correctly_parses_capabilities_advertise_pdu() { + let buffer = CAPABILITIES_ADVERTISE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*CAPABILITIES_ADVERTISE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_capabilities_advertise_pdu() { + let buffer = encode_vec(&*CAPABILITIES_ADVERTISE).unwrap(); + + assert_eq!(buffer, CAPABILITIES_ADVERTISE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_capabilities_advertise_pdu() { + assert_eq!(CAPABILITIES_ADVERTISE_BUFFER.len(), CAPABILITIES_ADVERTISE.size()); +} + +#[test] +fn from_buffer_correctly_parses_frame_acknowledge_pdu() { + let buffer = FRAME_ACKNOWLEDGE_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*FRAME_ACKNOWLEDGE, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_frame_acknowledge_pdu() { + let buffer = encode_vec(&*FRAME_ACKNOWLEDGE).unwrap(); + + assert_eq!(buffer, FRAME_ACKNOWLEDGE_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_frame_acknowledge_pdu() { + assert_eq!(FRAME_ACKNOWLEDGE_BUFFER.len(), FRAME_ACKNOWLEDGE.size()); +} + +#[test] +fn from_buffer_correctly_parses_cache_import_reply() { + let buffer = CACHE_IMPORT_REPLY_BUFFER.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*CACHE_IMPORT_REPLY, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_correctly_serializes_cache_import_reply() { + let buffer = encode_vec(&*CACHE_IMPORT_REPLY).unwrap(); + + assert_eq!(buffer, CACHE_IMPORT_REPLY_BUFFER.as_ref()); +} + +#[test] +fn buffer_length_is_correct_for_cache_import_reply() { + assert_eq!(CACHE_IMPORT_REPLY_BUFFER.len(), CACHE_IMPORT_REPLY.size()); +} + +#[test] +fn from_buffer_consume_correctly_parses_incorrect_len_avc_444_message() { + let buffer = AVC_444_MESSAGE_INCORRECT_LEN.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*AVC_444_BITMAP, decode_cursor(&mut cursor).unwrap()); + assert!(!cursor.is_empty()); +} + +#[test] +fn from_buffer_consume_correctly_parses_avc_444_message() { + let buffer = AVC_444_MESSAGE_CORRECT_LEN.as_ref(); + + let mut cursor = ReadCursor::new(buffer); + assert_eq!(*AVC_444_BITMAP, decode_cursor(&mut cursor).unwrap()); + assert!(cursor.is_empty()); +} + +#[test] +fn to_buffer_consume_correctly_serializes_avc_444_message() { + let buffer = encode_vec(&*AVC_444_BITMAP).unwrap(); + let expected = AVC_444_MESSAGE_CORRECT_LEN.as_ref(); + + assert_eq!(expected, buffer.as_slice()); +} diff --git a/crates/ironrdp-testsuite-core/tests/main.rs b/crates/ironrdp-testsuite-core/tests/main.rs index 65a30584b..178b98ace 100644 --- a/crates/ironrdp-testsuite-core/tests/main.rs +++ b/crates/ironrdp-testsuite-core/tests/main.rs @@ -14,6 +14,7 @@ mod clipboard; mod displaycontrol; mod dvc; +mod egfx; mod fuzz_regression; mod graphics; mod input; diff --git a/crates/ironrdp-testsuite-core/tests/pdu/gfx.rs b/crates/ironrdp-testsuite-core/tests/pdu/gfx.rs deleted file mode 100644 index 21f0cf992..000000000 --- a/crates/ironrdp-testsuite-core/tests/pdu/gfx.rs +++ /dev/null @@ -1,446 +0,0 @@ -use ironrdp_core::{decode, decode_cursor, encode_vec, Encode, ReadCursor}; -use ironrdp_testsuite_core::gfx::*; -use ironrdp_testsuite_core::graphics_messages::*; - -#[test] -fn from_buffer_correctly_parses_server_pdu() { - let buffer = HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.as_ref(); - - assert_eq!(*HEADER_WITH_WIRE_TO_SURFACE_1, decode(buffer).unwrap()); -} - -#[test] -fn to_buffer_correctly_serializes_server_pdu() { - let buffer = encode_vec(&*HEADER_WITH_WIRE_TO_SURFACE_1).unwrap(); - - assert_eq!(buffer, HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.as_slice()); -} - -#[test] -fn buffer_length_is_correct_for_server_pdu() { - assert_eq!( - HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER.len(), - HEADER_WITH_WIRE_TO_SURFACE_1.size() - ); -} - -#[test] -fn from_buffer_correctly_parses_client_pdu() { - let buffer = HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.as_ref(); - - assert_eq!(*HEADER_WITH_FRAME_ACKNOWLEDGE, decode(buffer).unwrap()); -} - -#[test] -fn to_buffer_correctly_serializes_client_pdu() { - let buffer = encode_vec(&*HEADER_WITH_FRAME_ACKNOWLEDGE).unwrap(); - - assert_eq!(buffer, HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.as_slice()); -} - -#[test] -fn buffer_length_is_correct_for_client_pdu() { - assert_eq!( - HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER.len(), - HEADER_WITH_FRAME_ACKNOWLEDGE.size() - ); -} - -#[test] -fn from_buffer_correctly_parses_wire_to_surface_1_pdu() { - let buffer = WIRE_TO_SURFACE_1_BUFFER.as_ref(); - - assert_eq!(*WIRE_TO_SURFACE_1, decode(buffer).unwrap()); -} - -#[test] -fn to_buffer_correctly_serializes_wire_to_surface_1_pdu() { - let buffer = encode_vec(&*WIRE_TO_SURFACE_1).unwrap(); - - assert_eq!(buffer, WIRE_TO_SURFACE_1_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_wire_to_surface_1_pdu() { - assert_eq!(WIRE_TO_SURFACE_1_BUFFER.len(), WIRE_TO_SURFACE_1.size()); -} - -#[test] -fn from_buffer_correctly_parses_wire_to_surface_2_pdu() { - let buffer = WIRE_TO_SURFACE_2_BUFFER.as_ref(); - - assert_eq!(*WIRE_TO_SURFACE_2, decode(buffer).unwrap()); -} - -#[test] -fn to_buffer_correctly_serializes_wire_to_surface_2_pdu() { - let buffer = encode_vec(&*WIRE_TO_SURFACE_2).unwrap(); - - assert_eq!(buffer, WIRE_TO_SURFACE_2_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_wire_to_surface_2_pdu() { - assert_eq!(WIRE_TO_SURFACE_2_BUFFER.len(), WIRE_TO_SURFACE_2.size()); -} - -#[test] -fn from_buffer_correctly_parses_delete_encoding_context_pdu() { - let buffer = DELETE_ENCODING_CONTEXT_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*DELETE_ENCODING_CONTEXT, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_delete_encoding_context_pdu() { - let buffer = encode_vec(&*DELETE_ENCODING_CONTEXT).unwrap(); - - assert_eq!(buffer, DELETE_ENCODING_CONTEXT_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_delete_encoding_context_pdu() { - assert_eq!(DELETE_ENCODING_CONTEXT_BUFFER.len(), DELETE_ENCODING_CONTEXT.size()); -} - -#[test] -fn from_buffer_correctly_parses_solid_fill_pdu() { - let buffer = SOLID_FILL_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*SOLID_FILL, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_solid_fill_pdu() { - let buffer = encode_vec(&*SOLID_FILL).unwrap(); - assert_eq!(buffer, SOLID_FILL_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_solid_fill_pdu() { - assert_eq!(SOLID_FILL_BUFFER.len(), SOLID_FILL.size()); -} - -#[test] -fn from_buffer_correctly_parses_surface_to_surface_pdu() { - let buffer = SURFACE_TO_SURFACE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*SURFACE_TO_SURFACE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_surface_to_surface_pdu() { - let buffer = encode_vec(&*SURFACE_TO_SURFACE).unwrap(); - - assert_eq!(buffer, SURFACE_TO_SURFACE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_surface_to_surface_pdu() { - assert_eq!(SURFACE_TO_SURFACE_BUFFER.len(), SURFACE_TO_SURFACE.size()); -} - -#[test] -fn from_buffer_correctly_parses_surface_to_cache_pdu() { - let buffer = SURFACE_TO_CACHE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*SURFACE_TO_CACHE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_surface_to_cache_pdu() { - let buffer = encode_vec(&*SURFACE_TO_CACHE).unwrap(); - - assert_eq!(buffer, SURFACE_TO_CACHE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_surface_to_cache_pdu() { - assert_eq!(SURFACE_TO_CACHE_BUFFER.len(), SURFACE_TO_CACHE.size()); -} - -#[test] -fn from_buffer_correctly_parses_cache_to_surface_pdu() { - let buffer = CACHE_TO_SURFACE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*CACHE_TO_SURFACE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_cache_to_surface_pdu() { - let buffer = encode_vec(&*CACHE_TO_SURFACE).unwrap(); - - assert_eq!(buffer, CACHE_TO_SURFACE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_cache_to_surface_pdu() { - assert_eq!(CACHE_TO_SURFACE_BUFFER.len(), CACHE_TO_SURFACE.size()); -} - -#[test] -fn from_buffer_correctly_parses_create_surface_pdu() { - let buffer = CREATE_SURFACE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*CREATE_SURFACE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_create_surface_pdu() { - let buffer = encode_vec(&*CREATE_SURFACE).unwrap(); - - assert_eq!(buffer, CREATE_SURFACE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_create_surface_pdu() { - assert_eq!(CREATE_SURFACE_BUFFER.len(), CREATE_SURFACE.size()); -} - -#[test] -fn from_buffer_correctly_parses_delete_surface_pdu() { - let buffer = DELETE_SURFACE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*DELETE_SURFACE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_delete_surface_pdu() { - let buffer = encode_vec(&*DELETE_SURFACE).unwrap(); - - assert_eq!(buffer, DELETE_SURFACE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_delete_surface_pdu() { - assert_eq!(DELETE_SURFACE_BUFFER.len(), DELETE_SURFACE.size()); -} - -#[test] -fn from_buffer_correctly_parses_reset_graphics() { - let buffer = RESET_GRAPHICS_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*RESET_GRAPHICS, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_reset_graphics() { - let buffer = encode_vec(&*RESET_GRAPHICS).unwrap(); - - assert_eq!(buffer, RESET_GRAPHICS_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_reset_graphics() { - assert_eq!(RESET_GRAPHICS_BUFFER.len(), RESET_GRAPHICS.size()); -} - -#[test] -fn from_buffer_correctly_parses_map_surface_to_output_pdu() { - let buffer = MAP_SURFACE_TO_OUTPUT_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*MAP_SURFACE_TO_OUTPUT, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_map_surface_to_output_pdu() { - let buffer = encode_vec(&*MAP_SURFACE_TO_OUTPUT).unwrap(); - - assert_eq!(buffer, MAP_SURFACE_TO_OUTPUT_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_map_surface_to_output_pdu() { - assert_eq!(MAP_SURFACE_TO_OUTPUT_BUFFER.len(), MAP_SURFACE_TO_OUTPUT.size()); -} - -#[test] -fn from_buffer_correctly_parses_evict_cache_entry_pdu() { - let buffer = EVICT_CACHE_ENTRY_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*EVICT_CACHE_ENTRY, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_evict_cache_entry_pdu() { - let buffer = encode_vec(&*EVICT_CACHE_ENTRY).unwrap(); - - assert_eq!(buffer, EVICT_CACHE_ENTRY_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_evict_cache_entry_pdu() { - assert_eq!(EVICT_CACHE_ENTRY_BUFFER.len(), EVICT_CACHE_ENTRY.size()); -} - -#[test] -fn from_buffer_correctly_parses_start_frame_pdu() { - let buffer = START_FRAME_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*START_FRAME, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_start_frame_pdu() { - let buffer = encode_vec(&*START_FRAME).unwrap(); - - assert_eq!(buffer, START_FRAME_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_start_frame_pdu() { - assert_eq!(START_FRAME_BUFFER.len(), START_FRAME.size()); -} - -#[test] -fn from_buffer_correctly_parses_end_frame_pdu() { - let buffer = END_FRAME_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*END_FRAME, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_end_frame_pdu() { - let buffer = encode_vec(&*END_FRAME).unwrap(); - - assert_eq!(buffer, END_FRAME_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_end_frame_pdu() { - assert_eq!(END_FRAME_BUFFER.len(), END_FRAME.size()); -} - -#[test] -fn from_buffer_correctly_parses_capabilities_confirm_pdu() { - let buffer = CAPABILITIES_CONFIRM_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*CAPABILITIES_CONFIRM, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_capabilities_confirm_pdu() { - let buffer = encode_vec(&*CAPABILITIES_CONFIRM).unwrap(); - - assert_eq!(buffer, CAPABILITIES_CONFIRM_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_capabilities_confirm_pdu() { - assert_eq!(CAPABILITIES_CONFIRM_BUFFER.len(), CAPABILITIES_CONFIRM.size()); -} - -#[test] -fn from_buffer_correctly_parses_capabilities_advertise_pdu() { - let buffer = CAPABILITIES_ADVERTISE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*CAPABILITIES_ADVERTISE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_capabilities_advertise_pdu() { - let buffer = encode_vec(&*CAPABILITIES_ADVERTISE).unwrap(); - - assert_eq!(buffer, CAPABILITIES_ADVERTISE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_capabilities_advertise_pdu() { - assert_eq!(CAPABILITIES_ADVERTISE_BUFFER.len(), CAPABILITIES_ADVERTISE.size()); -} - -#[test] -fn from_buffer_correctly_parses_frame_acknowledge_pdu() { - let buffer = FRAME_ACKNOWLEDGE_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*FRAME_ACKNOWLEDGE, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_frame_acknowledge_pdu() { - let buffer = encode_vec(&*FRAME_ACKNOWLEDGE).unwrap(); - - assert_eq!(buffer, FRAME_ACKNOWLEDGE_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_frame_acknowledge_pdu() { - assert_eq!(FRAME_ACKNOWLEDGE_BUFFER.len(), FRAME_ACKNOWLEDGE.size()); -} - -#[test] -fn from_buffer_correctly_parses_cache_import_reply() { - let buffer = CACHE_IMPORT_REPLY_BUFFER.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*CACHE_IMPORT_REPLY, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_correctly_serializes_cache_import_reply() { - let buffer = encode_vec(&*CACHE_IMPORT_REPLY).unwrap(); - - assert_eq!(buffer, CACHE_IMPORT_REPLY_BUFFER.as_ref()); -} - -#[test] -fn buffer_length_is_correct_for_cache_import_reply() { - assert_eq!(CACHE_IMPORT_REPLY_BUFFER.len(), CACHE_IMPORT_REPLY.size()); -} - -#[test] -fn from_buffer_consume_correctly_parses_incorrect_len_avc_444_message() { - let buffer = AVC_444_MESSAGE_INCORRECT_LEN.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*AVC_444_BITMAP, decode_cursor(&mut cursor).unwrap()); - assert!(!cursor.is_empty()); -} - -#[test] -fn from_buffer_consume_correctly_parses_avc_444_message() { - let buffer = AVC_444_MESSAGE_CORRECT_LEN.as_ref(); - - let mut cursor = ReadCursor::new(buffer); - assert_eq!(*AVC_444_BITMAP, decode_cursor(&mut cursor).unwrap()); - assert!(cursor.is_empty()); -} - -#[test] -fn to_buffer_consume_correctly_serializes_avc_444_message() { - let buffer = encode_vec(&*AVC_444_BITMAP).unwrap(); - let expected = AVC_444_MESSAGE_CORRECT_LEN.as_ref(); - - assert_eq!(expected, buffer.as_slice()); -} diff --git a/crates/ironrdp-testsuite-core/tests/pdu/mod.rs b/crates/ironrdp-testsuite-core/tests/pdu/mod.rs index 13110c527..10d7e0f52 100644 --- a/crates/ironrdp-testsuite-core/tests/pdu/mod.rs +++ b/crates/ironrdp-testsuite-core/tests/pdu/mod.rs @@ -1,5 +1,4 @@ mod gcc; -mod gfx; mod input; mod mcs; mod pointer; diff --git a/crates/ironrdp-testsuite-extra/tests/tests.rs b/crates/ironrdp-testsuite-extra/tests/tests.rs index d041b6f64..960ba3c37 100644 --- a/crates/ironrdp-testsuite-extra/tests/tests.rs +++ b/crates/ironrdp-testsuite-extra/tests/tests.rs @@ -300,5 +300,6 @@ fn default_client_config() -> connector::Config { no_server_pointer: true, pointer_software_rendering: true, performance_flags: Default::default(), + support_gfx: false, } } diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index 65251817d..1dfc6135f 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -863,6 +863,7 @@ fn build_config( request_data: None, pointer_software_rendering: false, performance_flags: PerformanceFlags::default(), + support_gfx: false, desktop_scale_factor: 0, hardware_id: None, license_cache: None, diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index 9aa15506c..045abbabf 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -31,6 +31,7 @@ dvc = ["dep:ironrdp-dvc"] rdpdr = ["dep:ironrdp-rdpdr"] rdpsnd = ["dep:ironrdp-rdpsnd"] displaycontrol = ["dep:ironrdp-displaycontrol"] +egfx = ["dep:ironrdp-egfx"] [dependencies] ironrdp-core = { workspace = true, optional = true } @@ -47,6 +48,7 @@ ironrdp-dvc = { workspace = true, optional = true } ironrdp-rdpdr = { workspace = true, optional = true } ironrdp-rdpsnd = { workspace = true, optional = true } ironrdp-displaycontrol = { workspace = true, optional = true } +ironrdp-egfx = { workspace = true, optional = true } [dev-dependencies] ironrdp-blocking.workspace = true diff --git a/crates/ironrdp/examples/screenshot.rs b/crates/ironrdp/examples/screenshot.rs index 1e5da29db..4d9a4dcc5 100644 --- a/crates/ironrdp/examples/screenshot.rs +++ b/crates/ironrdp/examples/screenshot.rs @@ -210,6 +210,7 @@ fn build_config(username: String, password: String, domain: Option) -> c autologon: false, pointer_software_rendering: true, performance_flags: PerformanceFlags::default(), + support_gfx: false, desktop_scale_factor: 0, hardware_id: None, license_cache: None, diff --git a/crates/ironrdp/src/lib.rs b/crates/ironrdp/src/lib.rs index 5caad0875..67cadbcf7 100644 --- a/crates/ironrdp/src/lib.rs +++ b/crates/ironrdp/src/lib.rs @@ -31,6 +31,10 @@ pub use ironrdp_displaycontrol as displaycontrol; #[doc(inline)] pub use ironrdp_dvc as dvc; +#[cfg(feature = "egfx")] +#[doc(inline)] +pub use ironrdp_egfx as egfx; + #[cfg(feature = "graphics")] #[doc(inline)] pub use ironrdp_graphics as graphics; diff --git a/ffi/src/connector/config.rs b/ffi/src/connector/config.rs index 6888339be..d40352ec8 100644 --- a/ffi/src/connector/config.rs +++ b/ffi/src/connector/config.rs @@ -41,6 +41,7 @@ pub mod ffi { pub autologon: Option, pub pointer_software_rendering: Option, pub performance_flags: Option, + pub support_gfx: Option, } #[diplomat::enum_convert(ironrdp::pdu::gcc::KeyboardType)] @@ -147,6 +148,10 @@ pub mod ffi { self.autologon = Some(autologon); } + pub fn set_support_gfx(&mut self, support_gfx: bool) { + self.support_gfx = Some(support_gfx); + } + pub fn set_pointer_software_rendering(&mut self, pointer_software_rendering: bool) { self.pointer_software_rendering = Some(pointer_software_rendering); } @@ -195,6 +200,7 @@ pub mod ffi { request_data: None, pointer_software_rendering: self.pointer_software_rendering.unwrap_or(false), performance_flags: self.performance_flags.ok_or("performance flag is missing")?, + support_gfx: self.support_gfx.unwrap_or(false), desktop_scale_factor: 0, hardware_id: None, license_cache: None, diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 8cf123c86..7df648cd4 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -322,6 +322,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "ironrdp-egfx" +version = "0.1.1" +dependencies = [ + "bit_field", + "bitflags 2.6.0", + "ironrdp-core", + "ironrdp-dvc", + "ironrdp-graphics", + "ironrdp-pdu", + "tracing", +] + [[package]] name = "ironrdp-error" version = "0.1.1" @@ -343,6 +356,7 @@ dependencies = [ "ironrdp-cliprdr-format", "ironrdp-core", "ironrdp-displaycontrol", + "ironrdp-egfx", "ironrdp-graphics", "ironrdp-pdu", "ironrdp-rdpdr",