Skip to content

GUACAMOLE-2123: Fix remote app render issues. Use per-window guac_dis…#673

Open
jjheinon wants to merge 1 commit into
apache:staging/1.6.1from
jjheinon:guacamole-2123-remote-app-windows-gfx
Open

GUACAMOLE-2123: Fix remote app render issues. Use per-window guac_dis…#673
jjheinon wants to merge 1 commit into
apache:staging/1.6.1from
jjheinon:guacamole-2123-remote-app-windows-gfx

Conversation

@jjheinon

Copy link
Copy Markdown

Fixing remote app render issues with this patch, using per-window guac_display_layer for remote apps. Also introducing a fix for stale desktop background on RDPGFX.

Using per-window guac_display_layer for remote apps and also prevent dirty-region propagation to desktop background. Added support for UpdateSurfaceArea (FreeRDP 2.x) and UpdateWindowFromSurface (FreeRDP 3.x)

When Guacamole's RDP plugin is configured with both RemoteApp and the RDPGFX pipeline enabled, remote application windows fail to render.
With GFX active, FreeRDP does not update RAIL window contents via BeginPaint/Endpaint, but via RDPGFX surfaces using windowId and callbacks.
For FreeRDP 3.x: UpdateWindowFromSurface is called per window-mapped surface with its accumulated invalid region.
For FreeRDP 2.x: UpdateSurfaceArea is called per surface with a rect list.

This patch has a dedicated guac_display_layer for each remote app and derives visibility via showState. Surface content painted with RDPGFX callbacks and scaled to correct size.
Layer is kept hidden until both window position is known and at least one surface paint request has arrived, avoiding flashes and race conditions.

Tested remote apps with FreeRDP 3.26.0 with the latest staging/1.6.1 branch, also built with 2.11.7.

@stephzero1

Copy link
Copy Markdown

great work

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jjheinon, this is great - I had started to work on this some time back but hit a couple of walls that I couldn't get past, and it looks like you've figured out the issues I couldn't!

The code looks pretty good to me, though I need to take time to more thoroughly review it - most of the things that need to be changed so far deal with style, particularly the comment blocks of the functions you've implemented.

Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.h
Comment thread src/protocols/rdp/channels/rail.h
Comment thread src/protocols/rdp/channels/rail.h
Comment thread src/protocols/rdp/gdi.c Outdated

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjheinon:
A few more changes and a couple of questions. Also, one of your commits lacks the required GUACAMOLE-2123: tag.

Overall looking pretty good and just about ready for merge.

Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.c
@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch 3 times, most recently from b062650 to 735cd68 Compare May 16, 2026 13:02
Comment thread src/protocols/rdp/rdp.c Outdated
Comment thread src/protocols/rdp/gdi.c
Comment thread src/protocols/rdp/gdi.c
Comment thread src/protocols/rdp/gdi.c

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good to me. Just one other change for an explicit type-cast at the moment.

Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c
* finish, leaving subsequent input ignored after dragging a RemoteApp
* window. */
RAIL_CLIENT_STATUS_ORDER client_status = {
.flags =

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot use TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE, or we might lose mouse input. We obey positioning info from server instead, which fixes the issue.

* @return
* CHANNEL_RC_OK (zero) as the order has been ignored successfully.
*/
static UINT guac_rdp_rail_local_move_size(RailClientContext* rail,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case server sends the request, we answer with CHANNEL_RC_OK.

Comment thread src/protocols/rdp/channels/rail.c
Comment thread src/protocols/rdp/channels/rail.c Outdated

/* RDPGFX window surfaces are copied as opaque layer content. We don't
* want holes on our RemoteApp windows. */
UINT32 dst_format = guac_rdp_get_native_pixel_format(FALSE);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GFX windows might have alpha channel, we don't want that when copying the layers to Guacamole side. (managed to get holes in paint app, where there should have been black instead). This fixes the issue.

@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch from 061ba6f to b8aa4f4 Compare May 17, 2026 08:55
@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch from b8aa4f4 to cdd4ade Compare June 15, 2026 07:43

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more change, and I think it'll be ready to merge.

Comment thread src/protocols/rdp/channels/rail.c
guac_display_layer_set_opacity(rail_window->layer, opacity);
}

static void guac_rdp_rail_paint_background(guac_rdp_client* rdp_client) {

@jjheinon jjheinon Jun 16, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the blackground painting here, as earlier we rendered the black background for RAIL apps too soon before the login was done. This caused screen to remain black if login failed.

Comment thread src/protocols/rdp/channels/rail.c Outdated
guac_client* client = (guac_client*) context->custom;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

if (execResult->execResult == RAIL_EXEC_E_NOT_IN_ALLOWLIST

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If server tells us RAIL_EXEC_E_NOT_IN_ALLOWLIST after rendering is already working and windows being drawn, just ignore it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we just ignore it? If the application is not in the allow list, the server shouldn't actually provide the application, and we shouldn't get anything, correct?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an interesting edge case. Apparently it's possible for Windows RemoteApp to launch another process, which does not belong to allowed app listing for the RemoteApp session.
In this case, the requested RAIL app launches properly (and renders the app window), but server sends RAIL_EXEC_E_NOT_IN_ALLOWLIST when it prevents launching the other app process.

Not sure what should be the optimal behavior in this case, but removed the modification and reverted back to the original behavior (connection fails), but added log message about it so the Windows admin can make the requested changes on their environment.

Comment thread src/protocols/rdp/channels/rdpgfx.c Outdated
* disabled.
*/

#if defined(HAVE_RDPGFX_SURFACE_AREA_UPDATE) \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed a bit, we kept on using UpdateSurfaceArea even when newer UpdateWindowFromSurface was available. Now it works better.

Comment thread src/protocols/rdp/gdi.c
* into its own primary buffer.
*/
if (rdp_client->settings->remote_app == NULL
|| !rdp_client->settings->enable_gfx

@jjheinon jjheinon Jun 16, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use primary buffer only if RAIL is disabled, GFX is disabled or both are enabled, but we're not yet received first paint event. This fixes the issue where invalid credentials ends up to Windows login screen on non-NLA mode, but login screen is not visible. Earlier we rendered black screen in that case.


guac_display_layer_close_raw(rail_window->layer, layer_context);
if (!copied) {
guac_common_list_unlock(rdp_client->rail_windows);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not render background before window rendering has started and we have received app data.

Comment thread src/protocols/rdp/channels/rail.c Outdated
guac_common_list_unlock(rdp_client->rail_windows);
return CHANNEL_RC_OK;
}
guac_rdp_rail_paint_background(rdp_client);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Render only after data has been received. This prevents us blocking Windows login dialog on non-NLA logins, if credentials are incorrect.

Comment thread src/protocols/rdp/channels/rail.c
rail_window->has_rail_order = 1;
int needs_raise = 0;

if (fieldFlags & WINDOW_ORDER_FIELD_OWNER) {

@jjheinon jjheinon Jun 17, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A window order includes the owner field. Make sure it's shown above the owner window.

Comment thread src/protocols/rdp/channels/rail.c Outdated
for (guac_common_list_element* current = rdp_client->rail_windows->head;
current != NULL; current = current->next) {
guac_rdp_rail_window* rail_window = current->data;
rail_window->in_monitored_desktop_zorder = 0;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We got the official z-index for this window from the server, so we don't need to handle it separately anymore.

@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch from 10d806b to 50b8f58 Compare June 17, 2026 09:11

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is looking really good - a handful of changes, mostly to comments, but overall I'm liking it.

@jjheinon Are you good with it? I see a lot of comments and pushes coming in - do we need to convert it to a draft at this point, or are you good with it?

Comment thread src/protocols/rdp/channels/rail.h Outdated
Comment on lines +106 to +107
* @return
* CHANNEL_RC_OK if the surface mapping was handled successfully.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...or what, if the mapping was not handled correctly?

Comment thread src/protocols/rdp/channels/rail.h Outdated
Comment on lines +122 to +123
* @return
* CHANNEL_RC_OK if the surface unmapping was handled successfully.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...or what, if the unmapping was not handled successfully?

Comment thread src/protocols/rdp/channels/rail.h Outdated
Comment on lines +148 to +149
* @return
* CHANNEL_RC_OK if the surface was painted successfully, or an error code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What type of error code? FreeRDP? Guacamole? Generic negative/positive integer?

Comment thread src/protocols/rdp/channels/rail.h Outdated
Comment on lines +167 to +168
* @return
* CHANNEL_RC_OK if the surface was painted successfully, or an error code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What type of error code?

Comment thread src/protocols/rdp/channels/rdpgfx.c Outdated
guac_client_log(client, GUAC_LOG_WARNING, "Rendering backend for RDPGFX "
"channel could not be loaded. Graphics may not render at all!");
else
} else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't cuddle braces.

Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment thread src/protocols/rdp/channels/rail.c Outdated
current != NULL; current = current->next) {
guac_rdp_rail_window* rail_window = current->data;

if (rail_window->z_order >= z)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And, here...check to make sure rail_window is not null.

Comment thread src/protocols/rdp/channels/rail.c Outdated
while (current != NULL) {
guac_rdp_rail_window* rail_window = current->data;

if (rail_window->owner_window_id == owner_window_id) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And, here...

Comment thread src/protocols/rdp/channels/rail.c Outdated
guac_client* client = (guac_client*) context->custom;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

if (execResult->execResult == RAIL_EXEC_E_NOT_IN_ALLOWLIST

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we just ignore it? If the application is not in the allow list, the server shouldn't actually provide the application, and we shouldn't get anything, correct?

Comment thread src/protocols/rdp/channels/rail.c Outdated
Comment on lines +1131 to +1132
* @return
* CHANNEL_RC_OK if the surface was painted successfully, or an error code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of error code?

@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch 4 times, most recently from 7aa7b43 to a308e7a Compare June 19, 2026 11:54
…ng by tracking RAIL windows as independent Guacamole layers. Synchronize RDPGFX surface updates with RAIL window metadata. Track surface-to-window mappings, window ownership, visibility, geometry, activation, and z-order to render RemoteApp windows independently of the hidden desktop. Add support for both FreeRDP 2.x UpdateSurfaceArea and FreeRDP 3.x UpdateWindowFromSurface, including HiDPI scaling. Improved input responsiveness. Preserve the hidden desktop framebuffer and black background during RemoteApp sessions.
@jjheinon jjheinon force-pushed the guacamole-2123-remote-app-windows-gfx branch from a308e7a to 534b89a Compare June 19, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants