patches: add 005-rdp-resize-dirty-flush (RDP resize black regions)

Addresses #118. After a dynamic RDP resize, guac_rdp_gdi_desktop_resize()
resized the GDI buffer and display layer but never marked the layer dirty,
so close_raw() flushed nothing and newly-exposed/stale regions rendered as
solid black until something else repainted them.

The patch marks the whole layer dirty and issues a RefreshRect for the new
desktop area after the resize. Root cause diagnosed and fix supplied by
@Bails309 on the issue; reworked here without the downstream debug logging
and scoped to the resize function only (the end-paint handler shares the
same close_raw call and must not be touched).

Fixes the legacy bitmap path (rustguac's default, enable_gfx=false). The
RDPGFX surface cache ignores RefreshRect so GFX sessions aren't addressed,
but they have not reproduced the artifact in practice.

Verified: applies cleanly via git apply against the pinned guacd 2980cf0;
full guacd build with all five patches compiles and links the RDP plugin
with no errors (only pre-existing FreeRDP deprecation warnings).
This commit is contained in:
Dave Kempe
2026-05-28 07:23:01 +10:00
parent 89129a54b5
commit a9bc21245b
2 changed files with 57 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
--- a/src/protocols/rdp/gdi.c
+++ b/src/protocols/rdp/gdi.c
@@ -188,6 +188,28 @@
guac_client_log(client, GUAC_LOG_DEBUG, "Server resized display to %ix%i",
gdi->width, gdi->height);
+ /* Mark the entire resized layer dirty so guac_display_layer_close_raw()
+ * flushes a full repaint to the client. Without this the dirty rect is
+ * empty after a resize, so newly-exposed (or stale) regions of the layer
+ * render as solid black until something else happens to repaint them.
+ * See sol1/rustguac#118. */
+ guac_rect_init(&current_context->dirty, 0, 0, gdi->width, gdi->height);
+
+ /* Ask the server to re-send pixels for the full new desktop area. This
+ * covers the legacy bitmap update path. The RDPGFX surface cache ignores
+ * RefreshRect, so GFX sessions are not fixed by this and need a separate
+ * approach. */
+ if (context->update != NULL && context->update->RefreshRect != NULL
+ && gdi->width > 0 && gdi->height > 0
+ && gdi->width <= UINT16_MAX && gdi->height <= UINT16_MAX) {
+ RECTANGLE_16 area;
+ area.left = 0;
+ area.top = 0;
+ area.right = (UINT16) gdi->width;
+ area.bottom = (UINT16) gdi->height;
+ context->update->RefreshRect(context, 1, &area);
+ }
+
guac_display_layer_close_raw(default_layer, current_context);
return retval;
+26
View File
@@ -90,6 +90,32 @@ This approach avoids the socket contention issue that occurred when H.264 was se
**Requires:** RDP server with H.264 support (xrdp with x264, or Windows with AVC hardware encoder). Browser must support WebCodecs VideoDecoder (Chrome/Edge 94+, Firefox 130+).
## 005-rdp-resize-dirty-flush.patch
**Problem:** After a dynamic RDP display resize (browser window resized,
`resize-method=display-update`), regions of the desktop render as solid black
until something repaints them. `guac_rdp_gdi_desktop_resize()` resizes the
FreeRDP GDI buffer and the guac display layer but never marks the layer dirty,
so `guac_display_layer_close_raw()` flushes nothing and the client keeps its
stale/blank canvas for the resized layer. See [sol1/rustguac#118](https://github.com/sol1/rustguac/issues/118) (reported by @Bails309, who diagnosed the root cause and supplied the fix).
**Fix:** In `guac_rdp_gdi_desktop_resize()`, after the layer resize and before
`guac_display_layer_close_raw()`:
1. Mark the entire layer dirty (`guac_rect_init(&current_context->dirty, ...)`) so a full repaint is flushed to the client.
2. Issue a `RefreshRect` for the full new desktop so the server re-sends authoritative pixels (legacy bitmap update path).
**Scope:** Fixes the legacy bitmap update path, which is rustguac's default
(`enable_gfx` defaults to false). The RDPGFX surface cache ignores
`RefreshRect`, so GFX sessions are not addressed by this patch; in practice
GFX sessions have not reproduced the artifact.
**Files patched:**
| File | Fix |
|------|-----|
| `src/protocols/rdp/gdi.c` | Mark layer dirty + `RefreshRect` after resize in `guac_rdp_gdi_desktop_resize()` |
## Applying patches
Patches are applied automatically by all build scripts (`build-deb.sh`, `build-rpm.sh`, `install.sh`, `dev.sh`, `Dockerfile`). To apply manually: