diff --git a/patches/004-h264-passthrough.patch b/patches/004-h264-passthrough.patch index aa10297..94a579c 100644 --- a/patches/004-h264-passthrough.patch +++ b/patches/004-h264-passthrough.patch @@ -992,7 +992,7 @@ index 327e7c2b..c49737d7 100644 /** * Callback which associates handlers specific to Guacamole with the * RdpgfxClientContext instance allocated by FreeRDP to deal with received -@@ -63,13 +438,45 @@ static void guac_rdp_rdpgfx_channel_connected(rdpContext* context, +@@ -63,13 +438,68 @@ static void guac_rdp_rdpgfx_channel_connected(rdpContext* context, RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface; rdpGdi* gdi = context->gdi; @@ -1009,27 +1009,50 @@ index 327e7c2b..c49737d7 100644 + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Installed only for a connection that asked for H.264, and only -+ * once. This handler runs again whenever the RDPGFX channel -+ * reconnects -- the display resize xrdp performs at login triggers -+ * exactly that -- and a second install reads back the wrapper the -+ * first one left behind, so orig_surface_command ends up pointing at -+ * guac_rdp_gfx_surface_command itself. Every surface command that is -+ * not H.264 then calls the wrapper from inside the wrapper and -+ * recurses until the stack is gone. H.264 commands take the ++ * onto a handler that is not already ours. This runs again every ++ * time the RDPGFX channel reconnects -- the display resize xrdp ++ * performs at login triggers exactly that -- and the two ways of ++ * getting it wrong fail in opposite directions. ++ * ++ * Reinstalling unconditionally is fatal: the second pass reads back ++ * the wrapper the first one left, orig_surface_command ends up ++ * pointing at guac_rdp_gfx_surface_command itself, and every surface ++ * command that is not H.264 calls the wrapper from inside the ++ * wrapper until the stack is gone. H.264 commands take the + * skip_decode path and never make that call, so a server sending -+ * nothing but H.264 survives it, while one mixing in planar or -+ * progressive surfaces dies on the first of them. */ -+ if (rdp_client->settings->enable_h264 -+ && rdp_client->orig_surface_command == NULL) { ++ * nothing but H.264 survives it while one mixing in planar or ++ * progressive surfaces dies on the first of them. ++ * ++ * Installing only once is quieter and just as broken. ++ * gdi_graphics_pipeline_uninit() sets SurfaceCommand to NULL and ++ * gdi_graphics_pipeline_init() puts gdi_SurfaceCommand back, so a ++ * reconnect leaves FreeRDP's own handler installed while a ++ * once-only guard declines to wrap it again. Passthrough then stops ++ * for the rest of the session -- silently, since the frames still ++ * render via GDI and guacd simply re-encodes them. ++ * ++ * Testing what is actually installed, per callback, covers both. */ ++ if (rdp_client->settings->enable_h264) { + + /* Wrap SurfaceCommand to intercept H.264 data after GDI decode. + * Store the original callback per-connection to avoid clobbering + * when multiple RDP sessions are active concurrently. */ -+ rdp_client->orig_surface_command = (void*) rdpgfx->SurfaceCommand; -+ rdpgfx->SurfaceCommand = guac_rdp_gfx_surface_command; ++ if (rdpgfx->SurfaceCommand != guac_rdp_gfx_surface_command) { ++ rdp_client->orig_surface_command = (void*) rdpgfx->SurfaceCommand; ++ rdpgfx->SurfaceCommand = guac_rdp_gfx_surface_command; ++ } + -+ rdp_client->orig_caps_confirm = (void*) rdpgfx->CapsConfirm; -+ rdpgfx->CapsConfirm = guac_rdp_gfx_caps_confirm; ++ /* Guarded separately, because the two callbacks are restored on ++ * different schedules: gdi_graphics_pipeline_init() assigns ++ * SurfaceCommand but never touches CapsConfirm, so after a ++ * reconnect CapsConfirm is still ours while SurfaceCommand is ++ * FreeRDP's again. One shared guard therefore re-reads a wrapper ++ * we installed ourselves and points orig_caps_confirm at ++ * guac_rdp_gfx_caps_confirm. */ ++ if (rdpgfx->CapsConfirm != guac_rdp_gfx_caps_confirm) { ++ rdp_client->orig_caps_confirm = (void*) rdpgfx->CapsConfirm; ++ rdpgfx->CapsConfirm = guac_rdp_gfx_caps_confirm; ++ } + + guac_client_log(client, GUAC_LOG_INFO, + "H.264 passthrough enabled for RDPGFX channel.");