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