From c4693009413905ebf77c94cdba94d4e42dcc57f5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 5 Jul 2021 16:54:31 -0700 Subject: [PATCH] GUACAMOLE-377: Support for RDPGFX. --- src/protocols/rdp/Makefile.am | 2 + src/protocols/rdp/channels/rdpgfx.c | 122 ++++++++++++++++++++++++++++ src/protocols/rdp/channels/rdpgfx.h | 49 +++++++++++ src/protocols/rdp/rdp.c | 22 +++-- src/protocols/rdp/settings.c | 11 +++ 5 files changed, 197 insertions(+), 9 deletions(-) create mode 100644 src/protocols/rdp/channels/rdpgfx.c create mode 100644 src/protocols/rdp/channels/rdpgfx.h diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 839cc9af..90404b71 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -57,6 +57,7 @@ libguac_client_rdp_la_SOURCES = \ channels/rdpdr/rdpdr-printer.c \ channels/rdpdr/rdpdr.c \ channels/rdpei.c \ + channels/rdpgfx.c \ channels/rdpsnd/rdpsnd-messages.c \ channels/rdpsnd/rdpsnd.c \ client.c \ @@ -103,6 +104,7 @@ noinst_HEADERS = \ channels/rdpdr/rdpdr-printer.h \ channels/rdpdr/rdpdr.h \ channels/rdpei.h \ + channels/rdpgfx.h \ channels/rdpsnd/rdpsnd-messages.h \ channels/rdpsnd/rdpsnd.h \ client.h \ diff --git a/src/protocols/rdp/channels/rdpgfx.c b/src/protocols/rdp/channels/rdpgfx.c new file mode 100644 index 00000000..f22ffbf3 --- /dev/null +++ b/src/protocols/rdp/channels/rdpgfx.c @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "channels/rdpgfx.h" +#include "plugins/channels.h" +#include "rdp.h" +#include "settings.h" + +#include +#include +#include +#include +#include + +#include +#include + +/** + * Callback which associates handlers specific to Guacamole with the + * RdpgfxClientContext instance allocated by FreeRDP to deal with received + * RDPGFX (Graphics Pipeline) messages. + * + * This function is called whenever a channel connects via the PubSub event + * system within FreeRDP, but only has any effect if the connected channel is + * the RDPGFX channel. This specific callback is registered with the + * PubSub system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is + * called. + * + * @param context + * The rdpContext associated with the active RDP session. + * + * @param e + * Event-specific arguments, mainly the name of the channel, and a + * reference to the associated plugin loaded for that channel by FreeRDP. + */ +static void guac_rdp_rdpgfx_channel_connected(rdpContext* context, + ChannelConnectedEventArgs* e) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + + /* Ignore connection event if it's not for the RDPGFX channel */ + if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) != 0) + return; + + /* Init GDI-backed support for the Graphics Pipeline */ + RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) e->pInterface; + rdpGdi* gdi = context->gdi; + + if (!gdi_graphics_pipeline_init(gdi, rdpgfx)) + guac_client_log(client, GUAC_LOG_WARNING, "Rendering backend for RDPGFX " + "channel could not be loaded. Graphics may not render at all!"); + else + guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel will be used for " + "the RDP Graphics Pipeline Extension."); + +} + +/** + * Callback which handles any RDPGFX cleanup specific to Guacamole. + * + * This function is called whenever a channel disconnects via the PubSub event + * system within FreeRDP, but only has any effect if the disconnected channel + * is the RDPGFX channel. This specific callback is registered with the PubSub + * system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is + * called. + * + * @param context + * The rdpContext associated with the active RDP session. + * + * @param e + * Event-specific arguments, mainly the name of the channel, and a + * reference to the associated plugin loaded for that channel by FreeRDP. + */ +static void guac_rdp_rdpgfx_channel_disconnected(rdpContext* context, + ChannelDisconnectedEventArgs* e) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + + /* Ignore disconnection event if it's not for the RDPGFX channel */ + if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) != 0) + return; + + /* Un-init GDI-backed support for the Graphics Pipeline */ + RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) e->pInterface; + rdpGdi* gdi = context->gdi; + gdi_graphics_pipeline_uninit(gdi, rdpgfx); + + guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel support unloaded."); + +} + +void guac_rdp_rdpgfx_load_plugin(rdpContext* context) { + + /* Subscribe to and handle channel connected events */ + PubSub_SubscribeChannelConnected(context->pubSub, + (pChannelConnectedEventHandler) guac_rdp_rdpgfx_channel_connected); + + /* Subscribe to and handle channel disconnected events */ + PubSub_SubscribeChannelDisconnected(context->pubSub, + (pChannelDisconnectedEventHandler) guac_rdp_rdpgfx_channel_disconnected); + + /* Add "rdpgfx" channel */ + guac_freerdp_dynamic_channel_collection_add(context->settings, "rdpgfx", NULL); + +} + diff --git a/src/protocols/rdp/channels/rdpgfx.h b/src/protocols/rdp/channels/rdpgfx.h new file mode 100644 index 00000000..baeec876 --- /dev/null +++ b/src/protocols/rdp/channels/rdpgfx.h @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_RDP_CHANNELS_RDPGFX_H +#define GUAC_RDP_CHANNELS_RDPGFX_H + +#include "settings.h" + +#include +#include +#include + +/** + * Adds FreeRDP's "rdpgfx" plugin to the list of dynamic virtual channel plugins + * to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will + * automatically be assicated with the guac_rdp_rdpgfx instance pointed to by the + * current guac_rdp_client. The plugin will only be loaded once the "drdynvc" + * plugin is loaded. The "rdpgfx" plugin ultimately adds support for the RDP + * Graphics Pipeline Extension. + * + * If failures occur, messages noting the specifics of those failures will be + * logged. + * + * This MUST be called within the PreConnect callback of the freerdp instance + * for Graphics Pipeline support to be loaded. + * + * @param context + * The rdpContext associated with the active RDP session. + */ +void guac_rdp_rdpgfx_load_plugin(rdpContext* context); + +#endif + diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 5905f8e6..b37e2633 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -28,6 +28,7 @@ #include "channels/rail.h" #include "channels/rdpdr/rdpdr.h" #include "channels/rdpei.h" +#include "channels/rdpgfx.h" #include "channels/rdpsnd/rdpsnd.h" #include "client.h" #include "color.h" @@ -137,15 +138,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { } - /* Load plugin providing Dynamic Virtual Channel support, if required */ - if (instance->settings->SupportDynamicChannels && - guac_freerdp_channels_load_plugin(context, "drdynvc", - instance->settings)) { - guac_client_log(client, GUAC_LOG_WARNING, - "Failed to load drdynvc plugin. Display update and audio " - "input support will be disabled."); - } - /* Init FreeRDP internal GDI implementation */ if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE))) return FALSE; @@ -204,6 +196,18 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { offscreen_cache_register_callbacks(instance->update); palette_cache_register_callbacks(instance->update); + /* Load "rdpgfx" plugin for Graphics Pipeline Extension */ + guac_rdp_rdpgfx_load_plugin(context); + + /* Load plugin providing Dynamic Virtual Channel support, if required */ + if (instance->settings->SupportDynamicChannels && + guac_freerdp_channels_load_plugin(context, "drdynvc", + 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; } diff --git a/src/protocols/rdp/settings.c b/src/protocols/rdp/settings.c index 81dfedf9..feb6a00a 100644 --- a/src/protocols/rdp/settings.c +++ b/src/protocols/rdp/settings.c @@ -1397,6 +1397,17 @@ void guac_rdp_push_settings(guac_client* client, /* Explicitly set flag value */ rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings); + rdp_settings->SupportGraphicsPipeline = TRUE; + rdp_settings->RemoteFxCodec = TRUE; + + /* Required for RemoteFX / Graphics Pipeline */ + rdp_settings->FastPathOutput = TRUE; + rdp_settings->FrameMarkerCommandEnabled = TRUE; + rdp_settings->ColorDepth = 32; + rdp_settings->SoftwareGdi = TRUE; + /*rdp_settings->GfxH264 = TRUE; + rdp_settings->GfxAVC444 = TRUE;*/ + /* Set individual flags - some FreeRDP versions overwrite the above */ rdp_settings->AllowFontSmoothing = guac_settings->font_smoothing_enabled; rdp_settings->DisableWallpaper = !guac_settings->wallpaper_enabled;