From 9fef5515e58667c6a09340dd2eb5af09a6de6e1a Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 6 Sep 2024 16:54:26 +0000 Subject: [PATCH 1/3] GUACAMOLE-1196: Resize guacamole display only when VNC screen size is changed by the server. --- src/protocols/vnc/display.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/protocols/vnc/display.c b/src/protocols/vnc/display.c index 9b029d781..ffb0b4346 100644 --- a/src/protocols/vnc/display.c +++ b/src/protocols/vnc/display.c @@ -51,6 +51,19 @@ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; + /* Resize the surface if VNC screen size has changed */ + int old_height = vnc_client->display->default_surface->height; + int old_width = vnc_client->display->default_surface->width; + int new_height = rfbClientSwap16IfLE(client->screen.height); + int new_width = rfbClientSwap16IfLE(client->screen.width); + if ( + new_height > 0 && new_width > 0 + && (new_height != old_height || new_width != old_width) + ) { + guac_common_surface_resize(vnc_client->display->default_surface, + new_width, new_height); + } + int dx, dy; /* Cairo image buffer */ @@ -309,15 +322,9 @@ void guac_vnc_display_set_size(rfbClient* client, int width, int height) { /* Send the display size update. */ guac_client_log(gc, GUAC_LOG_TRACE, "Setting VNC display size."); - if (guac_vnc_send_desktop_size(client, width, height)) { + if (guac_vnc_send_desktop_size(client, width, height)) guac_client_log(gc, GUAC_LOG_TRACE, "Successfully sent desktop size message."); - /* Resize the surface now that the VNC size update has completed */ - if (vnc_client->display != NULL) - guac_common_surface_resize(vnc_client->display->default_surface, - width, height); - } - else guac_client_log(gc, GUAC_LOG_TRACE, "Failed to send desktop size message."); From 4b36ea354c2388e63dbbefe7842060653211cc2d Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 6 Sep 2024 23:37:51 +0000 Subject: [PATCH 2/3] GUACAMOLE-1196: Fix compile issues with older VNC client versions where screen isn't defined. --- src/protocols/vnc/display.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/protocols/vnc/display.c b/src/protocols/vnc/display.c index ffb0b4346..7ac91b0b9 100644 --- a/src/protocols/vnc/display.c +++ b/src/protocols/vnc/display.c @@ -51,11 +51,17 @@ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; +#ifdef LIBVNC_CLIENT_HAS_SCREEN + int new_height = rfbClientSwap16IfLE(client->screen.height); + int new_width = rfbClientSwap16IfLE(client->screen.width); +#else + int new_height = rfbClientSwap16IfLE(client->height); + int new_width = rfbClientSwap16IfLE(client->width); +#endif + /* Resize the surface if VNC screen size has changed */ int old_height = vnc_client->display->default_surface->height; int old_width = vnc_client->display->default_surface->width; - int new_height = rfbClientSwap16IfLE(client->screen.height); - int new_width = rfbClientSwap16IfLE(client->screen.width); if ( new_height > 0 && new_width > 0 && (new_height != old_height || new_width != old_width) From dfc7d035aa9eb4451b6231017241ecd63a0de6d9 Mon Sep 17 00:00:00 2001 From: Alex Leitner Date: Fri, 6 Sep 2024 12:55:01 -0400 Subject: [PATCH 3/3] GUACAMOLE-1026: Use LoadChannels callback method to load plugins with FreeRDP3. --- configure.ac | 24 ++++++++++ src/protocols/rdp/rdp.c | 103 +++++++++++++++++++++++++++++++--------- 2 files changed, 104 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index 18d34dcad..fb7612f34 100644 --- a/configure.ac +++ b/configure.ac @@ -1141,6 +1141,30 @@ then [AC_MSG_RESULT([no])]) fi +if test "x${have_freerdp}" = "xyes" +then + AC_MSG_CHECKING([whether freerdp instance supports LoadChannels]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + + /* Mock LoadChannels function with the expected signature */ + BOOL load_channels(freerdp* instance) { + return TRUE; + } + + int main() { + freerdp* instance = freerdp_new(); + instance->LoadChannels = load_channels; + freerdp_free(instance); + return 0; + } + ]])], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([RDP_INST_HAS_LOAD_CHANNELS],, + [Defined if freerdp instance supports LoadChannels])], + [AC_MSG_RESULT([no])]) +fi + # Restore CPPFLAGS, removing FreeRDP-specific options needed for testing CPPFLAGS="$OLDCPPFLAGS" diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 4cf8e5f4f..f0ab9b768 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -79,21 +79,29 @@ #include #include -BOOL rdp_freerdp_pre_connect(freerdp* instance) { - +/** + * Initializes and loads the necessary FreeRDP plugins based on the current + * RDP session settings. This function is designed to work in environments + * where the FreeRDP instance expects a LoadChannels callback to be set + * otherwise it can becalled directly from our pre_connect callback. It + * configures various features such as display resizing, multi-touch support, + * audio input, clipboard synchronization, device redirection, and graphics + * pipeline, by loading their corresponding plugins if they are enabled in the + * session settings. + * + * @param instance + * The FreeRDP instance to be prepared, containing all context and + * settings for the session. + * + * @return + * Always TRUE. + */ +static BOOL rdp_freerdp_load_channels(freerdp* instance) { rdpContext* context = GUAC_RDP_CONTEXT(instance); - rdpGraphics* graphics = context->graphics; - guac_client* client = ((rdp_freerdp_context*) context)->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_settings* settings = rdp_client->settings; - /* Push desired settings to FreeRDP */ - guac_rdp_push_settings(client, settings, instance); - - /* Init FreeRDP add-in provider */ - freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); - /* Load "disp" plugin for display update */ if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) guac_rdp_disp_load_plugin(context); @@ -125,6 +133,53 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { guac_rdpsnd_load_plugin(context); } + /* Load "rdpgfx" plugin for Graphics Pipeline Extension */ + if (settings->enable_gfx) + guac_rdp_rdpgfx_load_plugin(context); + + /* Load plugin providing Dynamic Virtual Channel support, if required */ + if (freerdp_settings_get_bool(GUAC_RDP_CONTEXT(instance)->settings, FreeRDP_SupportDynamicChannels) && + guac_freerdp_channels_load_plugin(context, "drdynvc", + GUAC_RDP_CONTEXT(instance)->settings)) { + guac_client_log(client, GUAC_LOG_WARNING, + "Failed to load drdynvc plugin. Display update and audio " + "input support will be disabled."); + } + + return TRUE; +} + +/** + * Prepares the FreeRDP instance for connection by setting up session-specific + * configurations like graphics, plugins, and RDP settings. This involves + * integrating Guacamole's custom rendering handlers (for bitmaps, glyphs, + * and pointers). If using a freerdp instance that does not expect a + * LoadChannels callback then this function manually loads RDP channels. + * + * @param instance + * The FreeRDP instance to be prepared, containing all context and + * settings for the session. + * + * @return + * Returns TRUE if the pre-connection process completes successfully. + * Returns FALSE if an error occurs during the initialization of the + * FreeRDP GDI system. + */ +static BOOL rdp_freerdp_pre_connect(freerdp* instance) { + + rdpContext* context = GUAC_RDP_CONTEXT(instance); + rdpGraphics* graphics = context->graphics; + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_settings* settings = rdp_client->settings; + + /* Push desired settings to FreeRDP */ + guac_rdp_push_settings(client, settings, instance); + + /* Init FreeRDP add-in provider */ + freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); + /* Load RAIL plugin if RemoteApp in use */ if (settings->remote_app != NULL) guac_rdp_rail_load_plugin(context); @@ -194,21 +249,15 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { primary->MemBlt = guac_rdp_gdi_memblt; primary->OpaqueRect = guac_rdp_gdi_opaquerect; - /* Load "rdpgfx" plugin for Graphics Pipeline Extension */ - if (settings->enable_gfx) - guac_rdp_rdpgfx_load_plugin(context); - - /* Load plugin providing Dynamic Virtual Channel support, if required */ - if (freerdp_settings_get_bool(GUAC_RDP_CONTEXT(instance)->settings, FreeRDP_SupportDynamicChannels) && - guac_freerdp_channels_load_plugin(context, "drdynvc", - GUAC_RDP_CONTEXT(instance)->settings)) { - guac_client_log(client, GUAC_LOG_WARNING, - "Failed to load drdynvc plugin. Display update and audio " - "input support will be disabled."); - } + /* + * If the freerdp instance does not have a LoadChannels callback for + * loading plugins we use the PreConnect callback to load plugins instead. + */ + #ifndef RDP_INST_HAS_LOAD_CHANNELS + rdp_freerdp_load_channels(instance); + #endif return TRUE; - } /** @@ -489,6 +538,14 @@ static int guac_rdp_handle_connection(guac_client* client) { /* Init client */ freerdp* rdp_inst = freerdp_new(); + + /* + * If the freerdp instance has a LoadChannels callback for loading plugins + * we use that instead of the PreConnect callback to load plugins. + */ + #ifdef RDP_INST_HAS_LOAD_CHANNELS + rdp_inst->LoadChannels = rdp_freerdp_load_channels; + #endif rdp_inst->PreConnect = rdp_freerdp_pre_connect; rdp_inst->Authenticate = rdp_freerdp_authenticate;