Skip to content

feat(egfx): handle mid-session CapsAdvertise as decoder-recovery - #1833

Open
Greg Lamberson (glamberson) wants to merge 1 commit into
Devolutions:masterfrom
lamco-admin:feat/egfx-midsession-capsadvertise-recovery
Open

feat(egfx): handle mid-session CapsAdvertise as decoder-recovery#1833
Greg Lamberson (glamberson) wants to merge 1 commit into
Devolutions:masterfrom
lamco-admin:feat/egfx-midsession-capsadvertise-recovery

Conversation

@glamberson

Copy link
Copy Markdown
Contributor

Real-world clients (mstsc on Windows 11 under load, macOS Microsoft Remote Desktop) re-emit RDPGFX_CAPSADVERTISE mid-session as a decoder-recovery sequence when their decoder loses sync. The previous handler treated every CapsAdvertise as initial setup, leaving reset_graphics_sent=true so the application's follow-up create_surface wouldn't auto-emit ResetGraphics, breaking recovery.

I detect the re-advertise via state == Ready at entry, then silently clear surfaces + frames and re-arm reset_graphics_sent. I don't emit a DeleteSurface PDU for this: the client has already cleared its surface state on its end, and treats a stray DeleteSurface as a protocol violation, closing the connection within milliseconds.

Surfaces gains reset_for_reinit(), distinct from clear(): it also resets next_surface_id to 0 so the client's subsequent CreateSurface yields ID 0, matching its fresh-state expectation.

MS-RDPEGFX does not document this recovery flow explicitly, but the empirical pattern is CapsAdvertise -> CacheImportOffer -> the server is expected to emit CapsConfirm + ResetGraphics + CreateSurface(id=0) + MapSurfaceToOutput + IDR.

I validated this against mstsc on Windows 11 and openSUSE Tumbleweed (KDE Plasma 6.6.4).

Real-world clients (mstsc on Windows 11 under load, macOS Microsoft
Remote Desktop) re-emit RDPGFX_CAPSADVERTISE mid-session as a
decoder-recovery sequence when their decoder loses sync. The previous
handler treated every CapsAdvertise as initial setup, leaving
reset_graphics_sent=true so the application's follow-up create_surface
wouldn't auto-emit ResetGraphics, breaking recovery.

Detect re-advertise via state == Ready at entry, silently clear
surfaces + frames + re-arm reset_graphics_sent. No DeleteSurface PDU
is emitted: the client has already cleared its surface state on its
end and treats a stray DeleteSurface as a protocol violation, closing
the connection within milliseconds.

Surfaces gains reset_for_reinit(), distinct from clear(): it also
resets next_surface_id to 0 so the client's subsequent CreateSurface
yields ID 0, matching its fresh-state expectation.

Validated against mstsc on Windows 11 and openSUSE Tumbleweed (KDE
Plasma 6.6.4).
@github-actions github-actions Bot added maintainer-required Maintainer review or intervention is required risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny scope/core Touches the core architectural tier size/XS Size: up to 49 counted lines and 2 files labels Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer-required Maintainer review or intervention is required risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny scope/core Touches the core architectural tier size/XS Size: up to 49 counted lines and 2 files

Development

Successfully merging this pull request may close these issues.

1 participant