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