Repository navigation
feat(server)!: support the Large Pointer Capability Set - #1787
Merged
Marc-André Moreau (mamoreau-devolutions) merged 1 commit intoAug 28, 2026
Conversation
Greg Lamberson (glamberson)
temporarily deployed
to
llm-providers
August 23, 2026 22:59 — with
GitHub Actions
Inactive
Greg Lamberson (glamberson)
force-pushed
the
feat/large-pointer-capability-set
branch
from
August 26, 2026 22:10
4dd2c66 to
4b3174d
Compare
Greg Lamberson (glamberson)
temporarily deployed
to
llm-providers
August 26, 2026 22:10 — with
GitHub Actions
Inactive
Greg Lamberson (glamberson)
force-pushed
the
feat/large-pointer-capability-set
branch
from
August 27, 2026 22:27
4b3174d to
b234e2d
Compare
Greg Lamberson (glamberson)
temporarily deployed
to
llm-providers
August 27, 2026 22:28 — with
GitHub Actions
Inactive
Without this, ironrdp-server can never send a pointer shape above 32x32 (or 96x96 with the base Pointer Capability Set alone): The Large Pointer Capability Set (MS-RDPBCGR 2.2.7.2.7) is never advertised by the server and never read from the client, even though the wire layer already has everything needed to use it. ironrdp-pdu already defines LargePointer / LargePointerSupportFlags (the capability set) and LargePointerAttribute (TS_FP_LARGEPOINTERATTRIBUTE, the Fast-Path Large Pointer Update PDU itself, up to 384x384), and the client side already decodes and renders it (ironrdp-session's fast_path dispatch, ironrdp-graphics's pointer decode, and a fuzz oracle all already exercise UpdateCode::LargePointer). Only the server side was missing. Advertises the capability from ironrdp-server's own configured max_request_size rather than unconditionally claiming full 384x384 support: MS-RDPBCGR 2.2.7.2.7 requires the MaxRequestSize field of the Multifragment Update Capability Set to be at least 38,055 bytes for LARGE_POINTER_FLAG_96x96 and at least 608,299 bytes for LARGE_POINTER_FLAG_384x384 (enough to fit a 96x96 or 384x384 32bpp pointer respectively), so what gets advertised is derived from the same max_request_size the Multifragment Update Capability Set already uses, not asserted independently of it. The default max_request_size (8 MiB) clears both thresholds, so this is enabled by default for servers that haven't lowered it. Reading the client's negotiated flags surfaces a distinction the spec draws explicitly and that's easy to miss: LARGE_POINTER_FLAG_96x96 only raises the size ceiling of the existing Color/New Pointer Update from 32x32 to 96x96 (New Pointer Update carries the same ColorPointerAttribute internally as Color Pointer Update, so the same width/height field docs in MS-RDPBCGR 2.2.9.1.1.4.4 apply to both); it does not by itself enable the separate Large Pointer Update PDU. Only LARGE_POINTER_FLAG_384x384 does that. UpdateEncoder now enforces both halves of this: RGBAPointer is dropped (debug log, not an error) if its shape exceeds the negotiated 32x32/96x96 ceiling, and the new DisplayUpdate::LargePointer is dropped if the client never advertised LARGE_POINTER_FLAG_384x384 or if the shape exceeds the protocol's absolute 384x384 maximum regardless of what was advertised. DisplayUpdate::LargePointer mirrors RGBAPointer's shape exactly (32bpp XOR mask only, no AND mask, same field names) since that's the only color depth this crate's pointer path produces. ColorPointer's own 32x32/96x96 ceiling, governed by the same LargePointerSupportFlags as RGBAPointer's, is left unenforced for the same reason the prior PR left ColorPointer's availability gate (colorPointerCacheSize, a separate concern from the size ceiling) ungated: Nothing in this crate constructs a ColorPointer update. Stacked on Devolutions#1786 (both touch the same client_accepted() capabilities loop and UpdateEncoder construction); Devolutions#1786 must land first.
Greg Lamberson (glamberson)
force-pushed
the
feat/large-pointer-capability-set
branch
from
August 28, 2026 01:34
b234e2d to
219c5c9
Compare
Greg Lamberson (glamberson)
temporarily deployed
to
llm-providers
August 28, 2026 01:35 — with
GitHub Actions
Inactive
Marc-André Moreau (mamoreau-devolutions)
approved these changes
Aug 28, 2026
Marc-André Moreau (mamoreau-devolutions)
merged commit Aug 28, 2026
bde5824
into
Devolutions:master
43 checks passed
Anton Mostovoy (antonmos)
added a commit
to antonmos/IronRDP
that referenced
this pull request
Aug 30, 2026
…arge pointer capset) Three upstream commits since the last sync, all in client_accepted's capability loop or client_loop's body -- code this branch has never touched (the preemption/negotiation work stays entirely within PendingConnection/negotiate_and_authenticate and the accept-loop race, upstream of accept_finalize where client_accepted/client_loop live): Devolutions#1787! (Large Pointer Capability Set) -- additive to the capabilities match in client_accepted and UpdateEncoder::new's constructor, neither touched here. Devolutions#1834 (EGFX flow-control + dispatch timing diagnostics) -- logging only inside client_loop's existing futures, no signature changes. Devolutions#1842 (periodic Heartbeat PDUs) -- adds a field + a config setter to RdpServer and a 5th joined future inside client_loop; client_loop's own signature gained a parameter, but its only two call sites (both inside client_accepted) are upstream's own code, already updated by the merge. One trivial conflict: both branches touched the same two import lines at the top of the file (this branch added IpAddr for the anti-storm cooldown's EvictedPeer.ip; upstream added AtomicU64 for the heartbeat write-counter) -- kept both. Note from Devolutions#1834's own commit message, checked rather than trusted: it warns the workspace-wide test build was already broken on master independent of that PR (an ironrdp-daemon call-site mismatch). Did not reproduce here -- full cargo build --workspace succeeded clean, so either it was already fixed by a later commit or doesn't affect a non-test build; not chased further since it doesn't block this merge. 14/14 tests pass in both default and --features egfx (test count unchanged -- none of the three commits touch a test module), clippy --all-targets clean in both configs AND under CI's exact stricter command (--features helper,__bench --locked -- -D warnings), fmt clean, full cargo build --workspace green.
Greg Lamberson (glamberson)
deleted the
feat/large-pointer-capability-set
branch
August 30, 2026 15:12
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #1786
Stacked on top of #1786's branch: Both touch
client_accepted()'s capabilities loopand
UpdateEncoder::new()'s constructor signature. #1786 must land first.Summary
ironrdp-servernever advertises or reads the Large Pointer Capability Set(MS-RDPBCGR 2.2.7.2.7), even though
ironrdp-pdualready defines everything needed(
LargePointer/LargePointerSupportFlags,LargePointerAttribute) and the clientside already decodes and renders
UpdateCode::LargePointer. The server side was theonly gap: A pointer shape above 32x32 (or 96x96 with just the base Pointer
Capability Set) could never be sent.
ironrdp-server's own configuredmax_request_size, not asserted independently: MS-RDPBCGR 2.2.7.2.7 requires theMultifragment Update Capability Set's
MaxRequestSizeto be at least 38,055 bytesfor
LARGE_POINTER_FLAG_96x96and at least 608,299 bytes forLARGE_POINTER_FLAG_384x384. The defaultmax_request_size(8 MiB) clears both, sothis is enabled by default unless a server has lowered it.
explicitly:
LARGE_POINTER_FLAG_96x96only raises the existing Color/New PointerUpdate's size ceiling from 32x32 to 96x96 (New Pointer Update wraps the same
ColorPointerAttributeas Color Pointer Update, so MS-RDPBCGR 2.2.9.1.1.4.4'swidth/height field docs apply to both); it does not enable the separate Large
Pointer Update PDU. Only
LARGE_POINTER_FLAG_384x384does that.UpdateEncodernow enforces both halves:RGBAPointeris dropped (debug log) if itexceeds the negotiated 32x32/96x96 ceiling, and the new
DisplayUpdate::LargePointeris dropped if the client never advertisedLARGE_POINTER_FLAG_384x384, or if the shape exceeds the protocol's absolute384x384 maximum regardless of what was advertised.
DisplayUpdate::LargePointermirrorsRGBAPointer's shape exactly (32bpp XOR maskonly, no AND mask) since that's the only color depth this crate's pointer path
produces.
ColorPointer's own ceiling (sameLargePointerSupportFlags) is leftunenforced for the same reason the prior PR left its separate availability gate
(
colorPointerCacheSize) ungated: Nothing in this crate constructs aColorPointerupdate.
Breaking change
DisplayUpdategains a new variant (LargePointer), andUpdateEncoder's (crateinternal,
#[cfg(feature = "__bench")]-exposed for benchmarks) constructor gains a6th parameter. No exhaustive match over
DisplayUpdateexists elsewhere in theworkspace today, so nothing else needed updating.
Validation
cargo xtask check fmt/typos/locks/lints/testsall pass.