Commit 696fd8fb for guacamole.apache.org

commit 696fd8fb29d03fdf1fec3482b48aeaddd2ecfffd
Author: Bradley Bennett <bbennett@keepersecurity.com>
Date:   Wed Apr 29 16:45:39 2026 -0400

    GUACAMOLE-2271: VNC: implement FinishedFrameBufferUpdate callback to optimize frame flushing.

diff --git a/src/libguac/display-render-thread.c b/src/libguac/display-render-thread.c
index 121c3d23..345ffb44 100644
--- a/src/libguac/display-render-thread.c
+++ b/src/libguac/display-render-thread.c
@@ -48,8 +48,10 @@
 /**
  * The start routine for the display render thread, consisting of a single
  * render loop. The render loop will proceed until signalled to stop,
- * determining frame boundaries via a combination of heuristics and explicit
- * marking (if available).
+ * determining frame boundaries via explicit marking when available (e.g. VNC's
+ * FinishedFrameBufferUpdate, RDP's frame markers), falling back to heuristics
+ * based on the timing of display modifications when the protocol handler
+ * provides no explicit frame boundaries.
  *
  * @param data
  *     The guac_display_render_thread structure containing the render thread
diff --git a/src/protocols/vnc/display.c b/src/protocols/vnc/display.c
index ecb0a775..e02c0d33 100644
--- a/src/protocols/vnc/display.c
+++ b/src/protocols/vnc/display.c
@@ -307,6 +307,22 @@ void guac_vnc_display_set_size(rfbClient* client, int requested_width, int reque
 }
 #endif // LIBVNC_HAS_RESIZE_SUPPORT

+void guac_vnc_finished_frame(rfbClient* client) {
+
+    guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY);
+    guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data;
+
+    if (!vnc_client->finished_frame_logged) {
+        guac_client_log(gc, GUAC_LOG_DEBUG,
+                "Received first FinishedFrameBufferUpdate callback from VNC server.");
+        vnc_client->finished_frame_logged = 1;
+    }
+
+    /* All rectangles in this FramebufferUpdate have been processed. */
+    guac_display_render_thread_notify_frame(vnc_client->render_thread);
+
+}
+
 void guac_vnc_set_pixel_format(rfbClient* client, int color_depth) {
     client->format.trueColour = 1;
     switch(color_depth) {
diff --git a/src/protocols/vnc/display.h b/src/protocols/vnc/display.h
index 4074f3ee..6446578d 100644
--- a/src/protocols/vnc/display.h
+++ b/src/protocols/vnc/display.h
@@ -116,6 +116,17 @@ void* guac_vnc_display_set_owner_size(guac_user* owner, void* data);
  */
 void guac_vnc_display_set_size(rfbClient* client, int requested_width, int requested_height);

+/**
+ * Callback invoked by libVNCServer when all rectangles within a single
+ * FramebufferUpdate message have been fully processed. Signals the render
+ * thread that an explicit frame boundary has been reached.
+ *
+ * @param client
+ *     The VNC client associated with the VNC session in which the update
+ *     was completed.
+ */
+void guac_vnc_finished_frame(rfbClient* client);
+
 /**
  * Sets the pixel format to request of the VNC server. The request will be made
  * during the connection handshake with the VNC server using the values
diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c
index 630ce986..815ef4dc 100644
--- a/src/protocols/vnc/vnc.c
+++ b/src/protocols/vnc/vnc.c
@@ -139,6 +139,8 @@ rfbClient* guac_vnc_get_client(guac_client* client) {

     /* Framebuffer update handler */
     rfb_client->GotFrameBufferUpdate = guac_vnc_update;
+    /* Framebuffer finished frame handler */
+    rfb_client->FinishedFrameBufferUpdate = guac_vnc_finished_frame;
     vnc_client->rfb_GotCopyRect = rfb_client->GotCopyRect;
     rfb_client->GotCopyRect = guac_vnc_copyrect;

diff --git a/src/protocols/vnc/vnc.h b/src/protocols/vnc/vnc.h
index a51b7b50..3a348ecc 100644
--- a/src/protocols/vnc/vnc.h
+++ b/src/protocols/vnc/vnc.h
@@ -95,6 +95,11 @@ typedef struct guac_vnc_client {
      */
     int copy_rect_used;

+    /**
+     * Whether the first FinishedFrameBufferUpdate callback has been logged.
+     */
+    int finished_frame_logged;
+
     /**
      * Client settings, parsed from args.
      */
@@ -212,4 +217,3 @@ void* guac_vnc_client_thread(void* data);
 extern char* GUAC_VNC_CLIENT_KEY;

 #endif
-