From 7b2608a938e57bb3268099f3209ffa77207e4021 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Wed, 8 May 2019 11:43:22 +0200 Subject: [PATCH] Added gfx capability filter option. With this new option it is possible to selectively disable certain GFX capability versions. This way buggy behaviour can be addressed at runtime. --- channels/rdpgfx/client/rdpgfx_main.c | 177 ++++++++++++++++++++++++----------- channels/rdpgfx/client/rdpgfx_main.h | 1 + include/freerdp/settings.h | 4 +- server/shadow/shadow_client.c | 95 +++++++++++++------ 4 files changed, 191 insertions(+), 86 deletions(-) diff --git a/channels/rdpgfx/client/rdpgfx_main.c b/channels/rdpgfx/client/rdpgfx_main.c index dcf6cbe..2a448bd 100644 --- a/channels/rdpgfx/client/rdpgfx_main.c +++ b/channels/rdpgfx/client/rdpgfx_main.c @@ -106,6 +106,32 @@ fail: return error; } +static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps) +{ + const UINT32 filter = gfx->capsFilter; + const UINT32 capList[] = + { + RDPGFX_CAPVERSION_8, + RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, + RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, + RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, + RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106 + }; + UINT32 x; + + for (x = 0; x < ARRAYSIZE(capList); x++) + { + if (caps == capList[x]) + return (filter & (1 << x)) != 0; + } + + return TRUE; +} + /** * Function description * @@ -113,54 +139,69 @@ fail: */ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) { - UINT error = CHANNEL_RC_OK; RDPGFX_PLUGIN* gfx; RdpgfxClientContext* context; RDPGFX_CAPSET* capsSet; RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 }; RDPGFX_CAPS_ADVERTISE_PDU pdu; + + if (!callback) + return ERROR_BAD_ARGUMENTS; + gfx = (RDPGFX_PLUGIN*) callback->plugin; + + if (!gfx) + return ERROR_BAD_CONFIGURATION; + context = (RdpgfxClientContext*) gfx->iface.pInterface; + + if (!context) + return ERROR_BAD_CONFIGURATION; + pdu.capsSetCount = 0; pdu.capsSets = (RDPGFX_CAPSET*) capsSets; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_8; - capsSet->length = 4; - capsSet->flags = 0; - if (gfx->ThinClient) - capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT; + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_8)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_8; + capsSet->length = 4; + capsSet->flags = 0; - /* in CAPVERSION_8 the spec says that we should not have both - * thinclient and smallcache (and thinclient implies a small cache) - */ - if (gfx->SmallCache && !gfx->ThinClient) - capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE; + if (gfx->ThinClient) + capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_81; - capsSet->length = 4; - capsSet->flags = 0; + /* in CAPVERSION_8 the spec says that we should not have both + * thinclient and smallcache (and thinclient implies a small cache) + */ + if (gfx->SmallCache && !gfx->ThinClient) + capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE; + } - if (gfx->ThinClient) - capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT; + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_81)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_81; + capsSet->length = 4; + capsSet->flags = 0; - if (gfx->SmallCache) - capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE; + if (gfx->ThinClient) + capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT; + + if (gfx->SmallCache) + capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE; #ifdef WITH_GFX_H264 - if (gfx->H264) - capsSet->flags |= RDPGFX_CAPS_FLAG_AVC420_ENABLED; + if (gfx->H264) + capsSet->flags |= RDPGFX_CAPS_FLAG_AVC420_ENABLED; #endif + } if (!gfx->H264 || gfx->AVC444) { UINT32 caps10Flags = 0; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_10; - capsSet->length = 4; if (gfx->SmallCache) caps10Flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE; @@ -173,43 +214,68 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) #else caps10Flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED; #endif - capsSet->flags = caps10Flags; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_101; - capsSet->length = 0x10; - capsSet->flags = 0; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_102; - capsSet->length = 0x4; - capsSet->flags = caps10Flags; + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_10)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_10; + capsSet->length = 4; + capsSet->flags = caps10Flags; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_101)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_101; + capsSet->length = 0x10; + capsSet->flags = 0; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_102)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_102; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } if (gfx->ThinClient) caps10Flags |= RDPGFX_CAPS_FLAG_AVC_THINCLIENT; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_103; - capsSet->length = 0x4; - capsSet->flags = caps10Flags & ~RDPGFX_CAPS_FLAG_SMALL_CACHE; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_104; - capsSet->length = 0x4; - capsSet->flags = caps10Flags; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_105; - capsSet->length = 0x4; - capsSet->flags = caps10Flags; - capsSet = &capsSets[pdu.capsSetCount++]; - capsSet->version = RDPGFX_CAPVERSION_106; - capsSet->length = 0x4; - capsSet->flags = caps10Flags; - } + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_103)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_103; + capsSet->length = 0x4; + capsSet->flags = caps10Flags & ~RDPGFX_CAPS_FLAG_SMALL_CACHE; + } - if (context) - { - IFCALLRET(context->CapsAdvertise, error, context, &pdu); + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_104)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_104; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_105)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_105; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_106; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } } - return error; + return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu); } /** @@ -1949,6 +2015,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) gfx->H264 = gfx->settings->GfxH264; gfx->AVC444 = gfx->settings->GfxAVC444; gfx->SendQoeAck = gfx->settings->GfxSendQoeAck; + gfx->capsFilter = gfx->settings->GfxCapsFilter; if (gfx->H264) gfx->SmallCache = TRUE; diff --git a/channels/rdpgfx/client/rdpgfx_main.h b/channels/rdpgfx/client/rdpgfx_main.h index bf7be0d..aada843 100644 --- a/channels/rdpgfx/client/rdpgfx_main.h +++ b/channels/rdpgfx/client/rdpgfx_main.h @@ -67,6 +67,7 @@ struct _RDPGFX_PLUGIN BOOL ProgressiveV2; BOOL H264; BOOL AVC444; + UINT32 capsFilter; ZGFX_CONTEXT* zgfx; UINT32 UnacknowledgedFrames; diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 5a4e4c6..d6eef5c 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -835,6 +835,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_GfxAVC444 (3845) #define FreeRDP_GfxSendQoeAck (3846) #define FreeRDP_GfxAVC444v2 (3847) +#define FreeRDP_GfxCapsFilter (3848) #define FreeRDP_BitmapCacheV3CodecId (3904) #define FreeRDP_DrawNineGridEnabled (3968) #define FreeRDP_DrawNineGridCacheSize (3969) @@ -1419,7 +1420,8 @@ struct rdp_settings ALIGN64 BOOL GfxAVC444; /* 3845 */ ALIGN64 BOOL GfxSendQoeAck; /* 3846 */ ALIGN64 BOOL GfxAVC444v2; /* 3847 */ - UINT64 padding3904[3904 - 3848]; /* 3848 */ + ALIGN64 UINT32 GfxCapsFilter; /* 3848 */ + UINT64 padding3904[3904 - 3849]; /* 3849 */ /** * Caches diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index 18fc8e3..b414081 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -613,6 +613,32 @@ static UINT shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, return CHANNEL_RC_OK; } +static BOOL shadow_are_caps_filtered(const rdpSettings* settings, UINT32 caps) +{ + const UINT32 filter = settings->GfxCapsFilter; + const UINT32 capList[] = + { + RDPGFX_CAPVERSION_8, + RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, + RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, + RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, + RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106 + }; + UINT32 x; + + for (x = 0; x < ARRAYSIZE(capList); x++) + { + if (caps == capList[x]) + return (filter & (1 << x)) != 0; + } + + return TRUE; +} + static BOOL shadow_client_caps_test_version(RdpgfxServerContext* context, const RDPGFX_CAPSET* capsSets, UINT32 capsSetCount, @@ -623,6 +649,9 @@ static BOOL shadow_client_caps_test_version(RdpgfxServerContext* context, rdpSettings* settings; settings = context->rdpcontext->settings; + if (shadow_are_caps_filtered(settings, capsVersion)) + return FALSE; + for (index = 0; index < capsSetCount; index++) { const RDPGFX_CAPSET* currentCaps = &capsSets[index]; @@ -697,52 +726,58 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, RDPGFX_CAPVERSION_10, &rc)) return rc; - for (index = 0; index < capsAdvertise->capsSetCount; index++) + if (!shadow_are_caps_filtered(settings, RDPGFX_CAPVERSION_81)) { - const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index]; - - if (currentCaps->version == RDPGFX_CAPVERSION_81) + for (index = 0; index < capsAdvertise->capsSetCount; index++) { - RDPGFX_CAPSET caps = *currentCaps; - RDPGFX_CAPS_CONFIRM_PDU pdu; - pdu.capsSet = ∩︀ + const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index]; - if (settings) + if (currentCaps->version == RDPGFX_CAPVERSION_81) { - flags = pdu.capsSet->flags; - settings->GfxAVC444v2 = settings->GfxAVC444 = FALSE; - settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); - settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); + RDPGFX_CAPSET caps = *currentCaps; + RDPGFX_CAPS_CONFIRM_PDU pdu; + pdu.capsSet = ∩︀ + + if (settings) + { + flags = pdu.capsSet->flags; + settings->GfxAVC444v2 = settings->GfxAVC444 = FALSE; + settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); + settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); #ifndef WITH_GFX_H264 - settings->GfxH264 = FALSE; - pdu.capsSet->flags &= ~RDPGFX_CAPS_FLAG_AVC420_ENABLED; + settings->GfxH264 = FALSE; + pdu.capsSet->flags &= ~RDPGFX_CAPS_FLAG_AVC420_ENABLED; #else - settings->GfxH264 = (flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED); + settings->GfxH264 = (flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED); #endif - } + } - return context->CapsConfirm(context, &pdu); + return context->CapsConfirm(context, &pdu); + } } } - for (index = 0; index < capsAdvertise->capsSetCount; index++) + if (!shadow_are_caps_filtered(settings, RDPGFX_CAPVERSION_8)) { - const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index]; - - if (currentCaps->version == RDPGFX_CAPVERSION_8) + for (index = 0; index < capsAdvertise->capsSetCount; index++) { - RDPGFX_CAPSET caps = *currentCaps; - RDPGFX_CAPS_CONFIRM_PDU pdu; - pdu.capsSet = ∩︀ + const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index]; - if (settings) + if (currentCaps->version == RDPGFX_CAPVERSION_8) { - flags = pdu.capsSet->flags; - settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); - settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); - } + RDPGFX_CAPSET caps = *currentCaps; + RDPGFX_CAPS_CONFIRM_PDU pdu; + pdu.capsSet = ∩︀ - return context->CapsConfirm(context, &pdu); + if (settings) + { + flags = pdu.capsSet->flags; + settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); + settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); + } + + return context->CapsConfirm(context, &pdu); + } } } -- 2.7.4