Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,31 @@ Remote Desktop opens RDP, VNC, Apple Remote Desktop, RustDesk and Telnet session

Read the [docs](https://docs.termix.site/plugins/remote-desktop) to set it up and use it.

### RDP graphics compatibility

The built-in engine supports the RDP Graphics Pipeline (MS-RDPEGFX), including
non-AVC ClearCodec, RDP6 Planar and RFX Progressive rendering. Graphics Pipeline
negotiation is enabled independently of display resizing, so servers that require
it can connect even when display control is disabled.

H.264/AVC420 and AVC444 are not supported or advertised by the built-in engine.
Servers configured to require these codecs, including some KDE KRDP versions,
need a compatible external guacd build or a native RDP client. An administrator
can configure a saved host's **RDP** tab → **guacd Proxy (optional)** with the
external server's hostname and port. Check that the chosen guacd/FreeRDP build
supports the codec required by the server; guacd versions differ. Connections
are never automatically retried through another backend.

The graphics regression tests cover capability negotiation, encoded Planar
frames through X224/DRDYNVC into the framebuffer, frame responses and WASM
resize/display events. They do not substitute for live GNOME/KDE acceptance.
To run them after installing the matching `wasm-bindgen-cli` version:

```sh
cd native/engine
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner cargo test --locked --target wasm32-unknown-unknown graphics_tests
```

<br />

## Features
Expand Down
86 changes: 86 additions & 0 deletions native/engine/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions native/engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ url = "2.5"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[dev-dependencies]
wasm-bindgen-test = "=0.3.54"

[profile.release]
opt-level = 3
lto = true
Expand Down
35 changes: 29 additions & 6 deletions native/engine/src/rdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use ironrdp::connector::{
use ironrdp::displaycontrol::client::DisplayControlClient;
use ironrdp::displaycontrol::pdu::MonitorLayoutEntry;
use ironrdp::dvc::DrdynvcClient;
use ironrdp_egfx::client::{GraphicsPipelineClient, GraphicsPipelineHandler};
use ironrdp::graphics::image_processing::PixelFormat;
use ironrdp::pdu::gcc::KeyboardType;
use ironrdp::pdu::input::fast_path::{FastPathInputEvent, KeyboardFlags, SynchronizeFlags};
Expand All @@ -37,6 +38,20 @@ use crate::encode;
use crate::rail::RailApp;
use crate::sound::SoundBackend;

// IronRDP's ActiveStage composites this channel into the session framebuffer.
// No H.264 decoder is configured, so only non-AVC capabilities are advertised.
struct GraphicsHandler;
impl GraphicsPipelineHandler for GraphicsHandler {}

fn dynamic_channels(display_control: bool) -> DrdynvcClient {
let mut channels = DrdynvcClient::new()
.with_dynamic_channel(GraphicsPipelineClient::new(Box::new(GraphicsHandler), None));
if display_control {
channels.attach_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new())));
}
channels
}

#[derive(Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct Config {
Expand Down Expand Up @@ -529,7 +544,7 @@ impl RdpClient {
request_data: None,
pointer_software_rendering: false,
multitransport_flags: None,
support_dyn_vc_gfx_protocol: false,
support_dyn_vc_gfx_protocol: true,
performance_flags: perf,
desktop_scale_factor: if (100..=500).contains(&c.scale) { c.scale } else { 0 },
hardware_id: None,
Expand Down Expand Up @@ -599,11 +614,7 @@ impl RdpClient {
}
connector.attach_static_channel(rdpdr);
}
if self.config.display_control {
connector.attach_static_channel(
DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))),
);
}
connector.attach_static_channel(dynamic_channels(self.config.display_control));
connector
}

Expand Down Expand Up @@ -916,8 +927,16 @@ impl RdpClient {
return false;
}
let Session { stage, image, .. } = &mut *session;
let previous_size = (image.width(), image.height());
match stage.process(image, info.action, &frame) {
Ok(outputs) => {
// GFX ResetGraphics can resize without a deactivation sequence.
if (image.width(), image.height()) != previous_size {
let e = obj("resize");
set(&e, "w", image.width());
set(&e, "h", image.height());
self.events.push(e.into());
}
let keep = self.handle_outputs(&mut session, outputs);
if keep {
self.phase = Phase::Active(session);
Expand Down Expand Up @@ -1282,3 +1301,7 @@ fn classify(error: &connector::ConnectorError) -> &'static str {
_ => "protocol",
}
}

#[cfg(test)]
#[path = "rdp_graphics_tests.rs"]
mod graphics_tests;
Loading
Loading