FreeRDP
Loading...
Searching...
No Matches
client/rdpgfx_main.c
1
24#include <freerdp/config.h>
25
26#include <winpr/assert.h>
27#include <winpr/cast.h>
28
29#include <winpr/crt.h>
30#include <winpr/wlog.h>
31#include <winpr/print.h>
32#include <winpr/synch.h>
33#include <winpr/thread.h>
34#include <winpr/stream.h>
35#include <winpr/sysinfo.h>
36#include <winpr/cmdline.h>
37#include <winpr/collections.h>
38
39#include <freerdp/addin.h>
40#include <freerdp/channels/log.h>
41
42#include "rdpgfx_common.h"
43#include "rdpgfx_codec.h"
44
45#include "rdpgfx_main.h"
46
47#define GFXTAG CHANNELS_TAG("rdpgfx.client")
48
49#define RDPGFX_NUMBER_CAPSETS 0x100
50
51typedef struct
52{
53 uint64_t cntGfxCodecID[RDPGFX_CODECID_MAX];
54 uint64_t cntGfxCommandID[RDPGFX_CMDID_MAX + 1];
55} RdpgfxClientContextStats;
56
57typedef struct
58{
59 RdpgfxClientContext common;
60 RdpgfxClientContextStats stats;
61} RdpgfxClientContextInt;
62
63static const UINT32 MAX_SURFACE_SIZE = 32766;
64static const UINT32 MAX_MONITOR_COUNT = 16;
65
66size_t rdpgfx_stats_max_index(void)
67{
68 return sizeof(RdpgfxClientContextStats) / sizeof(uint64_t);
69}
70
71const char* rdpgfx_stats_name_for_index(size_t index)
72{
73 if (index < RDPGFX_CODECID_MAX)
74 return rdpgfx_get_codec_id_string(WINPR_ASSERTING_INT_CAST(UINT16, index));
75
76 index -= RDPGFX_CODECID_MAX;
77 if (index < RDPGFX_CMDID_MAX)
78 return rdpgfx_get_cmd_id_string(WINPR_ASSERTING_INT_CAST(UINT16, index));
79 return "RDPGFX_STATS_UNUSED";
80}
81
82uint64_t rdpgfx_stats_value_for_index(RdpgfxClientContext* context, size_t index)
83{
84 WINPR_ASSERT(context);
85
86 RdpgfxClientContextInt* intCtx = (RdpgfxClientContextInt*)context;
87
88 if (index < RDPGFX_CODECID_MAX)
89 return intCtx->stats.cntGfxCodecID[WINPR_ASSERTING_INT_CAST(UINT16, index)];
90
91 index -= RDPGFX_CODECID_MAX;
92 if (index < RDPGFX_CMDID_MAX)
93 return intCtx->stats.cntGfxCommandID[WINPR_ASSERTING_INT_CAST(UINT16, index)];
94 return 0;
95}
96
97static void rdpgfx_codecid_event(RdpgfxClientContext* context, uint32_t index)
98{
99 if (!context)
100 return;
101 RdpgfxClientContextInt* intCtx = (RdpgfxClientContextInt*)context;
102 WINPR_ASSERT(intCtx);
103 if (index < RDPGFX_CODECID_MAX)
104 intCtx->stats.cntGfxCodecID[index]++;
105 else
106 {
107 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
108 WINPR_ASSERT(gfx);
109 WLog_Print(gfx->base.log, WLOG_WARN, "Invalid event index %" PRIu32, index);
110 }
111}
112
113static void rdpgfx_stats_cmdid_event(RdpgfxClientContext* context, uint32_t index)
114{
115 if (!context)
116 return;
117 RdpgfxClientContextInt* intCtx = (RdpgfxClientContextInt*)context;
118 WINPR_ASSERT(intCtx);
119
120 if (index >= RDPGFX_CMDID_MAX)
121 {
122 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
123 WINPR_ASSERT(gfx);
124 WLog_Print(gfx->base.log, WLOG_WARN, "Invalid codecID %" PRIu32, index);
125 index = RDPGFX_CMDID_MAX;
126 }
127 intCtx->stats.cntGfxCommandID[index]++;
128}
129
130static BOOL delete_surface(const void* key, void* value, void* arg)
131{
132 const UINT16 id = (UINT16)(uintptr_t)(key);
133 if (id < 1)
134 return FALSE;
135
136 RdpgfxClientContext* context = arg;
137 const RDPGFX_DELETE_SURFACE_PDU pdu = { .surfaceId = id - 1 };
138
139 WINPR_UNUSED(value);
140
141 if (context)
142 {
143 UINT error = CHANNEL_RC_OK;
144 IFCALLRET(context->DeleteSurface, error, context, &pdu);
145
146 if (error)
147 {
148 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
149 WINPR_ASSERT(gfx);
150 WLog_Print(gfx->base.log, WLOG_ERROR,
151 "context->DeleteSurface failed with error %" PRIu32 "", error);
152 }
153 }
154 return TRUE;
155}
156
157static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable)
158{
159 WINPR_ASSERT(context);
160 if (!HashTable_Foreach(SurfaceTable, delete_surface, context))
161 {
162 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
163 WINPR_ASSERT(gfx);
164 WLog_Print(gfx->base.log, WLOG_WARN, "delete_surface failed");
165 }
166}
167
168static UINT evict_cache_slots(RdpgfxClientContext* context, UINT16 MaxCacheSlots, void** CacheSlots)
169{
170 UINT error = CHANNEL_RC_OK;
171
172 WINPR_ASSERT(CacheSlots);
173 for (UINT16 index = 0; index < MaxCacheSlots; index++)
174 {
175 if (CacheSlots[index])
176 {
177 const RDPGFX_EVICT_CACHE_ENTRY_PDU pdu = { .cacheSlot = index + 1 };
178
179 if (context && context->EvictCacheEntry)
180 {
181 const UINT rc = context->EvictCacheEntry(context, &pdu);
182 if (rc != CHANNEL_RC_OK)
183 error = rc;
184 }
185
186 CacheSlots[index] = nullptr;
187 }
188 }
189 return error;
190}
191
197static UINT rdpgfx_send_caps_advertise_pdu(RdpgfxClientContext* context,
198 const RDPGFX_CAPS_ADVERTISE_PDU* pdu)
199{
200 UINT error = CHANNEL_RC_OK;
201
202 WINPR_ASSERT(pdu);
203 WINPR_ASSERT(context);
204
205 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
206
207 if (!gfx || !gfx->base.listener_callback)
208 return ERROR_BAD_ARGUMENTS;
209
210 GENERIC_CHANNEL_CALLBACK* callback = gfx->base.listener_callback->channel_callback;
211
212 RDPGFX_HEADER header = { .flags = 0,
213 .cmdId = RDPGFX_CMDID_CAPSADVERTISE,
214 .pduLength = RDPGFX_HEADER_SIZE + 2 };
215
216 for (UINT16 index = 0; index < pdu->capsSetCount; index++)
217 {
218 const RDPGFX_CAPSET* capsSet = &(pdu->capsSets[index]);
219 header.pduLength += RDPGFX_CAPSET_BASE_SIZE + capsSet->length;
220 }
221
222 WLog_Print(gfx->base.log, WLOG_DEBUG, "SendCapsAdvertisePdu %" PRIu16 "", pdu->capsSetCount);
223 wStream* s = Stream_New(nullptr, header.pduLength);
224
225 if (!s)
226 {
227 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
228 return CHANNEL_RC_NO_MEMORY;
229 }
230
231 if ((error = rdpgfx_write_header(s, &header)))
232 goto fail;
233
234 /* RDPGFX_CAPS_ADVERTISE_PDU */
235 Stream_Write_UINT16(s, pdu->capsSetCount); /* capsSetCount (2 bytes) */
236
237 for (UINT16 index = 0; index < pdu->capsSetCount; index++)
238 {
239 const RDPGFX_CAPSET* capsSet = &(pdu->capsSets[index]);
240
241 WLog_Print(gfx->base.log, WLOG_DEBUG, "Sending %s [0x%08" PRIx32 "] flags=0x%08" PRIx32,
242 rdpgfx_caps_version_str(capsSet->version), capsSet->version, capsSet->flags);
243
244 Stream_Write_UINT32(s, capsSet->version); /* version (4 bytes) */
245 Stream_Write_UINT32(s, capsSet->length); /* capsDataLength (4 bytes) */
246 Stream_Write_UINT32(s, capsSet->flags); /* capsData (4 bytes) */
247 if (capsSet->length < 4)
248 goto fail;
249 Stream_Zero(s, capsSet->length - 4);
250 }
251
252 Stream_SealLength(s);
253 error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
254 nullptr);
255fail:
256 Stream_Free(s, TRUE);
257 return error;
258}
259
260static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps)
261{
262 WINPR_ASSERT(gfx);
263 const UINT32 filter =
264 freerdp_settings_get_uint32(gfx->rdpcontext->settings, FreeRDP_GfxCapsFilter);
265 const UINT32 capList[] = {
266#if defined(WITH_GFX_AV1)
267 RDPGFX_CAPVERSION_FRDP_1,
268#endif
269 RDPGFX_CAPVERSION_8,
270 RDPGFX_CAPVERSION_81,
271 RDPGFX_CAPVERSION_10,
272 RDPGFX_CAPVERSION_101,
273 RDPGFX_CAPVERSION_102,
274 RDPGFX_CAPVERSION_103,
275 RDPGFX_CAPVERSION_104,
276 RDPGFX_CAPVERSION_105,
277 RDPGFX_CAPVERSION_106,
278 RDPGFX_CAPVERSION_106_ERR,
279 RDPGFX_CAPVERSION_107
280#if defined(WITH_GFX_AZURE)
281 ,
282 RDPGFX_CAPVERSION_111,
283 RDPGFX_CAPVERSION_112,
284 RDPGFX_CAPVERSION_113
285#endif
286 };
287
288 for (size_t x = 0; x < ARRAYSIZE(capList); x++)
289 {
290 if (caps == capList[x])
291 return (filter & (1u << x)) != 0;
292 }
293
294 return TRUE;
295}
296
297WINPR_ATTR_NODISCARD
298static RDPGFX_CAPSET* nextCapset(RDPGFX_CAPS_ADVERTISE_PDU* pdu, size_t count)
299{
300 WINPR_ASSERT(pdu);
301 WINPR_ASSERT(pdu->capsSets);
302 WINPR_ASSERT(count > 0);
303 WINPR_ASSERT(pdu->capsSetCount < count);
304 if (pdu->capsSetCount >= count)
305 return nullptr;
306 return &pdu->capsSets[pdu->capsSetCount++];
307}
308
314static UINT rdpgfx_send_supported_caps(GENERIC_CHANNEL_CALLBACK* callback)
315{
316 RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = WINPR_C_ARRAY_INIT;
317
318 if (!callback)
319 return ERROR_BAD_ARGUMENTS;
320
321 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
322
323 if (!gfx)
324 return ERROR_BAD_CONFIGURATION;
325
326 RdpgfxClientContext* context = gfx->context;
327
328 if (!context)
329 return ERROR_BAD_CONFIGURATION;
330
331 RDPGFX_CAPS_ADVERTISE_PDU pdu = { .capsSetCount = 0, .capsSets = capsSets };
332
333 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_8))
334 {
335 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
336 if (!capsSet)
337 return ERROR_BAD_CONFIGURATION;
338 capsSet->version = RDPGFX_CAPVERSION_8;
339 capsSet->length = 4;
340 capsSet->flags = 0;
341
342 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxThinClient))
343 capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT;
344
345 /* in CAPVERSION_8 the spec says that we should not have both
346 * thinclient and smallcache (and thinclient implies a small cache)
347 */
348 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSmallCache) &&
349 !freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxThinClient))
350 capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
351 }
352
353 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_81))
354 {
355 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
356 if (!capsSet)
357 return ERROR_BAD_CONFIGURATION;
358 capsSet->version = RDPGFX_CAPVERSION_81;
359 capsSet->length = 4;
360 capsSet->flags = 0;
361
362 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxThinClient))
363 capsSet->flags |= RDPGFX_CAPS_FLAG_THINCLIENT;
364
365 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSmallCache))
366 capsSet->flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
367
368#ifdef WITH_GFX_H264
369
370 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxH264))
371 capsSet->flags |= RDPGFX_CAPS_FLAG_AVC420_ENABLED;
372
373#endif
374 }
375
376 UINT32 caps10Flags = 0;
377 if (!freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxH264) ||
378 freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxAVC444))
379 {
380 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSmallCache))
381 caps10Flags |= RDPGFX_CAPS_FLAG_SMALL_CACHE;
382
383#ifdef WITH_GFX_H264
384
385 if (!freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxAVC444))
386 caps10Flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED;
387
388#else
389 caps10Flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED;
390#endif
391
392 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_10))
393 {
394 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
395 if (!capsSet)
396 return ERROR_BAD_CONFIGURATION;
397 capsSet->version = RDPGFX_CAPVERSION_10;
398 capsSet->length = 4;
399 capsSet->flags = caps10Flags;
400 }
401
402 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_101))
403 {
404 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
405 if (!capsSet)
406 return ERROR_BAD_CONFIGURATION;
407 capsSet->version = RDPGFX_CAPVERSION_101;
408 capsSet->length = 0x10;
409 capsSet->flags = 0;
410 }
411
412 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_102))
413 {
414 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
415 if (!capsSet)
416 return ERROR_BAD_CONFIGURATION;
417 capsSet->version = RDPGFX_CAPVERSION_102;
418 capsSet->length = 0x4;
419 capsSet->flags = caps10Flags;
420 }
421
422 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxThinClient))
423 {
424 if ((caps10Flags & RDPGFX_CAPS_FLAG_AVC_DISABLED) == 0)
425 caps10Flags |= RDPGFX_CAPS_FLAG_AVC_THINCLIENT;
426 }
427
428 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_103))
429 {
430 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
431 if (!capsSet)
432 return ERROR_BAD_CONFIGURATION;
433 capsSet->version = RDPGFX_CAPVERSION_103;
434 capsSet->length = 0x4;
435 capsSet->flags = caps10Flags & ~RDPGFX_CAPS_FLAG_SMALL_CACHE;
436 }
437
438 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_104))
439 {
440 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
441 if (!capsSet)
442 return ERROR_BAD_CONFIGURATION;
443 capsSet->version = RDPGFX_CAPVERSION_104;
444 capsSet->length = 0x4;
445 capsSet->flags = caps10Flags;
446 }
447
448 /* The following capabilities expect support for image scaling.
449 * Disable these for builds that do not have support for that.
450 */
451#if defined(WITH_CAIRO) || defined(WITH_SWSCALE)
452 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_105))
453 {
454 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
455 if (!capsSet)
456 return ERROR_BAD_CONFIGURATION;
457 capsSet->version = RDPGFX_CAPVERSION_105;
458 capsSet->length = 0x4;
459 capsSet->flags = caps10Flags;
460 }
461
462 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106))
463 {
464 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
465 if (!capsSet)
466 return ERROR_BAD_CONFIGURATION;
467 capsSet->version = RDPGFX_CAPVERSION_106;
468 capsSet->length = 0x4;
469 capsSet->flags = caps10Flags;
470 }
471
472 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR))
473 {
474 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
475 if (!capsSet)
476 return ERROR_BAD_CONFIGURATION;
477 capsSet->version = RDPGFX_CAPVERSION_106_ERR;
478 capsSet->length = 0x4;
479 capsSet->flags = caps10Flags;
480 }
481#endif
482
483 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107))
484 {
485 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
486 if (!capsSet)
487 return ERROR_BAD_CONFIGURATION;
488 capsSet->version = RDPGFX_CAPVERSION_107;
489 capsSet->length = 0x4;
490 capsSet->flags = caps10Flags;
491#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE)
492 capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
493#endif
494 }
495
496#if defined(WITH_GFX_AZURE)
497 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_111))
498 {
499 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
500 if (!capsSet)
501 return ERROR_BAD_CONFIGURATION;
502 capsSet->version = RDPGFX_CAPVERSION_111;
503 capsSet->length = 0x4;
504 capsSet->flags = caps10Flags;
505#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE)
506 capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
507#endif
508 }
509
510 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_112))
511 {
512 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
513 if (!capsSet)
514 return ERROR_BAD_CONFIGURATION;
515 capsSet->version = RDPGFX_CAPVERSION_112;
516 capsSet->length = 0x4;
517 capsSet->flags = caps10Flags;
518#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE)
519 capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
520#endif
521 }
522
523 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_113))
524 {
525 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
526 if (!capsSet)
527 return ERROR_BAD_CONFIGURATION;
528 capsSet->version = RDPGFX_CAPVERSION_113;
529 capsSet->length = 0x4;
530 capsSet->flags = caps10Flags;
531#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE)
532 capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
533#endif
534 }
535#endif
536 }
537
538#if defined(WITH_GFX_AV1)
539 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxCodecAV1))
540 {
541 if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_FRDP_1))
542 {
543 RDPGFX_CAPSET* capsSet = nextCapset(&pdu, ARRAYSIZE(capsSets));
544 if (!capsSet)
545 return ERROR_BAD_CONFIGURATION;
546 capsSet->version = RDPGFX_CAPVERSION_FRDP_1;
547 capsSet->length = 0x4;
548
549 capsSet->flags = caps10Flags | RDPGFX_CAPS_FLAG_AV1_I444_SUPPORTED;
550 const UINT32 profile =
551 freerdp_settings_get_uint32(gfx->rdpcontext->settings, FreeRDP_GfxCodecAV1Profile);
552 if (profile == 0)
553 capsSet->flags |= RDPGFX_CAPS_FLAG_AV1_I444_DISABLED;
554
555#if !defined(WITH_CAIRO) && !defined(WITH_SWSCALE)
556 capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
557#endif
558 }
559 }
560#endif
561
562 return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
563}
564
570static UINT rdpgfx_recv_caps_confirm_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
571{
572 WINPR_ASSERT(callback);
573 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
574
575 WINPR_ASSERT(gfx);
576 RdpgfxClientContext* context = gfx->context;
577
578 RDPGFX_CAPSET capsSet = WINPR_C_ARRAY_INIT;
579 RDPGFX_CAPS_CONFIRM_PDU pdu = { .capsSet = &capsSet };
580
581 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 12))
582 return ERROR_INVALID_DATA;
583
584 Stream_Read_UINT32(s, capsSet.version); /* version (4 bytes) */
585 Stream_Read_UINT32(s, capsSet.length); /* capsDataLength (4 bytes) */
586 Stream_Read_UINT32(s, capsSet.flags); /* capsData (4 bytes) */
587 gfx->TotalDecodedFrames = 0;
588 gfx->ConnectionCaps = capsSet;
589 WLog_Print(gfx->base.log, WLOG_DEBUG,
590 "RecvCapsConfirmPdu: version: %s [0x%08" PRIX32 "] flags: 0x%08" PRIX32 "",
591 rdpgfx_caps_version_str(capsSet.version), capsSet.version, capsSet.flags);
592
593 if (!context)
594 return ERROR_BAD_CONFIGURATION;
595
596 return IFCALLRESULT(CHANNEL_RC_OK, context->CapsConfirm, context, &pdu);
597}
598
604static UINT rdpgfx_send_frame_acknowledge_pdu(RdpgfxClientContext* context,
606{
607 UINT error = 0;
608
609 if (!context || !pdu)
610 return ERROR_BAD_ARGUMENTS;
611
612 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
613
614 if (!gfx || !gfx->base.listener_callback)
615 return ERROR_BAD_CONFIGURATION;
616
617 GENERIC_CHANNEL_CALLBACK* callback = gfx->base.listener_callback->channel_callback;
618
619 if (!callback)
620 return ERROR_BAD_CONFIGURATION;
621
622 const RDPGFX_HEADER header = { .flags = 0,
623 .cmdId = RDPGFX_CMDID_FRAMEACKNOWLEDGE,
624 .pduLength = RDPGFX_HEADER_SIZE + 12 };
625
626 WLog_Print(gfx->base.log, WLOG_TRACE, "SendFrameAcknowledgePdu: %" PRIu32 "", pdu->frameId);
627
628 wStream* s = Stream_New(nullptr, header.pduLength);
629
630 if (!s)
631 {
632 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
633 return CHANNEL_RC_NO_MEMORY;
634 }
635
636 if ((error = rdpgfx_write_header(s, &header)))
637 goto fail;
638
639 /* RDPGFX_FRAME_ACKNOWLEDGE_PDU */
640 Stream_Write_UINT32(s, pdu->queueDepth); /* queueDepth (4 bytes) */
641 Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */
642 Stream_Write_UINT32(s, pdu->totalFramesDecoded); /* totalFramesDecoded (4 bytes) */
643 error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
644 nullptr);
645
646 if (error == CHANNEL_RC_OK) /* frame successfully acked */
647 gfx->UnacknowledgedFrames--;
648
649fail:
650 Stream_Free(s, TRUE);
651 return error;
652}
653
654static UINT rdpgfx_send_qoe_frame_acknowledge_pdu(RdpgfxClientContext* context,
656{
657 UINT error = CHANNEL_RC_OK;
658 const RDPGFX_HEADER header = { .flags = 0,
659 .cmdId = RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE,
660 .pduLength = RDPGFX_HEADER_SIZE + 12 };
661
662 if (!context || !pdu)
663 return ERROR_BAD_ARGUMENTS;
664
665 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
666
667 if (!gfx || !gfx->base.listener_callback)
668 return ERROR_BAD_CONFIGURATION;
669
670 GENERIC_CHANNEL_CALLBACK* callback = gfx->base.listener_callback->channel_callback;
671
672 if (!callback)
673 return ERROR_BAD_CONFIGURATION;
674
675 WLog_Print(gfx->base.log, WLOG_TRACE, "SendQoeFrameAcknowledgePdu: %" PRIu32 "", pdu->frameId);
676 wStream* s = Stream_New(nullptr, header.pduLength);
677
678 if (!s)
679 {
680 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
681 return CHANNEL_RC_NO_MEMORY;
682 }
683
684 if ((error = rdpgfx_write_header(s, &header)))
685 goto fail;
686
687 /* RDPGFX_FRAME_ACKNOWLEDGE_PDU */
688 Stream_Write_UINT32(s, pdu->frameId);
689 Stream_Write_UINT32(s, pdu->timestamp);
690 Stream_Write_UINT16(s, pdu->timeDiffSE);
691 Stream_Write_UINT16(s, pdu->timeDiffEDR);
692 error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
693 nullptr);
694fail:
695 Stream_Free(s, TRUE);
696 return error;
697}
698
704static UINT rdpgfx_recv_reset_graphics_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
705{
706 RDPGFX_RESET_GRAPHICS_PDU pdu = WINPR_C_ARRAY_INIT;
707 WINPR_ASSERT(callback);
708
709 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
710
711 WINPR_ASSERT(gfx);
712
713 RdpgfxClientContext* context = gfx->context;
714 UINT error = CHANNEL_RC_OK;
715 GraphicsResetEventArgs graphicsReset = WINPR_C_ARRAY_INIT;
716
717 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 12))
718 return ERROR_INVALID_DATA;
719
720 Stream_Read_UINT32(s, pdu.width); /* width (4 bytes) */
721 Stream_Read_UINT32(s, pdu.height); /* height (4 bytes) */
722 Stream_Read_UINT32(s, pdu.monitorCount); /* monitorCount (4 bytes) */
723
724 if ((pdu.width > MAX_SURFACE_SIZE) || (pdu.height > MAX_SURFACE_SIZE) ||
725 (pdu.monitorCount > MAX_MONITOR_COUNT))
726 return ERROR_INVALID_DATA;
727
728 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, pdu.monitorCount, 20ull))
729 return ERROR_INVALID_DATA;
730
731 pdu.monitorDefArray = (MONITOR_DEF*)calloc(pdu.monitorCount, sizeof(MONITOR_DEF));
732
733 if (!pdu.monitorDefArray)
734 {
735 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
736 return CHANNEL_RC_NO_MEMORY;
737 }
738
739 for (UINT32 index = 0; index < pdu.monitorCount; index++)
740 {
741 MONITOR_DEF* monitor = &(pdu.monitorDefArray[index]);
742 Stream_Read_INT32(s, monitor->left); /* left (4 bytes) */
743 Stream_Read_INT32(s, monitor->top); /* top (4 bytes) */
744 Stream_Read_INT32(s, monitor->right); /* right (4 bytes) */
745 Stream_Read_INT32(s, monitor->bottom); /* bottom (4 bytes) */
746 Stream_Read_UINT32(s, monitor->flags); /* flags (4 bytes) */
747 }
748
749 const size_t size = (RDPGFX_HEADER_SIZE + 12ULL + (pdu.monitorCount * 20ULL));
750 if (size > 340)
751 {
752 free(pdu.monitorDefArray);
753 return CHANNEL_RC_NULL_DATA;
754 }
755 const size_t pad = 340ULL - size;
756
757 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, (size_t)pad))
758 {
759 free(pdu.monitorDefArray);
760 return CHANNEL_RC_NO_MEMORY;
761 }
762
763 Stream_Seek(s, pad); /* pad (total size is 340 bytes) */
764 WLog_Print(gfx->base.log, WLOG_DEBUG,
765 "RecvResetGraphicsPdu: width: %" PRIu32 " height: %" PRIu32 " count: %" PRIu32 "",
766 pdu.width, pdu.height, pdu.monitorCount);
767
768 for (UINT32 index = 0; index < pdu.monitorCount; index++)
769 {
770 MONITOR_DEF* monitor = &(pdu.monitorDefArray[index]);
771 WLog_Print(gfx->base.log, WLOG_TRACE,
772 "RecvResetGraphicsPdu: monitor left:%" PRIi32 " top:%" PRIi32 " right:%" PRIi32
773 " bottom:%" PRIi32 " flags:0x%" PRIx32 "",
774 monitor->left, monitor->top, monitor->right, monitor->bottom, monitor->flags);
775 }
776
777 if (context)
778 {
779 IFCALLRET(context->ResetGraphics, error, context, &pdu);
780
781 if (error)
782 WLog_Print(gfx->base.log, WLOG_ERROR,
783 "context->ResetGraphics failed with error %" PRIu32 "", error);
784 }
785
786 free(pdu.monitorDefArray);
787
788 /* some listeners may be interested (namely the display channel) */
789 EventArgsInit(&graphicsReset, "libfreerdp");
790 graphicsReset.width = pdu.width;
791 graphicsReset.height = pdu.height;
792 if (PubSub_OnGraphicsReset(gfx->rdpcontext->pubSub, gfx->rdpcontext, &graphicsReset) < 0)
793 return ERROR_INTERNAL_ERROR;
794 return error;
795}
796
802static UINT rdpgfx_recv_evict_cache_entry_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
803{
804 RDPGFX_EVICT_CACHE_ENTRY_PDU pdu = WINPR_C_ARRAY_INIT;
805 WINPR_ASSERT(callback);
806 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
807 WINPR_ASSERT(gfx);
808 RdpgfxClientContext* context = gfx->context;
809 UINT error = CHANNEL_RC_OK;
810
811 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 2))
812 return ERROR_INVALID_DATA;
813
814 Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
815 WLog_Print(gfx->base.log, WLOG_DEBUG, "RecvEvictCacheEntryPdu: cacheSlot: %" PRIu16 "",
816 pdu.cacheSlot);
817
818 if (context)
819 {
820 IFCALLRET(context->EvictCacheEntry, error, context, &pdu);
821
822 if (error)
823 WLog_Print(gfx->base.log, WLOG_ERROR,
824 "context->EvictCacheEntry failed with error %" PRIu32 "", error);
825 }
826
827 return error;
828}
829
835static UINT rdpgfx_save_persistent_cache(RDPGFX_PLUGIN* gfx)
836{
837 UINT error = CHANNEL_RC_OK;
838
839 WINPR_ASSERT(gfx);
840 WINPR_ASSERT(gfx->rdpcontext);
841 rdpSettings* settings = gfx->rdpcontext->settings;
842 RdpgfxClientContext* context = gfx->context;
843
844 WINPR_ASSERT(context);
845 WINPR_ASSERT(settings);
846
847 if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
848 return CHANNEL_RC_OK;
849
850 const char* BitmapCachePersistFile =
851 freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
852 if (!BitmapCachePersistFile)
853 return CHANNEL_RC_OK;
854
855 if (!context->ExportCacheEntry)
856 return CHANNEL_RC_INITIALIZATION_ERROR;
857
858 rdpPersistentCache* persistent = persistent_cache_new();
859
860 if (!persistent)
861 return CHANNEL_RC_NO_MEMORY;
862
863 if (persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, 3) < 1)
864 {
865 error = CHANNEL_RC_INITIALIZATION_ERROR;
866 goto fail;
867 }
868
869 for (UINT16 idx = 0; idx < gfx->MaxCacheSlots; idx++)
870 {
871 if (gfx->CacheSlots[idx])
872 {
873 const UINT16 cacheSlot = idx;
874 PERSISTENT_CACHE_ENTRY cacheEntry = WINPR_C_ARRAY_INIT;
875
876 if (context->ExportCacheEntry(context, cacheSlot, &cacheEntry) != CHANNEL_RC_OK)
877 continue;
878
879 if (persistent_cache_write_entry(persistent, &cacheEntry) < 0)
880 goto fail;
881 }
882 }
883
884 persistent_cache_free(persistent);
885
886 return error;
887fail:
888 persistent_cache_free(persistent);
889 return error;
890}
891
897static UINT rdpgfx_send_cache_import_offer_pdu(RdpgfxClientContext* context,
899{
900 UINT error = CHANNEL_RC_OK;
901
902 if (!context || !pdu)
903 return ERROR_BAD_ARGUMENTS;
904
905 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
906
907 if (!gfx || !gfx->base.listener_callback)
908 return ERROR_BAD_CONFIGURATION;
909
910 GENERIC_CHANNEL_CALLBACK* callback = gfx->base.listener_callback->channel_callback;
911
912 if (!callback)
913 return ERROR_BAD_CONFIGURATION;
914
915 const RDPGFX_HEADER header = { .flags = 0,
916 .cmdId = RDPGFX_CMDID_CACHEIMPORTOFFER,
917 .pduLength =
918 RDPGFX_HEADER_SIZE + 2ul + pdu->cacheEntriesCount * 12ul };
919
920 WLog_Print(gfx->base.log, WLOG_DEBUG, "SendCacheImportOfferPdu: cacheEntriesCount: %" PRIu16 "",
921 pdu->cacheEntriesCount);
922 wStream* s = Stream_New(nullptr, header.pduLength);
923
924 if (!s)
925 {
926 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
927 return CHANNEL_RC_NO_MEMORY;
928 }
929
930 if ((error = rdpgfx_write_header(s, &header)))
931 goto fail;
932
933 if (pdu->cacheEntriesCount <= 0)
934 {
935 WLog_Print(gfx->base.log, WLOG_ERROR, "Invalid cacheEntriesCount: %" PRIu16 "",
936 pdu->cacheEntriesCount);
937 error = ERROR_INVALID_DATA;
938 goto fail;
939 }
940
941 /* cacheEntriesCount (2 bytes) */
942 Stream_Write_UINT16(s, pdu->cacheEntriesCount);
943
944 for (UINT16 index = 0; index < pdu->cacheEntriesCount; index++)
945 {
946 const RDPGFX_CACHE_ENTRY_METADATA* cacheEntry = &(pdu->cacheEntries[index]);
947 Stream_Write_UINT64(s, cacheEntry->cacheKey); /* cacheKey (8 bytes) */
948 Stream_Write_UINT32(s, cacheEntry->bitmapLength); /* bitmapLength (4 bytes) */
949 }
950
951 error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
952 nullptr);
953
954fail:
955 Stream_Free(s, TRUE);
956 return error;
957}
958
964static UINT rdpgfx_send_cache_offer(RDPGFX_PLUGIN* gfx)
965{
966 int count = 0;
967 UINT error = CHANNEL_RC_OK;
968 PERSISTENT_CACHE_ENTRY entry = WINPR_C_ARRAY_INIT;
969 RDPGFX_CACHE_IMPORT_OFFER_PDU* offer = nullptr;
970
971 WINPR_ASSERT(gfx);
972 WINPR_ASSERT(gfx->rdpcontext);
973
974 RdpgfxClientContext* context = gfx->context;
975 rdpSettings* settings = gfx->rdpcontext->settings;
976
977 if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
978 return CHANNEL_RC_OK;
979
980 const char* BitmapCachePersistFile =
981 freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
982 if (!BitmapCachePersistFile)
983 return CHANNEL_RC_OK;
984
985 rdpPersistentCache* persistent = persistent_cache_new();
986
987 if (!persistent)
988 return CHANNEL_RC_NO_MEMORY;
989
990 if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
991 {
992 error = CHANNEL_RC_INITIALIZATION_ERROR;
993 goto fail;
994 }
995
996 if (persistent_cache_get_version(persistent) != 3)
997 {
998 error = ERROR_INVALID_DATA;
999 goto fail;
1000 }
1001
1002 count = persistent_cache_get_count(persistent);
1003 if (count < 0)
1004 {
1005 error = ERROR_INVALID_DATA;
1006 goto fail;
1007 }
1008
1009 if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
1010 count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
1011
1012 if (count > gfx->MaxCacheSlots)
1013 count = gfx->MaxCacheSlots;
1014
1016 if (!offer)
1017 {
1018 error = CHANNEL_RC_NO_MEMORY;
1019 goto fail;
1020 }
1021
1022 WINPR_ASSERT(count <= UINT16_MAX);
1023 offer->cacheEntriesCount = (UINT16)count;
1024
1025 WLog_Print(gfx->base.log, WLOG_DEBUG, "Sending Cache Import Offer: %d", count);
1026
1027 for (int idx = 0; idx < count; idx++)
1028 {
1029 if (persistent_cache_read_entry(persistent, &entry) < 1)
1030 {
1031 error = ERROR_INVALID_DATA;
1032 goto fail;
1033 }
1034
1035 offer->cacheEntries[idx].cacheKey = entry.key64;
1036 offer->cacheEntries[idx].bitmapLength = entry.size;
1037 }
1038
1039 if (offer->cacheEntriesCount > 0)
1040 {
1041 error = rdpgfx_send_cache_import_offer_pdu(context, offer);
1042 if (error != CHANNEL_RC_OK)
1043 {
1044 WLog_Print(gfx->base.log, WLOG_ERROR, "Failed to send cache import offer PDU");
1045 goto fail;
1046 }
1047 }
1048
1049fail:
1050 persistent_cache_free(persistent);
1051 free(offer);
1052 return error;
1053}
1054
1060static UINT rdpgfx_load_cache_import_reply(RDPGFX_PLUGIN* gfx,
1061 const RDPGFX_CACHE_IMPORT_REPLY_PDU* reply)
1062{
1063 UINT error = CHANNEL_RC_OK;
1064 rdpPersistentCache* persistent = nullptr;
1065 WINPR_ASSERT(gfx);
1066 WINPR_ASSERT(gfx->rdpcontext);
1067 rdpSettings* settings = gfx->rdpcontext->settings;
1068 RdpgfxClientContext* context = gfx->context;
1069
1070 WINPR_ASSERT(settings);
1071 WINPR_ASSERT(reply);
1072 if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
1073 return CHANNEL_RC_OK;
1074
1075 const char* BitmapCachePersistFile =
1076 freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
1077 if (!BitmapCachePersistFile)
1078 return CHANNEL_RC_OK;
1079
1080 persistent = persistent_cache_new();
1081
1082 if (!persistent)
1083 return CHANNEL_RC_NO_MEMORY;
1084
1085 if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
1086 {
1087 error = CHANNEL_RC_INITIALIZATION_ERROR;
1088 goto fail;
1089 }
1090
1091 if (persistent_cache_get_version(persistent) != 3)
1092 {
1093 error = ERROR_INVALID_DATA;
1094 goto fail;
1095 }
1096
1097 int count = persistent_cache_get_count(persistent);
1098
1099 count = (count < reply->importedEntriesCount) ? count : reply->importedEntriesCount;
1100
1101 WLog_Print(gfx->base.log, WLOG_DEBUG, "Receiving Cache Import Reply: %d", count);
1102
1103 for (int idx = 0; idx < count; idx++)
1104 {
1105 PERSISTENT_CACHE_ENTRY entry = WINPR_C_ARRAY_INIT;
1106 if (persistent_cache_read_entry(persistent, &entry) < 1)
1107 {
1108 error = ERROR_INVALID_DATA;
1109 goto fail;
1110 }
1111
1112 const UINT16 cacheSlot = reply->cacheSlots[idx];
1113 if (context && context->ImportCacheEntry)
1114 {
1115 error = context->ImportCacheEntry(context, cacheSlot, &entry);
1116 if (error != CHANNEL_RC_OK)
1117 break;
1118 }
1119 }
1120
1121 persistent_cache_free(persistent);
1122
1123 return error;
1124fail:
1125 persistent_cache_free(persistent);
1126 return error;
1127}
1128
1134static UINT rdpgfx_recv_cache_import_reply_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1135{
1136 RDPGFX_CACHE_IMPORT_REPLY_PDU pdu = WINPR_C_ARRAY_INIT;
1137 WINPR_ASSERT(callback);
1138 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1139 WINPR_ASSERT(gfx);
1140 RdpgfxClientContext* context = gfx->context;
1141 UINT error = CHANNEL_RC_OK;
1142
1143 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 2))
1144 return ERROR_INVALID_DATA;
1145
1146 Stream_Read_UINT16(s, pdu.importedEntriesCount); /* cacheSlot (2 bytes) */
1147
1148 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, pdu.importedEntriesCount,
1149 2ull))
1150 return ERROR_INVALID_DATA;
1151
1152 if (pdu.importedEntriesCount > RDPGFX_CACHE_ENTRY_MAX_COUNT)
1153 return ERROR_INVALID_DATA;
1154
1155 for (UINT16 idx = 0; idx < pdu.importedEntriesCount; idx++)
1156 {
1157 Stream_Read_UINT16(s, pdu.cacheSlots[idx]); /* cacheSlot (2 bytes) */
1158 }
1159
1160 WLog_Print(gfx->base.log, WLOG_TRACE,
1161 "RecvCacheImportReplyPdu: importedEntriesCount: %" PRIu16 "",
1162 pdu.importedEntriesCount);
1163
1164 error = rdpgfx_load_cache_import_reply(gfx, &pdu);
1165
1166 if (error)
1167 {
1168 WLog_Print(gfx->base.log, WLOG_ERROR,
1169 "rdpgfx_load_cache_import_reply failed with error %" PRIu32 "", error);
1170 return error;
1171 }
1172
1173 if (context)
1174 {
1175 IFCALLRET(context->CacheImportReply, error, context, &pdu);
1176
1177 if (error)
1178 WLog_Print(gfx->base.log, WLOG_ERROR,
1179 "context->CacheImportReply failed with error %" PRIu32 "", error);
1180 }
1181
1182 return error;
1183}
1184
1190static UINT rdpgfx_recv_create_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1191{
1192 RDPGFX_CREATE_SURFACE_PDU pdu = WINPR_C_ARRAY_INIT;
1193 WINPR_ASSERT(callback);
1194 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1195 WINPR_ASSERT(gfx);
1196 RdpgfxClientContext* context = gfx->context;
1197 UINT error = CHANNEL_RC_OK;
1198
1199 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 7))
1200 return ERROR_INVALID_DATA;
1201
1202 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1203 Stream_Read_UINT16(s, pdu.width); /* width (2 bytes) */
1204 Stream_Read_UINT16(s, pdu.height); /* height (2 bytes) */
1205 Stream_Read_UINT8(s, pdu.pixelFormat); /* RDPGFX_PIXELFORMAT (1 byte) */
1206
1207 if ((pdu.width > MAX_SURFACE_SIZE) || (pdu.height > MAX_SURFACE_SIZE))
1208 return ERROR_INVALID_DATA;
1209
1210 WLog_Print(gfx->base.log, WLOG_DEBUG,
1211 "RecvCreateSurfacePdu: surfaceId: %" PRIu16 " width: %" PRIu16 " height: %" PRIu16
1212 " pixelFormat: 0x%02" PRIX8 "",
1213 pdu.surfaceId, pdu.width, pdu.height, pdu.pixelFormat);
1214
1215 if (context)
1216 {
1217 /* create surface PDU sometimes happens for surface ID that are already in use and have not
1218 * been removed yet. Ensure that there is no surface with the new ID by trying to remove it
1219 * manually.
1220 */
1221 RDPGFX_DELETE_SURFACE_PDU deletePdu = { pdu.surfaceId };
1222 const UINT drc = IFCALLRESULT(CHANNEL_RC_OK, context->DeleteSurface, context, &deletePdu);
1223 if (drc != CHANNEL_RC_OK)
1224 WLog_Print(gfx->base.log, WLOG_WARN,
1225 "context->DeleteSurface failed with error %" PRIu32 ", ignoring", drc);
1226
1227 IFCALLRET(context->CreateSurface, error, context, &pdu);
1228
1229 if (error)
1230 WLog_Print(gfx->base.log, WLOG_ERROR,
1231 "context->CreateSurface failed with error %" PRIu32 "", error);
1232 }
1233
1234 return error;
1235}
1236
1242static UINT rdpgfx_recv_delete_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1243{
1244 RDPGFX_DELETE_SURFACE_PDU pdu = WINPR_C_ARRAY_INIT;
1245 WINPR_ASSERT(callback);
1246 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1247 WINPR_ASSERT(gfx);
1248 RdpgfxClientContext* context = gfx->context;
1249 UINT error = CHANNEL_RC_OK;
1250
1251 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 2))
1252 return ERROR_INVALID_DATA;
1253
1254 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1255 WLog_Print(gfx->base.log, WLOG_DEBUG, "RecvDeleteSurfacePdu: surfaceId: %" PRIu16 "",
1256 pdu.surfaceId);
1257
1258 if (context)
1259 {
1260 IFCALLRET(context->DeleteSurface, error, context, &pdu);
1261
1262 if (error)
1263 WLog_Print(gfx->base.log, WLOG_ERROR,
1264 "context->DeleteSurface failed with error %" PRIu32 "", error);
1265 }
1266
1267 return error;
1268}
1269
1275static UINT rdpgfx_recv_start_frame_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1276{
1277 RDPGFX_START_FRAME_PDU pdu = WINPR_C_ARRAY_INIT;
1278 WINPR_ASSERT(callback);
1279 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1280 WINPR_ASSERT(gfx);
1281 RdpgfxClientContext* context = gfx->context;
1282 UINT error = CHANNEL_RC_OK;
1283
1284 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, RDPGFX_START_FRAME_PDU_SIZE))
1285 return ERROR_INVALID_DATA;
1286
1287 Stream_Read_UINT32(s, pdu.timestamp); /* timestamp (4 bytes) */
1288 Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
1289 WLog_Print(gfx->base.log, WLOG_TRACE,
1290 "RecvStartFramePdu: frameId: %" PRIu32 " timestamp: 0x%08" PRIX32 "", pdu.frameId,
1291 pdu.timestamp);
1292 gfx->StartDecodingTime = GetTickCount64();
1293
1294 if (context)
1295 {
1296 IFCALLRET(context->StartFrame, error, context, &pdu);
1297
1298 if (error)
1299 WLog_Print(gfx->base.log, WLOG_ERROR,
1300 "context->StartFrame failed with error %" PRIu32 "", error);
1301 }
1302
1303 gfx->UnacknowledgedFrames++;
1304 return error;
1305}
1306
1312static UINT rdpgfx_recv_end_frame_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1313{
1314 RDPGFX_END_FRAME_PDU pdu = WINPR_C_ARRAY_INIT;
1315 RDPGFX_FRAME_ACKNOWLEDGE_PDU ack = WINPR_C_ARRAY_INIT;
1316 WINPR_ASSERT(callback);
1317 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1318 WINPR_ASSERT(gfx);
1319 RdpgfxClientContext* context = gfx->context;
1320 UINT error = CHANNEL_RC_OK;
1321
1322 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, RDPGFX_END_FRAME_PDU_SIZE))
1323 return ERROR_INVALID_DATA;
1324
1325 Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
1326 WLog_Print(gfx->base.log, WLOG_TRACE, "RecvEndFramePdu: frameId: %" PRIu32 "", pdu.frameId);
1327
1328 const UINT64 start = GetTickCount64();
1329 if (context)
1330 {
1331 IFCALLRET(context->EndFrame, error, context, &pdu);
1332
1333 if (error)
1334 {
1335 WLog_Print(gfx->base.log, WLOG_ERROR, "context->EndFrame failed with error %" PRIu32 "",
1336 error);
1337 return error;
1338 }
1339 }
1340 const UINT64 end = GetTickCount64();
1341 const UINT64 EndFrameTime = end - start;
1342 gfx->TotalDecodedFrames++;
1343
1344 if (!gfx->sendFrameAcks)
1345 return error;
1346
1347 ack.frameId = pdu.frameId;
1348 ack.totalFramesDecoded = gfx->TotalDecodedFrames;
1349
1350 if (gfx->suspendFrameAcks)
1351 {
1352 ack.queueDepth = SUSPEND_FRAME_ACKNOWLEDGEMENT;
1353
1354 if (gfx->TotalDecodedFrames == 1)
1355 if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
1356 WLog_Print(gfx->base.log, WLOG_ERROR,
1357 "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 "",
1358 error);
1359 }
1360 else
1361 {
1362 ack.queueDepth = QUEUE_DEPTH_UNAVAILABLE;
1363
1364 if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
1365 WLog_Print(gfx->base.log, WLOG_ERROR,
1366 "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 "", error);
1367 }
1368
1369 switch (gfx->ConnectionCaps.version)
1370 {
1371#if defined(WITH_GFX_AV1)
1372 case RDPGFX_CAPVERSION_FRDP_1:
1373#endif
1374 case RDPGFX_CAPVERSION_10:
1375 case RDPGFX_CAPVERSION_102:
1376 case RDPGFX_CAPVERSION_103:
1377 case RDPGFX_CAPVERSION_104:
1378 case RDPGFX_CAPVERSION_105:
1379 case RDPGFX_CAPVERSION_106:
1380 case RDPGFX_CAPVERSION_106_ERR:
1381 case RDPGFX_CAPVERSION_107:
1382#if defined(WITH_GFX_AZURE)
1383 case RDPGFX_CAPVERSION_111:
1384 case RDPGFX_CAPVERSION_112:
1385 case RDPGFX_CAPVERSION_113:
1386#endif
1387 if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSendQoeAck))
1388 {
1389 RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe = WINPR_C_ARRAY_INIT;
1390 UINT64 diff = (GetTickCount64() - gfx->StartDecodingTime);
1391
1392 if (diff > 65000)
1393 diff = 0;
1394
1395 qoe.frameId = pdu.frameId;
1396 qoe.timestamp = gfx->StartDecodingTime % UINT32_MAX;
1397 qoe.timeDiffSE = WINPR_ASSERTING_INT_CAST(UINT16, diff);
1398 qoe.timeDiffEDR = WINPR_ASSERTING_INT_CAST(UINT16, EndFrameTime);
1399
1400 if ((error = rdpgfx_send_qoe_frame_acknowledge_pdu(context, &qoe)))
1401 WLog_Print(gfx->base.log, WLOG_ERROR,
1402 "rdpgfx_send_qoe_frame_acknowledge_pdu failed with error %" PRIu32
1403 "",
1404 error);
1405 }
1406
1407 break;
1408
1409 default:
1410 break;
1411 }
1412
1413 return error;
1414}
1415
1421static UINT rdpgfx_recv_wire_to_surface_1_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1422{
1423 RDPGFX_SURFACE_COMMAND cmd = WINPR_C_ARRAY_INIT;
1424 RDPGFX_WIRE_TO_SURFACE_PDU_1 pdu = WINPR_C_ARRAY_INIT;
1425 WINPR_ASSERT(callback);
1426 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1427 UINT error = 0;
1428
1429 WINPR_ASSERT(gfx);
1430 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE))
1431 return ERROR_INVALID_DATA;
1432
1433 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1434 Stream_Read_UINT16(s, pdu.codecId); /* codecId (2 bytes) */
1435 Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */
1436
1437 if ((error = rdpgfx_read_rect16(gfx->base.log, s, &(pdu.destRect)))) /* destRect (8 bytes) */
1438 {
1439 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "",
1440 error);
1441 return error;
1442 }
1443
1444 Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */
1445
1446 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, pdu.bitmapDataLength))
1447 return ERROR_INVALID_DATA;
1448
1449 pdu.bitmapData = Stream_Pointer(s);
1450 Stream_Seek(s, pdu.bitmapDataLength);
1451
1452 WLog_Print(gfx->base.log, WLOG_TRACE,
1453 "RecvWireToSurface1Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16
1454 ") pixelFormat: 0x%02" PRIX8 " "
1455 "destRect: left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16
1456 " bitmapDataLength: %" PRIu32 "",
1457 pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId, pdu.pixelFormat,
1458 pdu.destRect.left, pdu.destRect.top, pdu.destRect.right, pdu.destRect.bottom,
1459 pdu.bitmapDataLength);
1460 cmd.surfaceId = pdu.surfaceId;
1461 cmd.codecId = pdu.codecId;
1462 cmd.contextId = 0;
1463
1464 switch (pdu.pixelFormat)
1465 {
1466 case GFX_PIXEL_FORMAT_XRGB_8888:
1467 cmd.format = PIXEL_FORMAT_BGRX32;
1468 break;
1469
1470 case GFX_PIXEL_FORMAT_ARGB_8888:
1471 cmd.format = PIXEL_FORMAT_BGRA32;
1472 break;
1473
1474 default:
1475 return ERROR_INVALID_DATA;
1476 }
1477
1478 cmd.left = pdu.destRect.left;
1479 cmd.top = pdu.destRect.top;
1480 cmd.right = pdu.destRect.right;
1481 cmd.bottom = pdu.destRect.bottom;
1482 cmd.width = cmd.right - cmd.left;
1483 cmd.height = cmd.bottom - cmd.top;
1484 cmd.length = pdu.bitmapDataLength;
1485 cmd.data = pdu.bitmapData;
1486 cmd.extra = nullptr;
1487
1488 if (cmd.right < cmd.left)
1489 {
1490 WLog_Print(gfx->base.log, WLOG_ERROR,
1491 "RecvWireToSurface1Pdu right=%" PRIu32 " < left=%" PRIu32, cmd.right, cmd.left);
1492 return ERROR_INVALID_DATA;
1493 }
1494 if (cmd.bottom < cmd.top)
1495 {
1496 WLog_Print(gfx->base.log, WLOG_ERROR,
1497 "RecvWireToSurface1Pdu bottom=%" PRIu32 " < top=%" PRIu32, cmd.bottom, cmd.top);
1498 return ERROR_INVALID_DATA;
1499 }
1500
1501 if ((error = rdpgfx_decode(gfx, &cmd)))
1502 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!",
1503 error);
1504
1505 return error;
1506}
1507
1508WINPR_ATTR_NODISCARD
1509static BOOL checkSurfaceCommand(const RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
1510{
1511 switch (cmd->codecId)
1512 {
1513 case RDPGFX_CODECID_UNCOMPRESSED:
1514 case RDPGFX_CODECID_CAVIDEO:
1515 case RDPGFX_CODECID_CLEARCODEC:
1516 case RDPGFX_CODECID_PLANAR:
1517 case RDPGFX_CODECID_AVC420:
1518 case RDPGFX_CODECID_ALPHA:
1519 case RDPGFX_CODECID_AVC444:
1520 case RDPGFX_CODECID_AVC444v2:
1521 case RDPGFX_CODECID_CAPROGRESSIVE:
1522 case RDPGFX_CODECID_CAPROGRESSIVE_V2:
1523 return TRUE;
1524#if defined(WITH_GFX_AV1)
1525 case RDPGFX_CODECID_AV1:
1526 if (gfx->ConnectionCaps.version != RDPGFX_CAPVERSION_FRDP_1)
1527 {
1528 WLog_Print(gfx->base.log, WLOG_ERROR,
1529 "RDPGFX_SURFACE_COMMAND::codecId %" PRIu32
1530 " only supported with %s but connection uses %s [0x%08" PRIx32 "]",
1531 cmd->codecId, rdpgfx_caps_version_str(RDPGFX_CAPVERSION_FRDP_1),
1532 rdpgfx_caps_version_str(gfx->ConnectionCaps.version),
1533 gfx->ConnectionCaps.version);
1534 return FALSE;
1535 }
1536 return TRUE;
1537#endif
1538 default:
1539 WLog_Print(gfx->base.log, WLOG_ERROR,
1540 "Unknown RDPGFX_SURFACE_COMMAND::codecId %" PRIu32, cmd->codecId);
1541 return FALSE;
1542 }
1543}
1544
1545UINT logSurfaceCommand(RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
1546{
1547 WINPR_ASSERT(gfx);
1548
1549 WLog_Print(gfx->base.log, WLOG_DEBUG, "Got GFX %s",
1550 rdpgfx_get_codec_id_string(WINPR_ASSERTING_INT_CAST(UINT16, cmd->codecId)));
1551
1552 RdpgfxClientContext* context = gfx->context;
1553 if (!context)
1554 return CHANNEL_RC_OK;
1555
1556 if (!checkSurfaceCommand(gfx, cmd))
1557 return CHANNEL_RC_NULL_DATA;
1558
1559 const UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->SurfaceCommand, context, cmd);
1560
1561 if (error)
1562 WLog_Print(gfx->base.log, WLOG_ERROR,
1563 "context->SurfaceCommand failed with error %" PRIu32 "", error);
1564
1565 rdpgfx_codecid_event(context, cmd->codecId);
1566 return error;
1567}
1568
1574static UINT rdpgfx_recv_wire_to_surface_2_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1575{
1576 RDPGFX_SURFACE_COMMAND cmd = WINPR_C_ARRAY_INIT;
1577 RDPGFX_WIRE_TO_SURFACE_PDU_2 pdu = WINPR_C_ARRAY_INIT;
1578
1579 WINPR_ASSERT(callback);
1580 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1581 WINPR_ASSERT(gfx);
1582
1583 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE))
1584 return ERROR_INVALID_DATA;
1585
1586 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1587 Stream_Read_UINT16(s, pdu.codecId); /* codecId (2 bytes) */
1588 Stream_Read_UINT32(s, pdu.codecContextId); /* codecContextId (4 bytes) */
1589 Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */
1590 Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */
1591 pdu.bitmapData = Stream_Pointer(s);
1592 if (!Stream_SafeSeek(s, pdu.bitmapDataLength))
1593 return ERROR_INVALID_DATA;
1594
1595 WLog_Print(gfx->base.log, WLOG_TRACE,
1596 "RecvWireToSurface2Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 ") "
1597 "codecContextId: %" PRIu32 " pixelFormat: 0x%02" PRIX8 " bitmapDataLength: %" PRIu32
1598 "",
1599 pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId,
1600 pdu.codecContextId, pdu.pixelFormat, pdu.bitmapDataLength);
1601
1602 cmd.surfaceId = pdu.surfaceId;
1603 cmd.codecId = pdu.codecId;
1604 cmd.contextId = pdu.codecContextId;
1605
1606 switch (pdu.pixelFormat)
1607 {
1608 case GFX_PIXEL_FORMAT_XRGB_8888:
1609 cmd.format = PIXEL_FORMAT_BGRX32;
1610 break;
1611
1612 case GFX_PIXEL_FORMAT_ARGB_8888:
1613 cmd.format = PIXEL_FORMAT_BGRA32;
1614 break;
1615
1616 default:
1617 return ERROR_INVALID_DATA;
1618 }
1619
1620 cmd.length = pdu.bitmapDataLength;
1621 cmd.data = pdu.bitmapData;
1622 cmd.extra = nullptr;
1623
1624 return logSurfaceCommand(gfx, &cmd);
1625}
1626
1632static UINT rdpgfx_recv_delete_encoding_context_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1633{
1634 RDPGFX_DELETE_ENCODING_CONTEXT_PDU pdu = WINPR_C_ARRAY_INIT;
1635 WINPR_ASSERT(callback);
1636 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1637 WINPR_ASSERT(gfx);
1638 RdpgfxClientContext* context = gfx->context;
1639 UINT error = CHANNEL_RC_OK;
1640
1641 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 6))
1642 return ERROR_INVALID_DATA;
1643
1644 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1645 Stream_Read_UINT32(s, pdu.codecContextId); /* codecContextId (4 bytes) */
1646
1647 WLog_Print(gfx->base.log, WLOG_DEBUG,
1648 "RecvDeleteEncodingContextPdu: surfaceId: %" PRIu16 " codecContextId: %" PRIu32 "",
1649 pdu.surfaceId, pdu.codecContextId);
1650
1651 if (context)
1652 {
1653 IFCALLRET(context->DeleteEncodingContext, error, context, &pdu);
1654
1655 if (error)
1656 WLog_Print(gfx->base.log, WLOG_ERROR,
1657 "context->DeleteEncodingContext failed with error %" PRIu32 "", error);
1658 }
1659
1660 return error;
1661}
1662
1668static UINT rdpgfx_recv_solid_fill_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1669{
1670 RDPGFX_SOLID_FILL_PDU pdu = WINPR_C_ARRAY_INIT;
1671 WINPR_ASSERT(callback);
1672 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1673 WINPR_ASSERT(gfx);
1674 RdpgfxClientContext* context = gfx->context;
1675
1676 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 8))
1677 return ERROR_INVALID_DATA;
1678
1679 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1680
1681 UINT error = rdpgfx_read_color32(gfx->base.log, s, &(pdu.fillPixel));
1682 if (error != CHANNEL_RC_OK) /* fillPixel (4 bytes) */
1683 {
1684 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_read_color32 failed with error %" PRIu32 "!",
1685 error);
1686 return error;
1687 }
1688
1689 Stream_Read_UINT16(s, pdu.fillRectCount); /* fillRectCount (2 bytes) */
1690
1691 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, pdu.fillRectCount, 8ull))
1692 return ERROR_INVALID_DATA;
1693
1694 pdu.fillRects = (RECTANGLE_16*)calloc(pdu.fillRectCount, sizeof(RECTANGLE_16));
1695
1696 if (!pdu.fillRects)
1697 {
1698 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
1699 return CHANNEL_RC_NO_MEMORY;
1700 }
1701
1702 for (UINT16 index = 0; index < pdu.fillRectCount; index++)
1703 {
1704 RECTANGLE_16* fillRect = &(pdu.fillRects[index]);
1705
1706 if ((error = rdpgfx_read_rect16(gfx->base.log, s, fillRect)))
1707 {
1708 WLog_Print(gfx->base.log, WLOG_ERROR,
1709 "rdpgfx_read_rect16 failed with error %" PRIu32 "!", error);
1710 free(pdu.fillRects);
1711 return error;
1712 }
1713 }
1714 WLog_Print(gfx->base.log, WLOG_TRACE,
1715 "RecvSolidFillPdu: surfaceId: %" PRIu16 " fillRectCount: %" PRIu16 "", pdu.surfaceId,
1716 pdu.fillRectCount);
1717
1718 if (context)
1719 {
1720 IFCALLRET(context->SolidFill, error, context, &pdu);
1721
1722 if (error)
1723 WLog_Print(gfx->base.log, WLOG_ERROR,
1724 "context->SolidFill failed with error %" PRIu32 "", error);
1725 }
1726
1727 free(pdu.fillRects);
1728 return error;
1729}
1730
1736static UINT rdpgfx_recv_surface_to_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1737{
1738 RDPGFX_SURFACE_TO_SURFACE_PDU pdu = WINPR_C_ARRAY_INIT;
1739 WINPR_ASSERT(callback);
1740 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1741 WINPR_ASSERT(gfx);
1742 RdpgfxClientContext* context = gfx->context;
1743 UINT error = 0;
1744
1745 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 14))
1746 return ERROR_INVALID_DATA;
1747
1748 Stream_Read_UINT16(s, pdu.surfaceIdSrc); /* surfaceIdSrc (2 bytes) */
1749 Stream_Read_UINT16(s, pdu.surfaceIdDest); /* surfaceIdDest (2 bytes) */
1750
1751 if ((error = rdpgfx_read_rect16(gfx->base.log, s, &(pdu.rectSrc)))) /* rectSrc (8 bytes ) */
1752 {
1753 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "!",
1754 error);
1755 return error;
1756 }
1757
1758 Stream_Read_UINT16(s, pdu.destPtsCount); /* destPtsCount (2 bytes) */
1759
1760 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, pdu.destPtsCount, 4ull))
1761 return ERROR_INVALID_DATA;
1762
1763 pdu.destPts = (RDPGFX_POINT16*)calloc(pdu.destPtsCount, sizeof(RDPGFX_POINT16));
1764
1765 if (!pdu.destPts)
1766 {
1767 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
1768 return CHANNEL_RC_NO_MEMORY;
1769 }
1770
1771 for (UINT16 index = 0; index < pdu.destPtsCount; index++)
1772 {
1773 RDPGFX_POINT16* destPt = &(pdu.destPts[index]);
1774
1775 if ((error = rdpgfx_read_point16(gfx->base.log, s, destPt)))
1776 {
1777 WLog_Print(gfx->base.log, WLOG_ERROR,
1778 "rdpgfx_read_point16 failed with error %" PRIu32 "!", error);
1779 free(pdu.destPts);
1780 return error;
1781 }
1782 }
1783
1784 WLog_Print(gfx->base.log, WLOG_TRACE,
1785 "RecvSurfaceToSurfacePdu: surfaceIdSrc: %" PRIu16 " surfaceIdDest: %" PRIu16 " "
1786 "left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16
1787 " destPtsCount: %" PRIu16 "",
1788 pdu.surfaceIdSrc, pdu.surfaceIdDest, pdu.rectSrc.left, pdu.rectSrc.top,
1789 pdu.rectSrc.right, pdu.rectSrc.bottom, pdu.destPtsCount);
1790
1791 if (context)
1792 {
1793 IFCALLRET(context->SurfaceToSurface, error, context, &pdu);
1794
1795 if (error)
1796 WLog_Print(gfx->base.log, WLOG_ERROR,
1797 "context->SurfaceToSurface failed with error %" PRIu32 "", error);
1798 }
1799
1800 free(pdu.destPts);
1801 return error;
1802}
1803
1809static UINT rdpgfx_recv_surface_to_cache_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1810{
1811 RDPGFX_SURFACE_TO_CACHE_PDU pdu = WINPR_C_ARRAY_INIT;
1812 WINPR_ASSERT(callback);
1813 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1814
1815 WINPR_ASSERT(gfx);
1816 RdpgfxClientContext* context = gfx->context;
1817
1818 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 20))
1819 return ERROR_INVALID_DATA;
1820
1821 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1822 Stream_Read_UINT64(s, pdu.cacheKey); /* cacheKey (8 bytes) */
1823 Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
1824
1825 UINT error = rdpgfx_read_rect16(gfx->base.log, s, &(pdu.rectSrc));
1826 if (error != CHANNEL_RC_OK) /* rectSrc (8 bytes ) */
1827 {
1828 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "!",
1829 error);
1830 return error;
1831 }
1832
1833 WLog_Print(gfx->base.log, WLOG_TRACE,
1834 "RecvSurfaceToCachePdu: surfaceId: %" PRIu16 " cacheKey: 0x%016" PRIX64
1835 " cacheSlot: %" PRIu16 " "
1836 "left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16 "",
1837 pdu.surfaceId, pdu.cacheKey, pdu.cacheSlot, pdu.rectSrc.left, pdu.rectSrc.top,
1838 pdu.rectSrc.right, pdu.rectSrc.bottom);
1839
1840 if (context)
1841 {
1842 IFCALLRET(context->SurfaceToCache, error, context, &pdu);
1843
1844 if (error)
1845 WLog_Print(gfx->base.log, WLOG_ERROR,
1846 "context->SurfaceToCache failed with error %" PRIu32 "", error);
1847 }
1848
1849 return error;
1850}
1851
1857static UINT rdpgfx_recv_cache_to_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1858{
1859 RDPGFX_CACHE_TO_SURFACE_PDU pdu = WINPR_C_ARRAY_INIT;
1860 WINPR_ASSERT(callback);
1861 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1862
1863 WINPR_ASSERT(gfx);
1864 RdpgfxClientContext* context = gfx->context;
1865 UINT error = CHANNEL_RC_OK;
1866
1867 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 6))
1868 return ERROR_INVALID_DATA;
1869
1870 Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
1871 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1872 Stream_Read_UINT16(s, pdu.destPtsCount); /* destPtsCount (2 bytes) */
1873
1874 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, pdu.destPtsCount, 4ull))
1875 return ERROR_INVALID_DATA;
1876
1877 pdu.destPts = (RDPGFX_POINT16*)calloc(pdu.destPtsCount, sizeof(RDPGFX_POINT16));
1878
1879 if (!pdu.destPts)
1880 {
1881 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
1882 return CHANNEL_RC_NO_MEMORY;
1883 }
1884
1885 for (UINT16 index = 0; index < pdu.destPtsCount; index++)
1886 {
1887 RDPGFX_POINT16* destPt = &(pdu.destPts[index]);
1888
1889 if ((error = rdpgfx_read_point16(gfx->base.log, s, destPt)))
1890 {
1891 WLog_Print(gfx->base.log, WLOG_ERROR,
1892 "rdpgfx_read_point16 failed with error %" PRIu32 "", error);
1893 free(pdu.destPts);
1894 return error;
1895 }
1896 }
1897
1898 WLog_Print(gfx->base.log, WLOG_TRACE,
1899 "RdpGfxRecvCacheToSurfacePdu: cacheSlot: %" PRIu16 " surfaceId: %" PRIu16
1900 " destPtsCount: %" PRIu16 "",
1901 pdu.cacheSlot, pdu.surfaceId, pdu.destPtsCount);
1902
1903 if (context)
1904 {
1905 IFCALLRET(context->CacheToSurface, error, context, &pdu);
1906
1907 if (error)
1908 WLog_Print(gfx->base.log, WLOG_ERROR,
1909 "context->CacheToSurface failed with error %" PRIu32 "", error);
1910 }
1911
1912 free(pdu.destPts);
1913 return error;
1914}
1915
1921static UINT rdpgfx_recv_map_surface_to_output_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1922{
1923 RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU pdu = WINPR_C_ARRAY_INIT;
1924 WINPR_ASSERT(callback);
1925 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1926
1927 WINPR_ASSERT(gfx);
1928 RdpgfxClientContext* context = gfx->context;
1929 UINT error = CHANNEL_RC_OK;
1930
1931 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 12))
1932 return ERROR_INVALID_DATA;
1933
1934 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1935 Stream_Read_UINT16(s, pdu.reserved); /* reserved (2 bytes) */
1936 Stream_Read_UINT32(s, pdu.outputOriginX); /* outputOriginX (4 bytes) */
1937 Stream_Read_UINT32(s, pdu.outputOriginY); /* outputOriginY (4 bytes) */
1938 WLog_Print(gfx->base.log, WLOG_DEBUG,
1939 "RecvMapSurfaceToOutputPdu: surfaceId: %" PRIu16 " outputOriginX: %" PRIu32
1940 " outputOriginY: %" PRIu32 "",
1941 pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY);
1942
1943 if (context)
1944 {
1945 IFCALLRET(context->MapSurfaceToOutput, error, context, &pdu);
1946
1947 if (error)
1948 WLog_Print(gfx->base.log, WLOG_ERROR,
1949 "context->MapSurfaceToOutput failed with error %" PRIu32 "", error);
1950 }
1951
1952 return error;
1953}
1954
1955static UINT rdpgfx_recv_map_surface_to_scaled_output_pdu(GENERIC_CHANNEL_CALLBACK* callback,
1956 wStream* s)
1957{
1958 RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU pdu = WINPR_C_ARRAY_INIT;
1959 WINPR_ASSERT(callback);
1960 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1961
1962 WINPR_ASSERT(gfx);
1963 RdpgfxClientContext* context = gfx->context;
1964 UINT error = CHANNEL_RC_OK;
1965
1966 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 20))
1967 return ERROR_INVALID_DATA;
1968
1969 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1970 Stream_Read_UINT16(s, pdu.reserved); /* reserved (2 bytes) */
1971 Stream_Read_UINT32(s, pdu.outputOriginX); /* outputOriginX (4 bytes) */
1972 Stream_Read_UINT32(s, pdu.outputOriginY); /* outputOriginY (4 bytes) */
1973 Stream_Read_UINT32(s, pdu.targetWidth); /* targetWidth (4 bytes) */
1974 Stream_Read_UINT32(s, pdu.targetHeight); /* targetHeight (4 bytes) */
1975 WLog_Print(gfx->base.log, WLOG_DEBUG,
1976 "RecvMapSurfaceToScaledOutputPdu: surfaceId: %" PRIu16 " outputOriginX: %" PRIu32
1977 " outputOriginY: %" PRIu32 " targetWidth: %" PRIu32 " targetHeight: %" PRIu32,
1978 pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY, pdu.targetWidth,
1979 pdu.targetHeight);
1980
1981 if (context)
1982 {
1983 IFCALLRET(context->MapSurfaceToScaledOutput, error, context, &pdu);
1984
1985 if (error)
1986 WLog_Print(gfx->base.log, WLOG_ERROR,
1987 "context->MapSurfaceToScaledOutput failed with error %" PRIu32 "", error);
1988 }
1989
1990 return error;
1991}
1992
1998static UINT rdpgfx_recv_map_surface_to_window_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1999{
2000 RDPGFX_MAP_SURFACE_TO_WINDOW_PDU pdu = WINPR_C_ARRAY_INIT;
2001 WINPR_ASSERT(callback);
2002 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2003
2004 WINPR_ASSERT(gfx);
2005 RdpgfxClientContext* context = gfx->context;
2006 UINT error = CHANNEL_RC_OK;
2007
2008 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 18))
2009 return ERROR_INVALID_DATA;
2010
2011 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
2012 Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
2013 Stream_Read_UINT32(s, pdu.mappedWidth); /* mappedWidth (4 bytes) */
2014 Stream_Read_UINT32(s, pdu.mappedHeight); /* mappedHeight (4 bytes) */
2015 WLog_Print(gfx->base.log, WLOG_DEBUG,
2016 "RecvMapSurfaceToWindowPdu: surfaceId: %" PRIu16 " windowId: 0x%016" PRIX64
2017 " mappedWidth: %" PRIu32 " mappedHeight: %" PRIu32 "",
2018 pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight);
2019
2020 if (context && context->MapSurfaceToWindow)
2021 {
2022 IFCALLRET(context->MapSurfaceToWindow, error, context, &pdu);
2023
2024 if (error)
2025 WLog_Print(gfx->base.log, WLOG_ERROR,
2026 "context->MapSurfaceToWindow failed with error %" PRIu32 "", error);
2027 }
2028
2029 return error;
2030}
2031
2032static UINT rdpgfx_recv_map_surface_to_scaled_window_pdu(GENERIC_CHANNEL_CALLBACK* callback,
2033 wStream* s)
2034{
2035 RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU pdu = WINPR_C_ARRAY_INIT;
2036 WINPR_ASSERT(callback);
2037 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2038 WINPR_ASSERT(gfx);
2039 RdpgfxClientContext* context = gfx->context;
2040 UINT error = CHANNEL_RC_OK;
2041
2042 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 26))
2043 return ERROR_INVALID_DATA;
2044
2045 Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
2046 Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
2047 Stream_Read_UINT32(s, pdu.mappedWidth); /* mappedWidth (4 bytes) */
2048 Stream_Read_UINT32(s, pdu.mappedHeight); /* mappedHeight (4 bytes) */
2049 Stream_Read_UINT32(s, pdu.targetWidth); /* targetWidth (4 bytes) */
2050 Stream_Read_UINT32(s, pdu.targetHeight); /* targetHeight (4 bytes) */
2051 WLog_Print(gfx->base.log, WLOG_DEBUG,
2052 "RecvMapSurfaceToScaledWindowPdu: surfaceId: %" PRIu16 " windowId: 0x%016" PRIX64
2053 " mappedWidth: %" PRIu32 " mappedHeight: %" PRIu32 " targetWidth: %" PRIu32
2054 " targetHeight: %" PRIu32 "",
2055 pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight, pdu.targetWidth,
2056 pdu.targetHeight);
2057
2058 if (context && context->MapSurfaceToScaledWindow)
2059 {
2060 IFCALLRET(context->MapSurfaceToScaledWindow, error, context, &pdu);
2061
2062 if (error)
2063 WLog_Print(gfx->base.log, WLOG_ERROR,
2064 "context->MapSurfaceToScaledWindow failed with error %" PRIu32 "", error);
2065 }
2066
2067 return error;
2068}
2069
2075static UINT rdpgfx_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
2076{
2077 RDPGFX_HEADER header = WINPR_C_ARRAY_INIT;
2078
2079 WINPR_ASSERT(callback);
2080 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2081 const size_t beg = Stream_GetPosition(s);
2082
2083 WINPR_ASSERT(gfx);
2084
2085 size_t packetlen = 0;
2086 UINT error = rdpgfx_read_header(gfx->base.log, s, &header, &packetlen);
2087 if (error != CHANNEL_RC_OK)
2088 {
2089 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_read_header failed with error %" PRIu32 "!",
2090 error);
2091 return error;
2092 }
2093
2094 WLog_Print(gfx->base.log, WLOG_TRACE,
2095 "cmdId: %s (0x%04" PRIX16 ") flags: 0x%04" PRIX16 " pduLength: %" PRIu32 "",
2096 rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId, header.flags,
2097 header.pduLength);
2098
2099 rdpgfx_stats_cmdid_event(gfx->context, header.cmdId);
2100 switch (header.cmdId)
2101 {
2102 case RDPGFX_CMDID_WIRETOSURFACE_1:
2103 if ((error = rdpgfx_recv_wire_to_surface_1_pdu(callback, s)))
2104 WLog_Print(gfx->base.log, WLOG_ERROR,
2105 "rdpgfx_recv_wire_to_surface_1_pdu failed with error %" PRIu32 "!",
2106 error);
2107
2108 break;
2109
2110 case RDPGFX_CMDID_WIRETOSURFACE_2:
2111 if ((error = rdpgfx_recv_wire_to_surface_2_pdu(callback, s)))
2112 WLog_Print(gfx->base.log, WLOG_ERROR,
2113 "rdpgfx_recv_wire_to_surface_2_pdu failed with error %" PRIu32 "!",
2114 error);
2115
2116 break;
2117
2118 case RDPGFX_CMDID_DELETEENCODINGCONTEXT:
2119 if ((error = rdpgfx_recv_delete_encoding_context_pdu(callback, s)))
2120 WLog_Print(gfx->base.log, WLOG_ERROR,
2121 "rdpgfx_recv_delete_encoding_context_pdu failed with error %" PRIu32 "!",
2122 error);
2123
2124 break;
2125
2126 case RDPGFX_CMDID_SOLIDFILL:
2127 if ((error = rdpgfx_recv_solid_fill_pdu(callback, s)))
2128 WLog_Print(gfx->base.log, WLOG_ERROR,
2129 "rdpgfx_recv_solid_fill_pdu failed with error %" PRIu32 "!", error);
2130
2131 break;
2132
2133 case RDPGFX_CMDID_SURFACETOSURFACE:
2134 if ((error = rdpgfx_recv_surface_to_surface_pdu(callback, s)))
2135 WLog_Print(gfx->base.log, WLOG_ERROR,
2136 "rdpgfx_recv_surface_to_surface_pdu failed with error %" PRIu32 "!",
2137 error);
2138
2139 break;
2140
2141 case RDPGFX_CMDID_SURFACETOCACHE:
2142 if ((error = rdpgfx_recv_surface_to_cache_pdu(callback, s)))
2143 WLog_Print(gfx->base.log, WLOG_ERROR,
2144 "rdpgfx_recv_surface_to_cache_pdu failed with error %" PRIu32 "!",
2145 error);
2146
2147 break;
2148
2149 case RDPGFX_CMDID_CACHETOSURFACE:
2150 if ((error = rdpgfx_recv_cache_to_surface_pdu(callback, s)))
2151 WLog_Print(gfx->base.log, WLOG_ERROR,
2152 "rdpgfx_recv_cache_to_surface_pdu failed with error %" PRIu32 "!",
2153 error);
2154
2155 break;
2156
2157 case RDPGFX_CMDID_EVICTCACHEENTRY:
2158 if ((error = rdpgfx_recv_evict_cache_entry_pdu(callback, s)))
2159 WLog_Print(gfx->base.log, WLOG_ERROR,
2160 "rdpgfx_recv_evict_cache_entry_pdu failed with error %" PRIu32 "!",
2161 error);
2162
2163 break;
2164
2165 case RDPGFX_CMDID_CREATESURFACE:
2166 if ((error = rdpgfx_recv_create_surface_pdu(callback, s)))
2167 WLog_Print(gfx->base.log, WLOG_ERROR,
2168 "rdpgfx_recv_create_surface_pdu failed with error %" PRIu32 "!", error);
2169
2170 break;
2171
2172 case RDPGFX_CMDID_DELETESURFACE:
2173 if ((error = rdpgfx_recv_delete_surface_pdu(callback, s)))
2174 WLog_Print(gfx->base.log, WLOG_ERROR,
2175 "rdpgfx_recv_delete_surface_pdu failed with error %" PRIu32 "!", error);
2176
2177 break;
2178
2179 case RDPGFX_CMDID_STARTFRAME:
2180 if ((error = rdpgfx_recv_start_frame_pdu(callback, s)))
2181 WLog_Print(gfx->base.log, WLOG_ERROR,
2182 "rdpgfx_recv_start_frame_pdu failed with error %" PRIu32 "!", error);
2183
2184 break;
2185
2186 case RDPGFX_CMDID_ENDFRAME:
2187 if ((error = rdpgfx_recv_end_frame_pdu(callback, s)))
2188 WLog_Print(gfx->base.log, WLOG_ERROR,
2189 "rdpgfx_recv_end_frame_pdu failed with error %" PRIu32 "!", error);
2190
2191 break;
2192
2193 case RDPGFX_CMDID_RESETGRAPHICS:
2194 if ((error = rdpgfx_recv_reset_graphics_pdu(callback, s)))
2195 WLog_Print(gfx->base.log, WLOG_ERROR,
2196 "rdpgfx_recv_reset_graphics_pdu failed with error %" PRIu32 "!", error);
2197
2198 break;
2199
2200 case RDPGFX_CMDID_MAPSURFACETOOUTPUT:
2201 if ((error = rdpgfx_recv_map_surface_to_output_pdu(callback, s)))
2202 WLog_Print(gfx->base.log, WLOG_ERROR,
2203 "rdpgfx_recv_map_surface_to_output_pdu failed with error %" PRIu32 "!",
2204 error);
2205
2206 break;
2207
2208 case RDPGFX_CMDID_CACHEIMPORTREPLY:
2209 if ((error = rdpgfx_recv_cache_import_reply_pdu(callback, s)))
2210 WLog_Print(gfx->base.log, WLOG_ERROR,
2211 "rdpgfx_recv_cache_import_reply_pdu failed with error %" PRIu32 "!",
2212 error);
2213
2214 break;
2215
2216 case RDPGFX_CMDID_CAPSCONFIRM:
2217 if ((error = rdpgfx_recv_caps_confirm_pdu(callback, s)))
2218 WLog_Print(gfx->base.log, WLOG_ERROR,
2219 "rdpgfx_recv_caps_confirm_pdu failed with error %" PRIu32 "!", error);
2220
2221 if ((error = rdpgfx_send_cache_offer(gfx)))
2222 WLog_Print(gfx->base.log, WLOG_ERROR,
2223 "rdpgfx_send_cache_offer failed with error %" PRIu32 "!", error);
2224
2225 break;
2226
2227 case RDPGFX_CMDID_MAPSURFACETOWINDOW:
2228 if ((error = rdpgfx_recv_map_surface_to_window_pdu(callback, s)))
2229 WLog_Print(gfx->base.log, WLOG_ERROR,
2230 "rdpgfx_recv_map_surface_to_window_pdu failed with error %" PRIu32 "!",
2231 error);
2232
2233 break;
2234
2235 case RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW:
2236 if ((error = rdpgfx_recv_map_surface_to_scaled_window_pdu(callback, s)))
2237 WLog_Print(gfx->base.log, WLOG_ERROR,
2238 "rdpgfx_recv_map_surface_to_scaled_window_pdu failed with error %" PRIu32
2239 "!",
2240 error);
2241
2242 break;
2243
2244 case RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT:
2245 if ((error = rdpgfx_recv_map_surface_to_scaled_output_pdu(callback, s)))
2246 WLog_Print(gfx->base.log, WLOG_ERROR,
2247 "rdpgfx_recv_map_surface_to_scaled_output_pdu failed with error %" PRIu32
2248 "!",
2249 error);
2250
2251 break;
2252
2253 case RDPGFX_CMDID_PROTECT_SURFACE:
2254 case RDPGFX_CMDID_WATERMARK:
2255 WLog_Print(gfx->base.log, WLOG_WARN,
2256 "Command %s not implemented, skipping %" PRIu32 " bytes",
2257 rdpgfx_get_cmd_id_string(header.cmdId), header.pduLength);
2258 if (!Stream_SafeSeek(s, packetlen))
2259 error = ERROR_INVALID_DATA;
2260 break;
2261 default:
2262 error = CHANNEL_RC_BAD_PROC;
2263 break;
2264 }
2265
2266 if (error)
2267 {
2268 WLog_Print(gfx->base.log, WLOG_ERROR,
2269 "Error while processing GFX cmdId: %s (0x%04" PRIX16 ")",
2270 rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId);
2271 if (!Stream_SetPosition(s, (beg + header.pduLength)))
2272 return ERROR_INVALID_DATA;
2273 return error;
2274 }
2275
2276 const size_t end = Stream_GetPosition(s);
2277
2278 if (end != (beg + header.pduLength))
2279 {
2280 WLog_Print(gfx->base.log, WLOG_ERROR,
2281 "Unexpected gfx pdu end: Actual: %" PRIuz ", Expected: %" PRIuz, end,
2282 (beg + header.pduLength));
2283 if (!Stream_SetPosition(s, (beg + header.pduLength)))
2284 return ERROR_INVALID_DATA;
2285 }
2286
2287 return error;
2288}
2289
2295static UINT rdpgfx_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
2296{
2297 UINT32 DstSize = 0;
2298 BYTE* pDstData = nullptr;
2299 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2300 WINPR_ASSERT(callback);
2301 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2302 UINT error = CHANNEL_RC_OK;
2303
2304 WINPR_ASSERT(gfx);
2305 int status = zgfx_decompress(gfx->zgfx, Stream_ConstPointer(data),
2306 (UINT32)Stream_GetRemainingLength(data), &pDstData, &DstSize, 0);
2307
2308 if (status < 0)
2309 {
2310 WLog_Print(gfx->base.log, WLOG_ERROR, "zgfx_decompress failure! status: %d", status);
2311 free(pDstData);
2312 return ERROR_INTERNAL_ERROR;
2313 }
2314
2315 wStream sbuffer = WINPR_C_ARRAY_INIT;
2316 wStream* s = Stream_StaticConstInit(&sbuffer, pDstData, DstSize);
2317
2318 if (!s)
2319 {
2320 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
2321 free(pDstData);
2322 return CHANNEL_RC_NO_MEMORY;
2323 }
2324
2325 while (Stream_GetPosition(s) < Stream_Length(s))
2326 {
2327 if ((error = rdpgfx_recv_pdu(callback, s)))
2328 {
2329 WLog_Print(gfx->base.log, WLOG_ERROR, "rdpgfx_recv_pdu failed with error %" PRIu32 "!",
2330 error);
2331 break;
2332 }
2333 }
2334
2335 free(pDstData);
2336 return error;
2337}
2338
2344static UINT rdpgfx_on_open(IWTSVirtualChannelCallback* pChannelCallback)
2345{
2346 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2347 WINPR_ASSERT(callback);
2348 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2349 WINPR_ASSERT(gfx);
2350 RdpgfxClientContext* context = gfx->context;
2351 UINT error = CHANNEL_RC_OK;
2352 BOOL do_caps_advertise = TRUE;
2353
2354 gfx->sendFrameAcks = TRUE;
2355
2356 if (context)
2357 {
2358 IFCALLRET(context->OnOpen, error, context, &do_caps_advertise, &gfx->sendFrameAcks);
2359
2360 if (error)
2361 WLog_Print(gfx->base.log, WLOG_ERROR, "context->OnOpen failed with error %" PRIu32 "",
2362 error);
2363
2364 RdpgfxClientContextInt* ctx = (RdpgfxClientContextInt*)context;
2365 const RdpgfxClientContextStats empty = WINPR_C_ARRAY_INIT;
2366 ctx->stats = empty;
2367 }
2368
2369 if (do_caps_advertise)
2370 error = rdpgfx_send_supported_caps(callback);
2371
2372 return error;
2373}
2374
2375static void rdpgfx_dump_stats(RdpgfxClientContext* context)
2376{
2377 WINPR_ASSERT(context);
2378
2379 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2380 WINPR_ASSERT(gfx);
2381
2382 wLog* log = gfx->base.log;
2383 const DWORD level = WLOG_TRACE;
2384 if (!WLog_IsLevelActive(log, level))
2385 return;
2386
2387 WLog_Print(log, level, "RdpgfxClientContextStats");
2388 for (size_t x = 0; x < rdpgfx_stats_max_index(); x++)
2389 {
2390 const char* name = rdpgfx_stats_name_for_index(x);
2391 const uint64_t val = rdpgfx_stats_value_for_index(context, x);
2392 WLog_Print(log, level, "%s: %" PRIu64, name, val);
2393 }
2394}
2395
2401static UINT rdpgfx_on_close(IWTSVirtualChannelCallback* pChannelCallback)
2402{
2403 UINT error = CHANNEL_RC_OK;
2404 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2405 WINPR_ASSERT(callback);
2406 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2407
2408 if (!gfx)
2409 goto fail;
2410
2411 RdpgfxClientContext* context = gfx->context;
2412
2413 WLog_Print(gfx->base.log, WLOG_DEBUG, "OnClose");
2414 error = rdpgfx_save_persistent_cache(gfx);
2415
2416 if (error)
2417 {
2418 // print error, but don't consider this a hard failure
2419 WLog_Print(gfx->base.log, WLOG_ERROR,
2420 "rdpgfx_save_persistent_cache failed with error %" PRIu32 "", error);
2421 }
2422
2423 free_surfaces(context, gfx->SurfaceTable);
2424 error = evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
2425 if (error)
2426 {
2427 // print error, but don't consider this a hard failure
2428 WLog_Print(gfx->base.log, WLOG_ERROR, "evict_cache_slots failed with error %" PRIu32 "",
2429 error);
2430 }
2431
2432 free(callback);
2433 gfx->UnacknowledgedFrames = 0;
2434 gfx->TotalDecodedFrames = 0;
2435
2436 if (context)
2437 {
2438 error = IFCALLRESULT(CHANNEL_RC_OK, context->OnClose, context);
2439 if (error)
2440 {
2441 // print error, but don't consider this a hard failure
2442 WLog_Print(gfx->base.log, WLOG_ERROR, "context->OnClose failed with error %" PRIu32 "",
2443 error);
2444 }
2445 rdpgfx_dump_stats(context);
2446 }
2447
2448fail:
2449 return CHANNEL_RC_OK;
2450}
2451
2452static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
2453{
2454 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)base;
2455 WINPR_ASSERT(gfx);
2456 RdpgfxClientContext* context = gfx->context;
2457
2458 WLog_Print(gfx->base.log, WLOG_DEBUG, "Terminated");
2459 rdpgfx_client_context_free(context);
2460}
2461
2467static UINT rdpgfx_set_surface_data(RdpgfxClientContext* context, UINT16 surfaceId, void* pData)
2468{
2469 WINPR_ASSERT(context);
2470 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2471 WINPR_ASSERT(gfx);
2472 ULONG_PTR key = ((ULONG_PTR)surfaceId) + 1;
2473
2474 if (pData)
2475 {
2476 if (!HashTable_Insert(gfx->SurfaceTable, (void*)key, pData))
2477 return ERROR_BAD_ARGUMENTS;
2478 }
2479 else
2480 HashTable_Remove(gfx->SurfaceTable, (void*)key);
2481
2482 return CHANNEL_RC_OK;
2483}
2484
2490static UINT rdpgfx_get_surface_ids(RdpgfxClientContext* context, UINT16** ppSurfaceIds,
2491 UINT16* count_out)
2492{
2493 ULONG_PTR* pKeys = nullptr;
2494 WINPR_ASSERT(context);
2495 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2496 WINPR_ASSERT(gfx);
2497 size_t count = HashTable_GetKeys(gfx->SurfaceTable, &pKeys);
2498
2499 WINPR_ASSERT(ppSurfaceIds);
2500 WINPR_ASSERT(count_out);
2501 if (count < 1)
2502 {
2503 *count_out = 0;
2504 return CHANNEL_RC_OK;
2505 }
2506
2507 UINT16* pSurfaceIds = (UINT16*)calloc(count, sizeof(UINT16));
2508
2509 if (!pSurfaceIds)
2510 {
2511 WLog_Print(gfx->base.log, WLOG_ERROR, "calloc failed!");
2512 free(pKeys);
2513 return CHANNEL_RC_NO_MEMORY;
2514 }
2515
2516 for (size_t index = 0; index < count; index++)
2517 {
2518 pSurfaceIds[index] = (UINT16)(pKeys[index] - 1);
2519 }
2520
2521 free(pKeys);
2522 *ppSurfaceIds = pSurfaceIds;
2523 *count_out = (UINT16)count;
2524 return CHANNEL_RC_OK;
2525}
2526
2527static void* rdpgfx_get_surface_data(RdpgfxClientContext* context, UINT16 surfaceId)
2528{
2529 WINPR_ASSERT(context);
2530 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2531 WINPR_ASSERT(gfx);
2532 ULONG_PTR key = ((ULONG_PTR)surfaceId) + 1;
2533 return HashTable_GetItemValue(gfx->SurfaceTable, (void*)key);
2534}
2535
2541static UINT rdpgfx_set_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot, void* pData)
2542{
2543 WINPR_ASSERT(context);
2544 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2545
2546 WINPR_ASSERT(gfx);
2547 /* Microsoft uses 1-based indexing for the egfx bitmap cache ! */
2548 if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
2549 {
2550 WLog_Print(gfx->base.log, WLOG_ERROR,
2551 "invalid cache slot %" PRIu16 ", must be between 1 and %" PRIu16 "", cacheSlot,
2552 gfx->MaxCacheSlots);
2553 return ERROR_INVALID_INDEX;
2554 }
2555
2556 gfx->CacheSlots[cacheSlot - 1] = pData;
2557 return CHANNEL_RC_OK;
2558}
2559
2560static void* rdpgfx_get_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot)
2561{
2562 WINPR_ASSERT(context);
2563 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2564 WINPR_ASSERT(gfx);
2565 /* Microsoft uses 1-based indexing for the egfx bitmap cache ! */
2566 if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
2567 {
2568 WLog_Print(gfx->base.log, WLOG_ERROR,
2569 "invalid cache slot %" PRIu16 ", must be between 1 and %" PRIu16 "", cacheSlot,
2570 gfx->MaxCacheSlots);
2571 return nullptr;
2572 }
2573
2574 return gfx->CacheSlots[cacheSlot - 1];
2575}
2576
2577static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext,
2578 WINPR_ATTR_UNUSED rdpSettings* settings)
2579{
2580 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)base;
2581
2582 WINPR_ASSERT(base);
2583 gfx->rdpcontext = rcontext;
2584
2585 gfx->SurfaceTable = HashTable_New(TRUE);
2586 if (!gfx->SurfaceTable)
2587 {
2588 WLog_Print(gfx->base.log, WLOG_ERROR, "HashTable_New for surfaces failed !");
2589 return CHANNEL_RC_NO_MEMORY;
2590 }
2591
2592 gfx->suspendFrameAcks =
2593 freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSuspendFrameAck);
2594 gfx->MaxCacheSlots =
2595 freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSmallCache) ? 4096 : 25600;
2596
2597 RdpgfxClientContextInt* context =
2598 (RdpgfxClientContextInt*)calloc(1, sizeof(RdpgfxClientContextInt));
2599 if (!context)
2600 {
2601 WLog_Print(gfx->base.log, WLOG_ERROR, "context calloc failed!");
2602 HashTable_Free(gfx->SurfaceTable);
2603 gfx->SurfaceTable = nullptr;
2604 return CHANNEL_RC_NO_MEMORY;
2605 }
2606
2607 gfx->zgfx = zgfx_context_new(FALSE);
2608 if (!gfx->zgfx)
2609 {
2610 WLog_Print(gfx->base.log, WLOG_ERROR, "zgfx_context_new failed!");
2611 HashTable_Free(gfx->SurfaceTable);
2612 gfx->SurfaceTable = nullptr;
2613 free(context);
2614 return CHANNEL_RC_NO_MEMORY;
2615 }
2616
2617 context->common.handle = (void*)gfx;
2618 context->common.GetSurfaceIds = rdpgfx_get_surface_ids;
2619 context->common.SetSurfaceData = rdpgfx_set_surface_data;
2620 context->common.GetSurfaceData = rdpgfx_get_surface_data;
2621 context->common.SetCacheSlotData = rdpgfx_set_cache_slot_data;
2622 context->common.GetCacheSlotData = rdpgfx_get_cache_slot_data;
2623 context->common.CapsAdvertise = rdpgfx_send_caps_advertise_pdu;
2624 context->common.FrameAcknowledge = rdpgfx_send_frame_acknowledge_pdu;
2625 context->common.CacheImportOffer = rdpgfx_send_cache_import_offer_pdu;
2626 context->common.QoeFrameAcknowledge = rdpgfx_send_qoe_frame_acknowledge_pdu;
2627
2628 gfx->base.iface.pInterface = (void*)context;
2629 gfx->context = &context->common;
2630 return CHANNEL_RC_OK;
2631}
2632
2633void rdpgfx_client_context_free(RdpgfxClientContext* context)
2634{
2635 if (!context)
2636 return;
2637
2638 RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2639
2640 free_surfaces(context, gfx->SurfaceTable);
2641 evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
2642
2643 if (gfx->zgfx)
2644 {
2645 zgfx_context_free(gfx->zgfx);
2646 gfx->zgfx = nullptr;
2647 }
2648
2649 HashTable_Free(gfx->SurfaceTable);
2650 free(context);
2651}
2652
2653static const IWTSVirtualChannelCallback rdpgfx_callbacks = { rdpgfx_on_data_received,
2654 rdpgfx_on_open, rdpgfx_on_close,
2655 nullptr };
2656
2662FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpgfx_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
2663{
2664 return freerdp_generic_DVCPluginEntry(pEntryPoints, GFXTAG, RDPGFX_DVC_CHANNEL_NAME,
2665 sizeof(RDPGFX_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
2666 &rdpgfx_callbacks, init_plugin_cb, terminate_plugin_cb);
2667}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition persistent.h:70