1use std::sync::Arc;
2
3use ironrdp_core::{decode_cursor, DecodeErrorKind, ReadCursor, WriteBuf};
4use ironrdp_graphics::image_processing::PixelFormat;
5use ironrdp_graphics::pointer::{DecodedPointer, PointerBitmapTarget};
6use ironrdp_graphics::rdp6::BitmapStreamDecoder;
7use ironrdp_graphics::rle::RlePixelFormat;
8use ironrdp_pdu::codecs::rfx::FrameAcknowledgePdu;
9use ironrdp_pdu::fast_path::{FastPathHeader, FastPathUpdate, FastPathUpdatePdu, Fragmentation};
10use ironrdp_pdu::geometry::{InclusiveRectangle, Rectangle as _};
11use ironrdp_pdu::pointer::PointerUpdateData;
12use ironrdp_pdu::rdp::capability_sets::{CodecId, CODEC_ID_NONE, CODEC_ID_REMOTEFX};
13use ironrdp_pdu::rdp::headers::ShareDataPdu;
14use ironrdp_pdu::surface_commands::{FrameAction, FrameMarkerPdu, SurfaceCommand};
15use tracing::{debug, trace, warn};
16
17use crate::image::DecodedImage;
18use crate::pointer::PointerCache;
19use crate::{custom_err, reason_err, rfx, SessionError, SessionErrorExt as _, SessionResult};
20
21#[derive(Debug)]
22pub enum UpdateKind {
23 None,
24 Region(InclusiveRectangle),
25 PointerDefault,
26 PointerHidden,
27 PointerPosition { x: u16, y: u16 },
28 PointerBitmap(Arc<DecodedPointer>),
29}
30
31pub struct Processor {
32 complete_data: CompleteData,
33 rfx_handler: rfx::DecodingContext,
34 marker_processor: FrameMarkerProcessor,
35 bitmap_stream_decoder: BitmapStreamDecoder,
36 pointer_cache: PointerCache,
37 use_system_pointer: bool,
38 mouse_pos_update: Option<(u16, u16)>,
39 enable_server_pointer: bool,
40 pointer_software_rendering: bool,
41 #[cfg(feature = "qoiz")]
42 zdctx: zstd_safe::DCtx<'static>,
43}
44
45impl Processor {
46 pub fn update_mouse_pos(&mut self, x: u16, y: u16) {
47 self.mouse_pos_update = Some((x, y));
48 }
49
50 pub fn process(
52 &mut self,
53 image: &mut DecodedImage,
54 input: &[u8],
55 output: &mut WriteBuf,
56 ) -> SessionResult<Vec<UpdateKind>> {
57 let mut processor_updates = Vec::new();
58
59 if let Some((x, y)) = self.mouse_pos_update.take() {
60 if let Some(rect) = image.move_pointer(x, y)? {
61 processor_updates.push(UpdateKind::Region(rect));
62 }
63 }
64
65 let mut input = ReadCursor::new(input);
66
67 let header = decode_cursor::<FastPathHeader>(&mut input).map_err(SessionError::decode)?;
68 trace!(fast_path_header = ?header, "Received Fast-Path packet");
69
70 let update_pdu = decode_cursor::<FastPathUpdatePdu<'_>>(&mut input).map_err(SessionError::decode)?;
71 trace!(fast_path_update_fragmentation = ?update_pdu.fragmentation);
72
73 let processed_complete_data = self
74 .complete_data
75 .process_data(update_pdu.data, update_pdu.fragmentation);
76
77 let update_code = update_pdu.update_code;
78
79 let Some(data) = processed_complete_data else {
80 return Ok(Vec::new());
81 };
82
83 let update = FastPathUpdate::decode_with_code(data.as_slice(), update_code);
84
85 match update {
86 Ok(FastPathUpdate::SurfaceCommands(surface_commands)) => {
87 trace!("Received Surface Commands: {} pieces", surface_commands.len());
88 let update_region = self.process_surface_commands(image, output, surface_commands)?;
89 processor_updates.push(UpdateKind::Region(update_region));
90 }
91 Ok(FastPathUpdate::Bitmap(bitmap_update)) => {
92 trace!("Received bitmap update");
93
94 let mut buf = Vec::new();
95 let mut update_kind = UpdateKind::None;
96
97 for update in bitmap_update.rectangles {
98 trace!("{update:?}");
99 buf.clear();
100
101 let update_rectangle = if update
105 .compression_flags
106 .contains(ironrdp_pdu::bitmap::Compression::BITMAP_COMPRESSION)
107 {
108 if update.bits_per_pixel == 32 {
109 debug!("32 bpp compressed RDP6_BITMAP_STREAM");
113
114 match self.bitmap_stream_decoder.decode_bitmap_stream_to_rgb24(
115 update.bitmap_data,
116 &mut buf,
117 usize::from(update.width),
118 usize::from(update.height),
119 ) {
120 Ok(()) => image.apply_rgb24(&buf, &update.rectangle, true)?,
121 Err(err) => {
122 warn!("Invalid RDP6_BITMAP_STREAM: {err}");
123 update.rectangle.clone()
124 }
125 }
126 } else {
127 debug!(bpp = update.bits_per_pixel, "Non-32 bpp compressed RLE_BITMAP_STREAM",);
131
132 match ironrdp_graphics::rle::decompress(
133 update.bitmap_data,
134 &mut buf,
135 usize::from(update.width),
136 usize::from(update.height),
137 usize::from(update.bits_per_pixel),
138 ) {
139 Ok(RlePixelFormat::Rgb16) => image.apply_rgb16_bitmap(&buf, &update.rectangle)?,
140
141 Ok(format @ (RlePixelFormat::Rgb8 | RlePixelFormat::Rgb15 | RlePixelFormat::Rgb24)) => {
143 warn!("Received RLE-compressed bitmap with unsupported color depth: {format:?}");
144 update.rectangle.clone()
145 }
146
147 Err(e) => {
148 warn!("Invalid RLE-compressed bitmap: {e}");
149 update.rectangle.clone()
150 }
151 }
152 }
153 } else {
154 trace!("Uncompressed raw bitmap");
158
159 match update.bits_per_pixel {
160 16 => image.apply_rgb16_bitmap(update.bitmap_data, &update.rectangle)?,
161 unsupported => {
163 warn!("Invalid raw bitmap with {unsupported} bytes per pixels");
164 update.rectangle.clone()
165 }
166 }
167 };
168
169 match update_kind {
170 UpdateKind::Region(current) => {
171 update_kind = UpdateKind::Region(current.union(&update_rectangle))
172 }
173 _ => update_kind = UpdateKind::Region(update_rectangle),
174 }
175 }
176
177 processor_updates.push(update_kind);
178 }
179 Ok(FastPathUpdate::Pointer(update)) => {
180 if !self.enable_server_pointer {
181 return Ok(processor_updates);
182 }
183
184 let bitmap_target = if self.pointer_software_rendering {
185 PointerBitmapTarget::Software
186 } else {
187 PointerBitmapTarget::Accelerated
188 };
189
190 match update {
191 PointerUpdateData::SetHidden => {
192 processor_updates.push(UpdateKind::PointerHidden);
193 if self.pointer_software_rendering && !self.use_system_pointer {
194 self.use_system_pointer = true;
195 if let Some(rect) = image.hide_pointer()? {
196 processor_updates.push(UpdateKind::Region(rect));
197 }
198 }
199 }
200 PointerUpdateData::SetDefault => {
201 processor_updates.push(UpdateKind::PointerDefault);
202 if self.pointer_software_rendering && !self.use_system_pointer {
203 self.use_system_pointer = true;
204 if let Some(rect) = image.hide_pointer()? {
205 processor_updates.push(UpdateKind::Region(rect));
206 }
207 }
208 }
209 PointerUpdateData::SetPosition(position) => {
210 if self.use_system_pointer || !self.pointer_software_rendering {
211 processor_updates.push(UpdateKind::PointerPosition {
212 x: position.x,
213 y: position.y,
214 });
215 } else if let Some(rect) = image.move_pointer(position.x, position.y)? {
216 processor_updates.push(UpdateKind::Region(rect));
217 }
218 }
219 PointerUpdateData::Color(pointer) => {
220 let cache_index = pointer.cache_index;
221
222 let decoded_pointer = Arc::new(
223 DecodedPointer::decode_color_pointer_attribute(&pointer, bitmap_target)
224 .expect("Failed to decode color pointer attribute"),
225 );
226
227 let _ = self
228 .pointer_cache
229 .insert(usize::from(cache_index), Arc::clone(&decoded_pointer));
230
231 if !self.pointer_software_rendering {
232 processor_updates.push(UpdateKind::PointerBitmap(Arc::clone(&decoded_pointer)));
233 } else if let Some(rect) = image.update_pointer(decoded_pointer)? {
234 processor_updates.push(UpdateKind::Region(rect));
235 }
236 }
237 PointerUpdateData::Cached(cached) => {
238 let cache_index = cached.cache_index;
239
240 if let Some(cached_pointer) = self.pointer_cache.get(usize::from(cache_index)) {
241 processor_updates.push(UpdateKind::PointerHidden);
243 self.use_system_pointer = false;
244 if !self.pointer_software_rendering {
246 processor_updates.push(UpdateKind::PointerBitmap(Arc::clone(&cached_pointer)));
247 } else if let Some(rect) = image.update_pointer(cached_pointer)? {
248 processor_updates.push(UpdateKind::Region(rect));
249 } else {
250 if let Some(rect) = image.show_pointer()? {
252 processor_updates.push(UpdateKind::Region(rect));
253 }
254 }
255 } else {
256 warn!("Cached pointer not found {}", cache_index);
257 }
258 }
259 PointerUpdateData::New(pointer) => {
260 let cache_index = pointer.color_pointer.cache_index;
261
262 let decoded_pointer = Arc::new(
263 DecodedPointer::decode_pointer_attribute(&pointer, bitmap_target)
264 .expect("Failed to decode pointer attribute"),
265 );
266
267 let _ = self
268 .pointer_cache
269 .insert(usize::from(cache_index), Arc::clone(&decoded_pointer));
270
271 if !self.pointer_software_rendering {
272 processor_updates.push(UpdateKind::PointerBitmap(Arc::clone(&decoded_pointer)));
273 } else if let Some(rect) = image.update_pointer(decoded_pointer)? {
274 processor_updates.push(UpdateKind::Region(rect));
275 }
276 }
277 PointerUpdateData::Large(pointer) => {
278 let cache_index = pointer.cache_index;
279
280 let decoded_pointer: Arc<DecodedPointer> = Arc::new(
281 DecodedPointer::decode_large_pointer_attribute(&pointer, bitmap_target)
282 .expect("Failed to decode large pointer attribute"),
283 );
284
285 let _ = self
286 .pointer_cache
287 .insert(usize::from(cache_index), Arc::clone(&decoded_pointer));
288
289 if !self.pointer_software_rendering {
290 processor_updates.push(UpdateKind::PointerBitmap(Arc::clone(&decoded_pointer)));
291 } else if let Some(rect) = image.update_pointer(decoded_pointer)? {
292 processor_updates.push(UpdateKind::Region(rect));
293 }
294 }
295 };
296 }
297 Err(e) => {
298 if let DecodeErrorKind::InvalidField { field, reason } = e.kind {
302 warn!(field, reason, "Received invalid Fast-Path update");
303 processor_updates.push(UpdateKind::None);
304 } else {
305 return Err(custom_err!("Fast-Path", e));
306 }
307 }
308 };
309
310 Ok(processor_updates)
311 }
312
313 fn process_surface_commands(
314 &mut self,
315 image: &mut DecodedImage,
316 output: &mut WriteBuf,
317 surface_commands: Vec<SurfaceCommand<'_>>,
318 ) -> SessionResult<InclusiveRectangle> {
319 let mut update_rectangle = None;
320
321 for command in surface_commands {
322 match command {
323 SurfaceCommand::SetSurfaceBits(bits) | SurfaceCommand::StreamSurfaceBits(bits) => {
324 let codec_id = CodecId::from_u8(bits.extended_bitmap_data.codec_id).ok_or_else(|| {
325 reason_err!(
326 "Fast-Path",
327 "unexpected codec ID: {:x}",
328 bits.extended_bitmap_data.codec_id
329 )
330 })?;
331
332 trace!(?codec_id, "Surface bits");
333
334 let destination = bits.destination;
335 let destination = InclusiveRectangle {
340 left: destination.left,
341 top: destination.top,
342 right: destination.right - 1,
343 bottom: destination.bottom - 1,
344 };
345 match codec_id {
346 CODEC_ID_NONE => {
347 let ext_data = bits.extended_bitmap_data;
348 match ext_data.bpp {
349 32 => {
350 let rectangle =
351 image.apply_rgb32_bitmap(ext_data.data, PixelFormat::BgrX32, &destination)?;
352 update_rectangle = update_rectangle
353 .map(|rect: InclusiveRectangle| rect.union(&rectangle))
354 .or(Some(rectangle));
355 }
356 bpp => {
357 warn!("Unsupported bpp: {bpp}")
358 }
359 }
360 }
361 CODEC_ID_REMOTEFX => {
362 let mut data = ReadCursor::new(bits.extended_bitmap_data.data);
363 while !data.is_empty() {
364 let (_frame_id, rectangle) = self.rfx_handler.decode(image, &destination, &mut data)?;
365 update_rectangle = update_rectangle
366 .map(|rect: InclusiveRectangle| rect.union(&rectangle))
367 .or(Some(rectangle));
368 }
369 }
370 #[cfg(feature = "qoi")]
371 ironrdp_pdu::rdp::capability_sets::CODEC_ID_QOI => {
372 qoi_apply(
373 image,
374 destination,
375 bits.extended_bitmap_data.data,
376 &mut update_rectangle,
377 )?;
378 }
379 #[cfg(feature = "qoiz")]
380 ironrdp_pdu::rdp::capability_sets::CODEC_ID_QOIZ => {
381 let compressed = &bits.extended_bitmap_data.data;
382 let mut input = zstd_safe::InBuffer::around(compressed);
383 let mut data = vec![0; compressed.len() * 4];
384 let mut pos = 0;
385 loop {
386 let mut output = zstd_safe::OutBuffer::around_pos(data.as_mut_slice(), pos);
387 self.zdctx
388 .decompress_stream(&mut output, &mut input)
389 .map_err(zstd_safe::get_error_name)
390 .map_err(|e| reason_err!("zstd", "{}", e))?;
391 pos = output.pos();
392 if pos == output.capacity() {
393 data.resize(data.capacity() * 2, 0);
394 } else {
395 break;
396 }
397 }
398
399 qoi_apply(image, destination, &data, &mut update_rectangle)?;
400 }
401 _ => {
402 warn!("Unsupported codec ID: {}", bits.extended_bitmap_data.codec_id);
403 }
404 }
405 }
406 SurfaceCommand::FrameMarker(marker) => {
407 trace!(
408 "Frame marker: action {:?} with ID #{}",
409 marker.frame_action,
410 marker.frame_id.unwrap_or(0)
411 );
412 self.marker_processor.process(&marker, output)?;
413 }
414 }
415 }
416
417 Ok(update_rectangle.unwrap_or_else(InclusiveRectangle::empty))
418 }
419}
420
421#[cfg(feature = "qoi")]
422fn qoi_apply(
423 image: &mut DecodedImage,
424 destination: InclusiveRectangle,
425 data: &[u8],
426 update_rectangle: &mut Option<InclusiveRectangle>,
427) -> SessionResult<()> {
428 let (header, decoded) = qoi::decode_to_vec(data).map_err(|e| reason_err!("QOI decode", "{}", e))?;
429 match header.channels {
430 qoi::Channels::Rgb => {
431 let rectangle = image.apply_rgb24(&decoded, &destination, false)?;
432
433 *update_rectangle = update_rectangle
434 .as_ref()
435 .map(|rect: &InclusiveRectangle| rect.union(&rectangle))
436 .or(Some(rectangle));
437 }
438 qoi::Channels::Rgba => {
439 warn!("Unsupported RGBA QOI data");
440 }
441 }
442 Ok(())
443}
444
445pub struct ProcessorBuilder {
446 pub io_channel_id: u16,
447 pub user_channel_id: u16,
448 pub enable_server_pointer: bool,
450 pub pointer_software_rendering: bool,
454}
455
456impl ProcessorBuilder {
457 pub fn build(self) -> Processor {
458 Processor {
459 complete_data: CompleteData::new(),
460 rfx_handler: rfx::DecodingContext::new(),
461 marker_processor: FrameMarkerProcessor::new(self.user_channel_id, self.io_channel_id),
462 bitmap_stream_decoder: BitmapStreamDecoder::default(),
463 pointer_cache: PointerCache::default(),
464 use_system_pointer: true,
465 mouse_pos_update: None,
466 enable_server_pointer: self.enable_server_pointer,
467 pointer_software_rendering: self.pointer_software_rendering,
468 #[cfg(feature = "qoiz")]
469 zdctx: zstd_safe::DCtx::default(),
470 }
471 }
472}
473
474#[derive(Debug, PartialEq)]
475struct CompleteData {
476 fragmented_data: Option<Vec<u8>>,
477}
478
479impl CompleteData {
480 fn new() -> Self {
481 Self { fragmented_data: None }
482 }
483
484 fn process_data(&mut self, data: &[u8], fragmentation: Fragmentation) -> Option<Vec<u8>> {
485 match fragmentation {
486 Fragmentation::Single => {
487 self.check_data_is_empty();
488
489 Some(data.to_vec())
490 }
491 Fragmentation::First => {
492 self.check_data_is_empty();
493
494 self.fragmented_data = Some(data.to_vec());
495
496 None
497 }
498 Fragmentation::Next => {
499 self.append_data(data);
500
501 None
502 }
503 Fragmentation::Last => {
504 self.append_data(data);
505
506 self.fragmented_data.take()
507 }
508 }
509 }
510
511 fn check_data_is_empty(&mut self) {
512 if self.fragmented_data.is_some() {
513 warn!("Skipping pending Fast-Path Update internal multiple elements data");
514 self.fragmented_data = None;
515 }
516 }
517
518 fn append_data(&mut self, data: &[u8]) {
519 if let Some(fragmented_data) = self.fragmented_data.as_mut() {
520 fragmented_data.extend_from_slice(data);
521 } else {
522 warn!("Got unexpected Next fragmentation PDU without prior First fragmentation PDU");
523 }
524 }
525}
526
527struct FrameMarkerProcessor {
528 user_channel_id: u16,
529 io_channel_id: u16,
530}
531
532impl FrameMarkerProcessor {
533 fn new(user_channel_id: u16, io_channel_id: u16) -> Self {
534 Self {
535 user_channel_id,
536 io_channel_id,
537 }
538 }
539
540 fn process(&mut self, marker: &FrameMarkerPdu, output: &mut WriteBuf) -> SessionResult<()> {
541 match marker.frame_action {
542 FrameAction::Begin => Ok(()),
543 FrameAction::End => {
544 ironrdp_connector::legacy::encode_share_data(
545 self.user_channel_id,
546 self.io_channel_id,
547 0,
548 ShareDataPdu::FrameAcknowledge(FrameAcknowledgePdu {
549 frame_id: marker.frame_id.unwrap_or(0),
550 }),
551 output,
552 )
553 .map_err(crate::legacy::map_error)?;
554
555 Ok(())
556 }
557 }
558 }
559}