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