Skip to content

feat(connector)!: expose SUPPORT_DYN_VC_GFX_PROTOCOL early-cap flag for EGFX clients - #1237

Merged
Marc-André Moreau (mamoreau-devolutions) merged 2 commits into
Devolutions:masterfrom
GlassOnTin:feat/egfx-early-cap-flag
Aug 30, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 2 commits into
Devolutions:masterfrom
GlassOnTin:feat/egfx-early-cap-flag

Conversation

@GlassOnTin

Copy link
Copy Markdown
Contributor

Currently, early_capability_flags in the GCC core data is built from a fixed set in connection.rs. Clients that want to use the Graphics Pipeline Extension (MS-RDPEGFX) — by attaching a DvcClientProcessor for Microsoft::Windows::RDS::Graphics — have no way to set SUPPORT_DYN_VC_GFX_PROTOCOL without forking the connector, and modern Windows servers won't open the EGFX channel unless the client advertises support.

This PR adds an opt-in Config.support_dyn_vc_gfx_protocol: bool (default false). When set, the flag is OR'd into early_capability_flags alongside the existing WANT_32_BPP_SESSION conditional. Existing consumers are unaffected; the doc comment includes a safety note that setting this without an EGFX implementation will cause Windows to stop sending legacy bitmap updates, leaving the desktop blank.

Used downstream by Haven (an Android RDP/VNC client) which implements EGFX with ClearCodec + RemoteFxProgressive decoders; this lets us drop a vendored fork of ironrdp-connector we currently carry just for this one flag.

Default false to preserve current behaviour. cargo check --workspace clean — six other in-tree Config { … } builders updated with the default-false field.

@glamberson

Copy link
Copy Markdown
Contributor

Reading through, this is the smallest possible fix and the doc-comment captures the foot-gun precisely. Without wiring a DvcClientProcessor for the Graphics channel, modern Windows servers stop sending legacy bitmap updates entirely and the desktop goes blank, which is exactly the GRD case in #1232 and the failure mode every downstream EGFX client hits.

No conflict with #1098 (multitransport bootstrapping) on my read of the GCC block area, just two independent flags being OR'd in. Looks good from the server-side perspective too: any IronRDP server that already speaks EGFX (lamco-rdp-server, GRD) sees this flag and routes graphics through DVC as expected.

@glamberson

Copy link
Copy Markdown
Contributor

GlassOnTin Hi, could you rebase this? I'm trying to get this prioritized, and this would be a first step on the client side work I'm looking t. Thanks!

@GlassOnTin
GlassOnTin force-pushed the feat/egfx-early-cap-flag branch from 84ddffa to dbe5d82 Compare July 20, 2026 19:24
@GlassOnTin

Copy link
Copy Markdown
Contributor Author

Done — rebased onto current master and force-pushed. It's a single commit again and GitHub now shows it as mergeable.

The only conflicts were in the test builders, where master had restructured things since I opened this: session/autodetect.rs's local test_config() and the monolithic testsuite-extra/tests/main.rs were removed/split, so I dropped my now-obsolete edits to those and instead added support_dyn_vc_gfx_protocol: false to the Config { … } literals in their new homes — connector/autodetect.rs, session/connection_activation.rs, and e2e.rs. The core change (the early_capability_flags OR in connection.rs and the field on Config) merged cleanly.

cargo check --workspace --all-targets is clean. Happy to squash/adjust further if you'd like it shaped differently for the client-side work.

@glamberson

Copy link
Copy Markdown
Contributor

Thank you! Benoît Cortier (@CBenoit) can you expedite this please? Thank you. My client issue will be posted later today.

@GlassOnTin

Copy link
Copy Markdown
Contributor Author

For reviewer context: I've opened #1472 as a sibling to this — a small ironrdp-pdu fix to tolerate an empty Server Font Map body (another VirtualBox VRDP quirk). Together these two are the last things a downstream Android RDP client needs to drop a vendored ironrdp-connector fork it currently carries: this PR removes the EGFX early-capability workaround, #1472 removes the Font Map one. Both are opt-in / strictly-more-lenient and don't change existing behaviour. No rush — just flagging they land the same cleanup.

Marc-André Moreau (mamoreau-devolutions) pushed a commit that referenced this pull request Jul 31, 2026
…tionResult (#1488)

Per [MS-RDPBCGR] 2.2.8.1.2, a client must not send fast-path input
events unless the server advertised `INPUT_FLAG_FASTPATH_INPUT` or
`INPUT_FLAG_FASTPATH_INPUT2` in its Input Capability Set. Today the
connector discards the server's capability sets after Demand Active, so
client code has no way to honour that requirement.

This PR captures the server's Input capability flags during capabilities
exchange, carries them through the `ConnectionFinalization`/`Finalized`
activation states (so they are refreshed correctly across a
Deactivation-Reactivation Sequence too), and surfaces them as
`ConnectionResult::input_flags`. The session layer can then choose
between fast-path and slow-path input per server.

### Motivation / real-world interop

This is not theoretical: VirtualBox's VRDP server closes the connection
outright on receiving a fast-path input PDU — its `VBox.log` reports

```
VRDP: Network packet length is incorrect 0x0004. Closing connection.
```

(a single fast-path scancode event is a 4-byte packet). VirtualBox never
advertises fast-path input; its Demand Active offers
`InputFlags(SCANCODES)` alone. mstsc and FreeRDP honour the negotiation
and fall back to slow-path `TS_INPUT_PDU`s, which is why they work
against VRDE.

Haven (Android RDP client built on IronRDP) has been shipping this exact
change as a vendored-connector patch since v5.86.1, with the slow-path
fallback keyed off `ConnectionResult::input_flags`. Verified against a
real VirtualBox 7.2.6 VRDE server: before the gate, the first arrow-key
press killed the session with the log line above; with the gate,
extended input sessions run clean, and a fast-path-capable server on the
same host still takes the fast-path branch. (Discussed in #1158; this is
the third and last piece Haven carries in its connector fork, alongside
#1237 and #1472.)

### Changes

- `connection_activation.rs`: capture `input_flags` from the
`CapabilitySet::Input` in Server Demand Active (empty if absent); add
the field to `ConnectionActivationState::{ConnectionFinalization,
Finalized}`.
- `connection.rs`: add `ConnectionResult::input_flags`, populated from
the `Finalized` state.
- Call sites in `ironrdp-client`, `ironrdp-web`, ffi, and the e2e test
updated for the new variant field (all currently ignore it).

### Testing

- Two new integration tests in
`ironrdp-testsuite-core/tests/session/connection_activation.rs`: the
fixture's Demand Active yields `SCANCODES | MOUSEX | UNICODE |
FASTPATH_INPUT_2` in the `ConnectionFinalization` state, and a Demand
Active with the Input capability stripped yields `InputFlags::empty()`.
- `cargo check --workspace --all-targets`, `cargo clippy --workspace
--all-targets`, and `cargo fmt --check` are clean; the 7 activation
tests pass.

Co-authored-by: GlassOnTin <GlassOnTin@users.noreply.github.com>
@github-actions github-actions Bot added scope/web Affects the web/WASM ecosystem scope/core Touches the core architectural tier A-extra size/S Size: up to 199 counted lines and 5 files; exceeds XS in either measure labels Jul 31, 2026
@CBenoit Benoît Cortier (CBenoit) added the risk/medium Behavioral change that does not substantially alter a core public API label Aug 3, 2026
@github-actions github-actions Bot added scope/ffi Affects native or .NET bindings size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure and removed size/S Size: up to 199 counted lines and 5 files; exceeds XS in either measure risk/medium Behavioral change that does not substantially alter a core public API labels Aug 4, 2026
@github-actions github-actions Bot added kind/protocol Affects RDP or related protocol behavior risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny and removed risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny labels Aug 5, 2026
@github-actions github-actions Bot added scope/cross-cutting Spans multiple architectural boundaries size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure and removed size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure labels Aug 5, 2026
…or EGFX clients

Adds an opt-in `Config::support_dyn_vc_gfx_protocol` (default `false`). When
set, `SUPPORT_DYN_VC_GFX_PROTOCOL` is OR'd into the client's early capability
flags, so the server may negotiate `Microsoft::Windows::RDS::Graphics` for
surface-based graphics instead of the legacy slow-path bitmap protocol.

BREAKING CHANGE: `Config` is not `#[non_exhaustive]`, so the new field breaks
downstream struct literals. Same shape as Devolutions#1501, which added
`auto_reconnect_cookie` to this struct.

The default is `false` on purpose. A client that advertises the bit without
wiring an EGFX-capable `DvcClientProcessor` for that channel gets modern
Windows servers to stop sending legacy bitmap updates altogether and route
everything over EGFX, leaving the desktop blank — so the doc comment spells
out the three steps to enable it properly.

Coverage:
  - create_gcc_blocks unit tests (inline mod at file end, per Devolutions#1527) assert
    the bit is clear by default, set when enabled, and that the opt-in's
    symmetric difference against the default flag set is exactly that one bit
    — so it cannot quietly disturb the others. Verified to fail: gating on
    `if false` reddens two of the three.
  - testsuite-extra e2e runs a full client<->server connect with the bit
    advertised. Scope stated in the test: it asserts negotiation completes,
    not that the server acted on it, since ironrdp-server does not surface
    the client's early capability flags.
@GlassOnTin
GlassOnTin force-pushed the feat/egfx-early-cap-flag branch from 1502311 to d911125 Compare August 20, 2026 21:10
@GlassOnTin

Copy link
Copy Markdown
Contributor Author

Rebased onto current master and back to a single commit (d911125). While doing so I found that a push on Aug 5 had accidentally stacked five unrelated commits onto this branch (they belonged with #1541 and with session work that has since landed upstream), so anyone opening the PR since then saw a six-commit mixture instead of the one-line capability flag. Sorry about that; it is cleaned up now.

Checked locally on the rebased branch: the fmt gate passes via cargo xtask check fmt, cargo check --workspace --all-targets is clean, and the connector unit tests pass including the three tests covering the flag (absent by default, set on opt-in, changes exactly one bit).

Benoît Cortier (@CBenoit) the state since your Aug 3 review is unchanged in substance: the PR is marked breaking (feat(connector)!:) for the added Config field, matching the shape #1501 shipped with, and the default keeps the wire bit clear. Is there anything else you would like changed, or can this go into the review queue? Greg Lamberson (@glamberson) mentioned in July this was a first step for their client-side work, so there is still downstream interest beyond mine.

@github-actions github-actions Bot added size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure and removed size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure labels Aug 20, 2026
Marc-André Moreau (mamoreau-devolutions) pushed a commit that referenced this pull request Aug 27, 2026
…1462)

## Summary

- `ironrdp-client` never registered `GraphicsPipelineClient`, so the
native client did not open the `Microsoft::Windows::RDS::Graphics`
channel and could not use the Graphics Pipeline.
- Registers it as a DVC alongside Display Control and Echo, with a no-op
`GraphicsPipelineHandler` (the compositor and session render do the
work) and no H.264 decoder, so the client advertises only the non-AVC
capability sets it can decode.

## Validation

- `cargo xtask check fmt/lints/tests/typos/locks` all pass.

## Notes

- Codec decoders (H.264 via `openh264-libloading`, ClearCodec #1175,
Progressive #1443) and the connector early-cap flag #1237 layer on
separately.
- #1377 and #1460 have merged. This is now stacked on #1461 alone, so
the merge order is #1461 then this, and #1461's commit appears in the
diff until it lands. Its own change is 3 files, +12/-1.
- Part of #1464. Motivated by #1446.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The capability flag wiring is protocol-conformant and preserves existing behavior by default. The inline notes are nonblocking follow-ups.

Note

Human-tuned, LLM-assisted content.

Comment thread crates/ironrdp-connector/src/lib.rs Resolved
Point EGFX users to the supported GraphicsPipelineClient registration path and clarify that disabling the flag only leaves EGFX unadvertised.

Move capability-bit coverage into the enabled core testsuite and decode the emitted Connect Initial PDU so normal CI exercises the assertion.
@github-actions github-actions Bot added risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure and removed size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny labels Aug 29, 2026
@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) merged commit 5bdb679 into Devolutions:master Aug 30, 2026
43 checks passed

This branch was previously deployed

1 inactive deployment
llm-providers — 28fabec6 Deployed Aug 29, 2026 by mamoreau-devolutions via Classify pull request #4384
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed/1 One automated review completed breaking-change Includes a breaking change, and requires special scrutiny at the boundaries kind/protocol Affects RDP or related protocol behavior needs-review A human reviewer is the current next actor risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny scope/core Touches the core architectural tier scope/cross-cutting Spans multiple architectural boundaries scope/ffi Affects native or .NET bindings scope/web Affects the web/WASM ecosystem size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure

Development

Successfully merging this pull request may close these issues.

4 participants