Overview
The ironrdp-session crate provides state machines for managing an active RDP session after the connection is established. It handles:
- Fast-path and X.224 PDU processing
- Graphics output (bitmaps, surface commands, RemoteFX)
- Pointer (cursor) updates
- Graceful disconnection
- Bulk data decompression
Like ironrdp-connector, this crate is transport-agnostic and operates on byte buffers.
Core Types
ActiveStage
pub struct ActiveStage { /* ... */ }
impl ActiveStage {
pub fn new(
user_channel_id: u16,
io_channel_id: u16,
no_server_pointer: bool,
pointer_software_rendering: bool,
desktop_size: DesktopSize,
) -> Self;
pub fn process(
&mut self,
image: &mut dyn image::DecodedImage,
payload: &[u8],
output_buf: &mut WriteBuf,
) -> SessionResult<ActiveStageOutput>;
}
Parameters:
MCS user channel ID from connection result
MCS I/O channel ID from connection result
If true, render pointer locally instead of using server-provided cursor
pointer_software_rendering
Use software rendering for pointer updates
Initial desktop dimensions
ActiveStageOutput
pub enum ActiveStageOutput {
GraphicsUpdate,
PointerDefault,
PointerHidden,
PointerPosition { x: u16, y: u16 },
PointerBitmap { /* ... */ },
Terminate(GracefulDisconnectReason),
}
Output events from processing a PDU.
Variants:
Graphics data was decoded into the DecodedImage
Use the default system pointer
Update pointer to a custom bitmap
Server requested graceful disconnection
GracefulDisconnectReason
pub enum GracefulDisconnectReason {
UserInitiated,
ServerInitiated,
// ... other reasons
}
Image Handling
DecodedImage Trait
pub trait DecodedImage {
fn width(&self) -> usize;
fn height(&self) -> usize;
fn from_bitmap(&mut self, region: &BitmapRegion<'_>) -> SessionResult<()>;
fn read_to_buffer(&self, buffer: &mut [u8]) -> SessionResult<()>;
}
Implement this trait to receive decoded graphics updates.
Example Implementation:
use ironrdp_session::image::{DecodedImage, BitmapRegion};
use ironrdp_session::SessionResult;
struct MyImage {
width: usize,
height: usize,
data: Vec<u8>, // BGRA format
}
impl DecodedImage for MyImage {
fn width(&self) -> usize { self.width }
fn height(&self) -> usize { self.height }
fn from_bitmap(&mut self, region: &BitmapRegion<'_>) -> SessionResult<()> {
// Decode bitmap data into self.data at the specified region
// region.left, region.top, region.width, region.height
// region.pixel_format, region.pixel_data
Ok(())
}
fn read_to_buffer(&self, buffer: &mut [u8]) -> SessionResult<()> {
buffer.copy_from_slice(&self.data);
Ok(())
}
}
BitmapRegion
pub struct BitmapRegion<'a> {
pub left: usize,
pub top: usize,
pub width: usize,
pub height: usize,
pub pixel_format: PixelFormat,
pub pixel_data: &'a [u8],
}
PDU Processing Modules
Fast-Path
Processes fast-path output PDUs (most graphics updates).
X.224
Processes X.224-framed PDUs (licensing, capability sets).
Legacy
Handles legacy graphics orders.
Pointer
Pointer/cursor update processing.
RemoteFX
RemoteFX tile decoding.
Usage Example
use ironrdp_session::{ActiveStage, ActiveStageOutput};
use ironrdp_connector::{ConnectionResult, DesktopSize};
use ironrdp_core::WriteBuf;
// After connection is established
let result: ConnectionResult = /* ... */;
let mut session = ActiveStage::new(
result.user_channel_id,
result.io_channel_id,
result.no_server_pointer,
result.pointer_software_rendering,
result.desktop_size,
);
let mut image = MyImage::new(result.desktop_size.width, result.desktop_size.height);
let mut output = WriteBuf::new();
loop {
// ... receive PDU data into input_buf
match session.process(&mut image, &input_buf, &mut output)? {
ActiveStageOutput::GraphicsUpdate => {
// Image was updated, render it
render_frame(&image);
},
ActiveStageOutput::PointerPosition { x, y } => {
// Move cursor to (x, y)
},
ActiveStageOutput::Terminate(reason) => {
println!("Session terminated: {:?}", reason);
break;
},
_ => {}
}
// Send any output PDUs
if !output.filled().is_empty() {
// ... send output.filled() over network
output.clear();
}
}
Error Handling
pub type SessionResult<T> = Result<T, SessionError>;
pub type SessionError = ironrdp_error::Error<SessionErrorKind>;
#[non_exhaustive]
pub enum SessionErrorKind {
Pdu(ironrdp_pdu::PduError),
Encode(ironrdp_core::EncodeError),
Decode(ironrdp_core::DecodeError),
Reason(String),
General,
Custom,
}
Bulk Compression
The session automatically handles bulk decompression when the server sends compressed PDUs:
- MPPC (8 KB or 64 KB history)
- NCRUSH (RDP 6.0)
- XCRUSH (RDP 6.1)
Compression is negotiated during connection via Config::compression_type in ironrdp-connector.
Features
Enable QOI image codec support
Enable QOI + Zstd compressed image support (requires qoi)
Dependencies
ironrdp-core - Core traits (public)
ironrdp-pdu - PDU structures (public)
ironrdp-graphics - Image processing (public)
ironrdp-connector - Connection result types (public)
ironrdp-svc - Virtual channel support (public)
ironrdp-dvc - Dynamic virtual channels (public)
ironrdp-bulk - Bulk decompression
ironrdp-displaycontrol - Display control channel
Usage Notes
Image Ownership:
The DecodedImage is owned by the caller. The session decodes graphics updates directly into your image buffer, avoiding unnecessary copies.
PDU Framing:
The session expects complete PDUs (after TPKT/X.224/MCS unwrapping). Use ironrdp_pdu::find_size() to detect PDU boundaries in your framing layer.
Output Buffer:
Reuse the WriteBuf across calls:let mut output = WriteBuf::new();
loop {
session.process(&mut image, input, &mut output)?;
// ... send output.filled()
output.clear(); // Reuse buffer
}
See Also