FreeRDP
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 
49 static 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 
71 static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable)
72 {
73  HashTable_Foreach(SurfaceTable, delete_surface, context);
74 }
75 
76 static 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  {
83  RDPGFX_EVICT_CACHE_ENTRY_PDU pdu = { 0 };
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 
101 static 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);
161 fail:
162  Stream_Free(s, TRUE);
163  return error;
164 }
165 
166 static 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 
192 static 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 
367 static 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  DEBUG_RDPGFX(gfx->log,
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 
402 static UINT rdpgfx_send_frame_acknowledge_pdu(RdpgfxClientContext* context,
403  const RDPGFX_FRAME_ACKNOWLEDGE_PDU* pdu)
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 
449 fail:
450  Stream_Free(s, TRUE);
451  return error;
452 }
453 
454 static 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);
499 fail:
500  Stream_Free(s, TRUE);
501  return error;
502 }
503 
509 static UINT rdpgfx_recv_reset_graphics_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
510 {
511  MONITOR_DEF* monitor = NULL;
512  RDPGFX_RESET_GRAPHICS_PDU pdu = { 0 };
513  WINPR_ASSERT(callback);
514 
515  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
516 
517  WINPR_ASSERT(gfx);
518 
519  RdpgfxClientContext* context = gfx->context;
520  UINT error = CHANNEL_RC_OK;
521  GraphicsResetEventArgs graphicsReset = { 0 };
522 
523  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
524  return ERROR_INVALID_DATA;
525 
526  Stream_Read_UINT32(s, pdu.width); /* width (4 bytes) */
527  Stream_Read_UINT32(s, pdu.height); /* height (4 bytes) */
528  Stream_Read_UINT32(s, pdu.monitorCount); /* monitorCount (4 bytes) */
529 
530  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.monitorCount, 20ull))
531  return ERROR_INVALID_DATA;
532 
533  pdu.monitorDefArray = (MONITOR_DEF*)calloc(pdu.monitorCount, sizeof(MONITOR_DEF));
534 
535  if (!pdu.monitorDefArray)
536  {
537  WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
538  return CHANNEL_RC_NO_MEMORY;
539  }
540 
541  for (UINT32 index = 0; index < pdu.monitorCount; index++)
542  {
543  monitor = &(pdu.monitorDefArray[index]);
544  Stream_Read_INT32(s, monitor->left); /* left (4 bytes) */
545  Stream_Read_INT32(s, monitor->top); /* top (4 bytes) */
546  Stream_Read_INT32(s, monitor->right); /* right (4 bytes) */
547  Stream_Read_INT32(s, monitor->bottom); /* bottom (4 bytes) */
548  Stream_Read_UINT32(s, monitor->flags); /* flags (4 bytes) */
549  }
550 
551  const size_t size = (RDPGFX_HEADER_SIZE + 12ULL + (pdu.monitorCount * 20ULL));
552  if (size > 340)
553  {
554  free(pdu.monitorDefArray);
555  return CHANNEL_RC_NULL_DATA;
556  }
557  const size_t pad = 340ULL - size;
558 
559  if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)pad))
560  {
561  free(pdu.monitorDefArray);
562  return CHANNEL_RC_NO_MEMORY;
563  }
564 
565  Stream_Seek(s, pad); /* pad (total size is 340 bytes) */
566  DEBUG_RDPGFX(gfx->log,
567  "RecvResetGraphicsPdu: width: %" PRIu32 " height: %" PRIu32 " count: %" PRIu32 "",
568  pdu.width, pdu.height, pdu.monitorCount);
569 
570 #if defined(WITH_DEBUG_RDPGFX)
571  for (UINT32 index = 0; index < pdu.monitorCount; index++)
572  {
573  monitor = &(pdu.monitorDefArray[index]);
574  DEBUG_RDPGFX(gfx->log,
575  "RecvResetGraphicsPdu: monitor left:%" PRIi32 " top:%" PRIi32 " right:%" PRIi32
576  " bottom:%" PRIi32 " flags:0x%" PRIx32 "",
577  monitor->left, monitor->top, monitor->right, monitor->bottom, monitor->flags);
578  }
579 #endif
580 
581  if (context)
582  {
583  IFCALLRET(context->ResetGraphics, error, context, &pdu);
584 
585  if (error)
586  WLog_Print(gfx->log, WLOG_ERROR, "context->ResetGraphics failed with error %" PRIu32 "",
587  error);
588  }
589 
590  /* some listeners may be interested (namely the display channel) */
591  EventArgsInit(&graphicsReset, "libfreerdp");
592  graphicsReset.width = pdu.width;
593  graphicsReset.height = pdu.height;
594  PubSub_OnGraphicsReset(gfx->rdpcontext->pubSub, gfx->rdpcontext, &graphicsReset);
595  free(pdu.monitorDefArray);
596  return error;
597 }
598 
604 static UINT rdpgfx_recv_evict_cache_entry_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
605 {
606  RDPGFX_EVICT_CACHE_ENTRY_PDU pdu = { 0 };
607  WINPR_ASSERT(callback);
608  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
609  WINPR_ASSERT(gfx);
610  RdpgfxClientContext* context = gfx->context;
611  UINT error = CHANNEL_RC_OK;
612 
613  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
614  return ERROR_INVALID_DATA;
615 
616  Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
617  WLog_Print(gfx->log, WLOG_DEBUG, "RecvEvictCacheEntryPdu: cacheSlot: %" PRIu16 "",
618  pdu.cacheSlot);
619 
620  if (context)
621  {
622  IFCALLRET(context->EvictCacheEntry, error, context, &pdu);
623 
624  if (error)
625  WLog_Print(gfx->log, WLOG_ERROR,
626  "context->EvictCacheEntry failed with error %" PRIu32 "", error);
627  }
628 
629  return error;
630 }
631 
637 static UINT rdpgfx_load_cache_import_offer(RDPGFX_PLUGIN* gfx, RDPGFX_CACHE_IMPORT_OFFER_PDU* offer)
638 {
639  int count = 0;
640  UINT error = CHANNEL_RC_OK;
642  rdpPersistentCache* persistent = NULL;
643  WINPR_ASSERT(gfx);
644  WINPR_ASSERT(gfx->rdpcontext);
645  rdpSettings* settings = gfx->rdpcontext->settings;
646 
647  WINPR_ASSERT(offer);
648  WINPR_ASSERT(settings);
649 
650  offer->cacheEntriesCount = 0;
651 
652  if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
653  return CHANNEL_RC_OK;
654 
655  const char* BitmapCachePersistFile =
656  freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
657  if (!BitmapCachePersistFile)
658  return CHANNEL_RC_OK;
659 
660  persistent = persistent_cache_new();
661 
662  if (!persistent)
663  return CHANNEL_RC_NO_MEMORY;
664 
665  if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
666  {
667  error = CHANNEL_RC_INITIALIZATION_ERROR;
668  goto fail;
669  }
670 
671  if (persistent_cache_get_version(persistent) != 3)
672  {
673  error = ERROR_INVALID_DATA;
674  goto fail;
675  }
676 
677  count = persistent_cache_get_count(persistent);
678 
679  if (count < 1)
680  {
681  error = ERROR_INVALID_DATA;
682  goto fail;
683  }
684 
685  if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
686  count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
687 
688  if (count > gfx->MaxCacheSlots)
689  count = gfx->MaxCacheSlots;
690 
691  offer->cacheEntriesCount = (UINT16)count;
692 
693  for (int idx = 0; idx < count; idx++)
694  {
695  if (persistent_cache_read_entry(persistent, &entry) < 1)
696  {
697  error = ERROR_INVALID_DATA;
698  goto fail;
699  }
700 
701  offer->cacheEntries[idx].cacheKey = entry.key64;
702  offer->cacheEntries[idx].bitmapLength = entry.size;
703  }
704 
705  persistent_cache_free(persistent);
706 
707  return error;
708 fail:
709  persistent_cache_free(persistent);
710  return error;
711 }
712 
718 static UINT rdpgfx_save_persistent_cache(RDPGFX_PLUGIN* gfx)
719 {
720  UINT error = CHANNEL_RC_OK;
721  PERSISTENT_CACHE_ENTRY cacheEntry;
722  rdpPersistentCache* persistent = NULL;
723  WINPR_ASSERT(gfx);
724  WINPR_ASSERT(gfx->rdpcontext);
725  rdpSettings* settings = gfx->rdpcontext->settings;
726  RdpgfxClientContext* context = gfx->context;
727 
728  WINPR_ASSERT(context);
729  WINPR_ASSERT(settings);
730 
731  if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
732  return CHANNEL_RC_OK;
733 
734  const char* BitmapCachePersistFile =
735  freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
736  if (!BitmapCachePersistFile)
737  return CHANNEL_RC_OK;
738 
739  if (!context->ExportCacheEntry)
740  return CHANNEL_RC_INITIALIZATION_ERROR;
741 
742  persistent = persistent_cache_new();
743 
744  if (!persistent)
745  return CHANNEL_RC_NO_MEMORY;
746 
747  if (persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, 3) < 1)
748  {
749  error = CHANNEL_RC_INITIALIZATION_ERROR;
750  goto fail;
751  }
752 
753  for (UINT16 idx = 0; idx < gfx->MaxCacheSlots; idx++)
754  {
755  if (gfx->CacheSlots[idx])
756  {
757  UINT16 cacheSlot = idx;
758 
759  if (context->ExportCacheEntry(context, cacheSlot, &cacheEntry) != CHANNEL_RC_OK)
760  continue;
761 
762  persistent_cache_write_entry(persistent, &cacheEntry);
763  }
764  }
765 
766  persistent_cache_free(persistent);
767 
768  return error;
769 fail:
770  persistent_cache_free(persistent);
771  return error;
772 }
773 
779 static UINT rdpgfx_send_cache_import_offer_pdu(RdpgfxClientContext* context,
781 {
782  UINT error = CHANNEL_RC_OK;
783  wStream* s = NULL;
784  RDPGFX_HEADER header;
785  GENERIC_CHANNEL_CALLBACK* callback = NULL;
786 
787  if (!context || !pdu)
788  return ERROR_BAD_ARGUMENTS;
789 
790  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
791 
792  if (!gfx || !gfx->base.listener_callback)
793  return ERROR_BAD_CONFIGURATION;
794 
795  callback = gfx->base.listener_callback->channel_callback;
796 
797  if (!callback)
798  return ERROR_BAD_CONFIGURATION;
799 
800  header.flags = 0;
801  header.cmdId = RDPGFX_CMDID_CACHEIMPORTOFFER;
802  header.pduLength = RDPGFX_HEADER_SIZE + 2ul + pdu->cacheEntriesCount * 12ul;
803  DEBUG_RDPGFX(gfx->log, "SendCacheImportOfferPdu: cacheEntriesCount: %" PRIu16 "",
804  pdu->cacheEntriesCount);
805  s = Stream_New(NULL, header.pduLength);
806 
807  if (!s)
808  {
809  WLog_ERR(TAG, "Stream_New failed!");
810  return CHANNEL_RC_NO_MEMORY;
811  }
812 
813  if ((error = rdpgfx_write_header(s, &header)))
814  goto fail;
815 
816  if (pdu->cacheEntriesCount <= 0)
817  {
818  WLog_ERR(TAG, "Invalid cacheEntriesCount: %" PRIu16 "", pdu->cacheEntriesCount);
819  error = ERROR_INVALID_DATA;
820  goto fail;
821  }
822 
823  /* cacheEntriesCount (2 bytes) */
824  Stream_Write_UINT16(s, pdu->cacheEntriesCount);
825 
826  for (UINT16 index = 0; index < pdu->cacheEntriesCount; index++)
827  {
828  const RDPGFX_CACHE_ENTRY_METADATA* cacheEntry = &(pdu->cacheEntries[index]);
829  Stream_Write_UINT64(s, cacheEntry->cacheKey); /* cacheKey (8 bytes) */
830  Stream_Write_UINT32(s, cacheEntry->bitmapLength); /* bitmapLength (4 bytes) */
831  }
832 
833  error = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
834  NULL);
835 
836 fail:
837  Stream_Free(s, TRUE);
838  return error;
839 }
840 
846 static UINT rdpgfx_send_cache_offer(RDPGFX_PLUGIN* gfx)
847 {
848  int count = 0;
849  UINT error = CHANNEL_RC_OK;
851  RDPGFX_CACHE_IMPORT_OFFER_PDU* offer = NULL;
852  rdpPersistentCache* persistent = NULL;
853 
854  WINPR_ASSERT(gfx);
855  WINPR_ASSERT(gfx->rdpcontext);
856 
857  RdpgfxClientContext* context = gfx->context;
858  rdpSettings* settings = gfx->rdpcontext->settings;
859 
860  if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
861  return CHANNEL_RC_OK;
862 
863  const char* BitmapCachePersistFile =
864  freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
865  if (!BitmapCachePersistFile)
866  return CHANNEL_RC_OK;
867 
868  persistent = persistent_cache_new();
869 
870  if (!persistent)
871  return CHANNEL_RC_NO_MEMORY;
872 
873  if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
874  {
875  error = CHANNEL_RC_INITIALIZATION_ERROR;
876  goto fail;
877  }
878 
879  if (persistent_cache_get_version(persistent) != 3)
880  {
881  error = ERROR_INVALID_DATA;
882  goto fail;
883  }
884 
885  count = persistent_cache_get_count(persistent);
886  if (count < 0)
887  {
888  error = ERROR_INVALID_DATA;
889  goto fail;
890  }
891 
892  if (count >= RDPGFX_CACHE_ENTRY_MAX_COUNT)
893  count = RDPGFX_CACHE_ENTRY_MAX_COUNT - 1;
894 
895  if (count > gfx->MaxCacheSlots)
896  count = gfx->MaxCacheSlots;
897 
899  if (!offer)
900  {
901  error = CHANNEL_RC_NO_MEMORY;
902  goto fail;
903  }
904 
905  WINPR_ASSERT(count <= UINT16_MAX);
906  offer->cacheEntriesCount = (UINT16)count;
907 
908  WLog_DBG(TAG, "Sending Cache Import Offer: %d", count);
909 
910  for (int idx = 0; idx < count; idx++)
911  {
912  if (persistent_cache_read_entry(persistent, &entry) < 1)
913  {
914  error = ERROR_INVALID_DATA;
915  goto fail;
916  }
917 
918  offer->cacheEntries[idx].cacheKey = entry.key64;
919  offer->cacheEntries[idx].bitmapLength = entry.size;
920  }
921 
922  if (offer->cacheEntriesCount > 0)
923  {
924  error = rdpgfx_send_cache_import_offer_pdu(context, offer);
925  if (error != CHANNEL_RC_OK)
926  {
927  WLog_Print(gfx->log, WLOG_ERROR, "Failed to send cache import offer PDU");
928  goto fail;
929  }
930  }
931 
932 fail:
933  persistent_cache_free(persistent);
934  free(offer);
935  return error;
936 }
937 
943 static UINT rdpgfx_load_cache_import_reply(RDPGFX_PLUGIN* gfx,
944  const RDPGFX_CACHE_IMPORT_REPLY_PDU* reply)
945 {
946  int count = 0;
947  UINT error = CHANNEL_RC_OK;
948  rdpPersistentCache* persistent = NULL;
949  WINPR_ASSERT(gfx);
950  WINPR_ASSERT(gfx->rdpcontext);
951  rdpSettings* settings = gfx->rdpcontext->settings;
952  RdpgfxClientContext* context = gfx->context;
953 
954  WINPR_ASSERT(settings);
955  WINPR_ASSERT(reply);
956  if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
957  return CHANNEL_RC_OK;
958 
959  const char* BitmapCachePersistFile =
960  freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
961  if (!BitmapCachePersistFile)
962  return CHANNEL_RC_OK;
963 
964  persistent = persistent_cache_new();
965 
966  if (!persistent)
967  return CHANNEL_RC_NO_MEMORY;
968 
969  if (persistent_cache_open(persistent, BitmapCachePersistFile, FALSE, 3) < 1)
970  {
971  error = CHANNEL_RC_INITIALIZATION_ERROR;
972  goto fail;
973  }
974 
975  if (persistent_cache_get_version(persistent) != 3)
976  {
977  error = ERROR_INVALID_DATA;
978  goto fail;
979  }
980 
981  count = persistent_cache_get_count(persistent);
982 
983  count = (count < reply->importedEntriesCount) ? count : reply->importedEntriesCount;
984 
985  WLog_DBG(TAG, "Receiving Cache Import Reply: %d", count);
986 
987  for (int idx = 0; idx < count; idx++)
988  {
989  PERSISTENT_CACHE_ENTRY entry = { 0 };
990  if (persistent_cache_read_entry(persistent, &entry) < 1)
991  {
992  error = ERROR_INVALID_DATA;
993  goto fail;
994  }
995 
996  const UINT16 cacheSlot = reply->cacheSlots[idx];
997  if (context && context->ImportCacheEntry)
998  context->ImportCacheEntry(context, cacheSlot, &entry);
999  }
1000 
1001  persistent_cache_free(persistent);
1002 
1003  return error;
1004 fail:
1005  persistent_cache_free(persistent);
1006  return error;
1007 }
1008 
1014 static UINT rdpgfx_recv_cache_import_reply_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1015 {
1016  RDPGFX_CACHE_IMPORT_REPLY_PDU pdu = { 0 };
1017  WINPR_ASSERT(callback);
1018  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1019  WINPR_ASSERT(gfx);
1020  RdpgfxClientContext* context = gfx->context;
1021  UINT error = CHANNEL_RC_OK;
1022 
1023  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
1024  return ERROR_INVALID_DATA;
1025 
1026  Stream_Read_UINT16(s, pdu.importedEntriesCount); /* cacheSlot (2 bytes) */
1027 
1028  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.importedEntriesCount, 2ull))
1029  return ERROR_INVALID_DATA;
1030 
1031  if (pdu.importedEntriesCount > RDPGFX_CACHE_ENTRY_MAX_COUNT)
1032  return ERROR_INVALID_DATA;
1033 
1034  for (UINT16 idx = 0; idx < pdu.importedEntriesCount; idx++)
1035  {
1036  Stream_Read_UINT16(s, pdu.cacheSlots[idx]); /* cacheSlot (2 bytes) */
1037  }
1038 
1039  DEBUG_RDPGFX(gfx->log, "RecvCacheImportReplyPdu: importedEntriesCount: %" PRIu16 "",
1040  pdu.importedEntriesCount);
1041 
1042  error = rdpgfx_load_cache_import_reply(gfx, &pdu);
1043 
1044  if (error)
1045  {
1046  WLog_Print(gfx->log, WLOG_ERROR,
1047  "rdpgfx_load_cache_import_reply failed with error %" PRIu32 "", error);
1048  return error;
1049  }
1050 
1051  if (context)
1052  {
1053  IFCALLRET(context->CacheImportReply, error, context, &pdu);
1054 
1055  if (error)
1056  WLog_Print(gfx->log, WLOG_ERROR,
1057  "context->CacheImportReply failed with error %" PRIu32 "", error);
1058  }
1059 
1060  return error;
1061 }
1062 
1068 static UINT rdpgfx_recv_create_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1069 {
1070  RDPGFX_CREATE_SURFACE_PDU pdu = { 0 };
1071  WINPR_ASSERT(callback);
1072  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1073  WINPR_ASSERT(gfx);
1074  RdpgfxClientContext* context = gfx->context;
1075  UINT error = CHANNEL_RC_OK;
1076 
1077  if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
1078  return ERROR_INVALID_DATA;
1079 
1080  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1081  Stream_Read_UINT16(s, pdu.width); /* width (2 bytes) */
1082  Stream_Read_UINT16(s, pdu.height); /* height (2 bytes) */
1083  Stream_Read_UINT8(s, pdu.pixelFormat); /* RDPGFX_PIXELFORMAT (1 byte) */
1084  DEBUG_RDPGFX(gfx->log,
1085  "RecvCreateSurfacePdu: surfaceId: %" PRIu16 " width: %" PRIu16 " height: %" PRIu16
1086  " pixelFormat: 0x%02" PRIX8 "",
1087  pdu.surfaceId, pdu.width, pdu.height, pdu.pixelFormat);
1088 
1089  if (context)
1090  {
1091  /* create surface PDU sometimes happens for surface ID that are already in use and have not
1092  * been removed yet. Ensure that there is no surface with the new ID by trying to remove it
1093  * manually.
1094  */
1095  RDPGFX_DELETE_SURFACE_PDU deletePdu = { pdu.surfaceId };
1096  IFCALL(context->DeleteSurface, context, &deletePdu);
1097 
1098  IFCALLRET(context->CreateSurface, error, context, &pdu);
1099 
1100  if (error)
1101  WLog_Print(gfx->log, WLOG_ERROR, "context->CreateSurface failed with error %" PRIu32 "",
1102  error);
1103  }
1104 
1105  return error;
1106 }
1107 
1113 static UINT rdpgfx_recv_delete_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1114 {
1115  RDPGFX_DELETE_SURFACE_PDU pdu = { 0 };
1116  WINPR_ASSERT(callback);
1117  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1118  WINPR_ASSERT(gfx);
1119  RdpgfxClientContext* context = gfx->context;
1120  UINT error = CHANNEL_RC_OK;
1121 
1122  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
1123  return ERROR_INVALID_DATA;
1124 
1125  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1126  DEBUG_RDPGFX(gfx->log, "RecvDeleteSurfacePdu: surfaceId: %" PRIu16 "", pdu.surfaceId);
1127 
1128  if (context)
1129  {
1130  IFCALLRET(context->DeleteSurface, error, context, &pdu);
1131 
1132  if (error)
1133  WLog_Print(gfx->log, WLOG_ERROR, "context->DeleteSurface failed with error %" PRIu32 "",
1134  error);
1135  }
1136 
1137  return error;
1138 }
1139 
1145 static UINT rdpgfx_recv_start_frame_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1146 {
1147  RDPGFX_START_FRAME_PDU pdu = { 0 };
1148  WINPR_ASSERT(callback);
1149  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1150  WINPR_ASSERT(gfx);
1151  RdpgfxClientContext* context = gfx->context;
1152  UINT error = CHANNEL_RC_OK;
1153 
1154  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_START_FRAME_PDU_SIZE))
1155  return ERROR_INVALID_DATA;
1156 
1157  Stream_Read_UINT32(s, pdu.timestamp); /* timestamp (4 bytes) */
1158  Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
1159  DEBUG_RDPGFX(gfx->log, "RecvStartFramePdu: frameId: %" PRIu32 " timestamp: 0x%08" PRIX32 "",
1160  pdu.frameId, pdu.timestamp);
1161  gfx->StartDecodingTime = GetTickCount64();
1162 
1163  if (context)
1164  {
1165  IFCALLRET(context->StartFrame, error, context, &pdu);
1166 
1167  if (error)
1168  WLog_Print(gfx->log, WLOG_ERROR, "context->StartFrame failed with error %" PRIu32 "",
1169  error);
1170  }
1171 
1172  gfx->UnacknowledgedFrames++;
1173  return error;
1174 }
1175 
1181 static UINT rdpgfx_recv_end_frame_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1182 {
1183  RDPGFX_END_FRAME_PDU pdu = { 0 };
1184  RDPGFX_FRAME_ACKNOWLEDGE_PDU ack = { 0 };
1185  WINPR_ASSERT(callback);
1186  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1187  WINPR_ASSERT(gfx);
1188  RdpgfxClientContext* context = gfx->context;
1189  UINT error = CHANNEL_RC_OK;
1190 
1191  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_END_FRAME_PDU_SIZE))
1192  return ERROR_INVALID_DATA;
1193 
1194  Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
1195  DEBUG_RDPGFX(gfx->log, "RecvEndFramePdu: frameId: %" PRIu32 "", pdu.frameId);
1196 
1197  const UINT64 start = GetTickCount64();
1198  if (context)
1199  {
1200  IFCALLRET(context->EndFrame, error, context, &pdu);
1201 
1202  if (error)
1203  {
1204  WLog_Print(gfx->log, WLOG_ERROR, "context->EndFrame failed with error %" PRIu32 "",
1205  error);
1206  return error;
1207  }
1208  }
1209  const UINT64 end = GetTickCount64();
1210  const UINT64 EndFrameTime = end - start;
1211  gfx->TotalDecodedFrames++;
1212 
1213  if (!gfx->sendFrameAcks)
1214  return error;
1215 
1216  ack.frameId = pdu.frameId;
1217  ack.totalFramesDecoded = gfx->TotalDecodedFrames;
1218 
1219  if (gfx->suspendFrameAcks)
1220  {
1221  ack.queueDepth = SUSPEND_FRAME_ACKNOWLEDGEMENT;
1222 
1223  if (gfx->TotalDecodedFrames == 1)
1224  if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
1225  WLog_Print(gfx->log, WLOG_ERROR,
1226  "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 "",
1227  error);
1228  }
1229  else
1230  {
1231  ack.queueDepth = QUEUE_DEPTH_UNAVAILABLE;
1232 
1233  if ((error = rdpgfx_send_frame_acknowledge_pdu(context, &ack)))
1234  WLog_Print(gfx->log, WLOG_ERROR,
1235  "rdpgfx_send_frame_acknowledge_pdu failed with error %" PRIu32 "", error);
1236  }
1237 
1238  switch (gfx->ConnectionCaps.version)
1239  {
1240  case RDPGFX_CAPVERSION_10:
1241  case RDPGFX_CAPVERSION_102:
1242  case RDPGFX_CAPVERSION_103:
1243  case RDPGFX_CAPVERSION_104:
1244  case RDPGFX_CAPVERSION_105:
1245  case RDPGFX_CAPVERSION_106:
1246  case RDPGFX_CAPVERSION_106_ERR:
1247  case RDPGFX_CAPVERSION_107:
1248  if (freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSendQoeAck))
1249  {
1251  UINT64 diff = (GetTickCount64() - gfx->StartDecodingTime);
1252 
1253  if (diff > 65000)
1254  diff = 0;
1255 
1256  qoe.frameId = pdu.frameId;
1257  qoe.timestamp = gfx->StartDecodingTime % UINT32_MAX;
1258  qoe.timeDiffSE = WINPR_ASSERTING_INT_CAST(UINT16, diff);
1259  qoe.timeDiffEDR = WINPR_ASSERTING_INT_CAST(UINT16, EndFrameTime);
1260 
1261  if ((error = rdpgfx_send_qoe_frame_acknowledge_pdu(context, &qoe)))
1262  WLog_Print(gfx->log, WLOG_ERROR,
1263  "rdpgfx_send_qoe_frame_acknowledge_pdu failed with error %" PRIu32
1264  "",
1265  error);
1266  }
1267 
1268  break;
1269 
1270  default:
1271  break;
1272  }
1273 
1274  return error;
1275 }
1276 
1282 static UINT rdpgfx_recv_wire_to_surface_1_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1283 {
1284  RDPGFX_SURFACE_COMMAND cmd = { 0 };
1285  RDPGFX_WIRE_TO_SURFACE_PDU_1 pdu = { 0 };
1286  WINPR_ASSERT(callback);
1287  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1288  UINT error = 0;
1289 
1290  WINPR_ASSERT(gfx);
1291  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE))
1292  return ERROR_INVALID_DATA;
1293 
1294  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1295  Stream_Read_UINT16(s, pdu.codecId); /* codecId (2 bytes) */
1296  Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */
1297 
1298  if ((error = rdpgfx_read_rect16(s, &(pdu.destRect)))) /* destRect (8 bytes) */
1299  {
1300  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "", error);
1301  return error;
1302  }
1303 
1304  Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */
1305 
1306  if (!Stream_CheckAndLogRequiredLength(TAG, s, pdu.bitmapDataLength))
1307  return ERROR_INVALID_DATA;
1308 
1309  pdu.bitmapData = Stream_Pointer(s);
1310  Stream_Seek(s, pdu.bitmapDataLength);
1311 
1312  DEBUG_RDPGFX(gfx->log,
1313  "RecvWireToSurface1Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16
1314  ") pixelFormat: 0x%02" PRIX8 " "
1315  "destRect: left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16
1316  " bitmapDataLength: %" PRIu32 "",
1317  pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId,
1318  pdu.pixelFormat, pdu.destRect.left, pdu.destRect.top, pdu.destRect.right,
1319  pdu.destRect.bottom, pdu.bitmapDataLength);
1320  cmd.surfaceId = pdu.surfaceId;
1321  cmd.codecId = pdu.codecId;
1322  cmd.contextId = 0;
1323 
1324  switch (pdu.pixelFormat)
1325  {
1326  case GFX_PIXEL_FORMAT_XRGB_8888:
1327  cmd.format = PIXEL_FORMAT_BGRX32;
1328  break;
1329 
1330  case GFX_PIXEL_FORMAT_ARGB_8888:
1331  cmd.format = PIXEL_FORMAT_BGRA32;
1332  break;
1333 
1334  default:
1335  return ERROR_INVALID_DATA;
1336  }
1337 
1338  cmd.left = pdu.destRect.left;
1339  cmd.top = pdu.destRect.top;
1340  cmd.right = pdu.destRect.right;
1341  cmd.bottom = pdu.destRect.bottom;
1342  cmd.width = cmd.right - cmd.left;
1343  cmd.height = cmd.bottom - cmd.top;
1344  cmd.length = pdu.bitmapDataLength;
1345  cmd.data = pdu.bitmapData;
1346  cmd.extra = NULL;
1347 
1348  if (cmd.right < cmd.left)
1349  {
1350  WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu right=%" PRIu32 " < left=%" PRIu32,
1351  cmd.right, cmd.left);
1352  return ERROR_INVALID_DATA;
1353  }
1354  if (cmd.bottom < cmd.top)
1355  {
1356  WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu bottom=%" PRIu32 " < top=%" PRIu32,
1357  cmd.bottom, cmd.top);
1358  return ERROR_INVALID_DATA;
1359  }
1360 
1361  if ((error = rdpgfx_decode(gfx, &cmd)))
1362  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!", error);
1363 
1364  return error;
1365 }
1366 
1372 static UINT rdpgfx_recv_wire_to_surface_2_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1373 {
1374  RDPGFX_SURFACE_COMMAND cmd = { 0 };
1375  RDPGFX_WIRE_TO_SURFACE_PDU_2 pdu = { 0 };
1376  WINPR_ASSERT(callback);
1377  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1378  WINPR_ASSERT(gfx);
1379  RdpgfxClientContext* context = gfx->context;
1380  UINT error = CHANNEL_RC_OK;
1381 
1382  if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE))
1383  return ERROR_INVALID_DATA;
1384 
1385  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1386  Stream_Read_UINT16(s, pdu.codecId); /* codecId (2 bytes) */
1387  Stream_Read_UINT32(s, pdu.codecContextId); /* codecContextId (4 bytes) */
1388  Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */
1389  Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */
1390  pdu.bitmapData = Stream_Pointer(s);
1391  Stream_Seek(s, pdu.bitmapDataLength);
1392 
1393  DEBUG_RDPGFX(gfx->log,
1394  "RecvWireToSurface2Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 ") "
1395  "codecContextId: %" PRIu32 " pixelFormat: 0x%02" PRIX8
1396  " bitmapDataLength: %" PRIu32 "",
1397  pdu.surfaceId, rdpgfx_get_codec_id_string(pdu.codecId), pdu.codecId,
1398  pdu.codecContextId, pdu.pixelFormat, pdu.bitmapDataLength);
1399 
1400  cmd.surfaceId = pdu.surfaceId;
1401  cmd.codecId = pdu.codecId;
1402  cmd.contextId = pdu.codecContextId;
1403 
1404  switch (pdu.pixelFormat)
1405  {
1406  case GFX_PIXEL_FORMAT_XRGB_8888:
1407  cmd.format = PIXEL_FORMAT_BGRX32;
1408  break;
1409 
1410  case GFX_PIXEL_FORMAT_ARGB_8888:
1411  cmd.format = PIXEL_FORMAT_BGRA32;
1412  break;
1413 
1414  default:
1415  return ERROR_INVALID_DATA;
1416  }
1417 
1418  cmd.left = 0;
1419  cmd.top = 0;
1420  cmd.right = 0;
1421  cmd.bottom = 0;
1422  cmd.width = 0;
1423  cmd.height = 0;
1424  cmd.length = pdu.bitmapDataLength;
1425  cmd.data = pdu.bitmapData;
1426  cmd.extra = NULL;
1427 
1428  if (context)
1429  {
1430  IFCALLRET(context->SurfaceCommand, error, context, &cmd);
1431 
1432  if (error)
1433  WLog_Print(gfx->log, WLOG_ERROR,
1434  "context->SurfaceCommand failed with error %" PRIu32 "", error);
1435  }
1436 
1437  return error;
1438 }
1439 
1445 static UINT rdpgfx_recv_delete_encoding_context_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1446 {
1448  WINPR_ASSERT(callback);
1449  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1450  WINPR_ASSERT(gfx);
1451  RdpgfxClientContext* context = gfx->context;
1452  UINT error = CHANNEL_RC_OK;
1453 
1454  if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
1455  return ERROR_INVALID_DATA;
1456 
1457  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1458  Stream_Read_UINT32(s, pdu.codecContextId); /* codecContextId (4 bytes) */
1459 
1460  DEBUG_RDPGFX(gfx->log,
1461  "RecvDeleteEncodingContextPdu: surfaceId: %" PRIu16 " codecContextId: %" PRIu32 "",
1462  pdu.surfaceId, pdu.codecContextId);
1463 
1464  if (context)
1465  {
1466  IFCALLRET(context->DeleteEncodingContext, error, context, &pdu);
1467 
1468  if (error)
1469  WLog_Print(gfx->log, WLOG_ERROR,
1470  "context->DeleteEncodingContext failed with error %" PRIu32 "", error);
1471  }
1472 
1473  return error;
1474 }
1475 
1481 static UINT rdpgfx_recv_solid_fill_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1482 {
1483  RECTANGLE_16* fillRect = NULL;
1484  RDPGFX_SOLID_FILL_PDU pdu = { 0 };
1485  WINPR_ASSERT(callback);
1486  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1487  WINPR_ASSERT(gfx);
1488  RdpgfxClientContext* context = gfx->context;
1489  UINT error = 0;
1490 
1491  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1492  return ERROR_INVALID_DATA;
1493 
1494  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1495 
1496  if ((error = rdpgfx_read_color32(s, &(pdu.fillPixel)))) /* fillPixel (4 bytes) */
1497  {
1498  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_color32 failed with error %" PRIu32 "!",
1499  error);
1500  return error;
1501  }
1502 
1503  Stream_Read_UINT16(s, pdu.fillRectCount); /* fillRectCount (2 bytes) */
1504 
1505  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.fillRectCount, 8ull))
1506  return ERROR_INVALID_DATA;
1507 
1508  pdu.fillRects = (RECTANGLE_16*)calloc(pdu.fillRectCount, sizeof(RECTANGLE_16));
1509 
1510  if (!pdu.fillRects)
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.fillRectCount; index++)
1517  {
1518  fillRect = &(pdu.fillRects[index]);
1519 
1520  if ((error = rdpgfx_read_rect16(s, fillRect)))
1521  {
1522  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "!",
1523  error);
1524  free(pdu.fillRects);
1525  return error;
1526  }
1527  }
1528  DEBUG_RDPGFX(gfx->log, "RecvSolidFillPdu: surfaceId: %" PRIu16 " fillRectCount: %" PRIu16 "",
1529  pdu.surfaceId, pdu.fillRectCount);
1530 
1531  if (context)
1532  {
1533  IFCALLRET(context->SolidFill, error, context, &pdu);
1534 
1535  if (error)
1536  WLog_Print(gfx->log, WLOG_ERROR, "context->SolidFill failed with error %" PRIu32 "",
1537  error);
1538  }
1539 
1540  free(pdu.fillRects);
1541  return error;
1542 }
1543 
1549 static UINT rdpgfx_recv_surface_to_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1550 {
1551  RDPGFX_POINT16* destPt = NULL;
1552  RDPGFX_SURFACE_TO_SURFACE_PDU pdu = { 0 };
1553  WINPR_ASSERT(callback);
1554  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1555  WINPR_ASSERT(gfx);
1556  RdpgfxClientContext* context = gfx->context;
1557  UINT error = 0;
1558 
1559  if (!Stream_CheckAndLogRequiredLength(TAG, s, 14))
1560  return ERROR_INVALID_DATA;
1561 
1562  Stream_Read_UINT16(s, pdu.surfaceIdSrc); /* surfaceIdSrc (2 bytes) */
1563  Stream_Read_UINT16(s, pdu.surfaceIdDest); /* surfaceIdDest (2 bytes) */
1564 
1565  if ((error = rdpgfx_read_rect16(s, &(pdu.rectSrc)))) /* rectSrc (8 bytes ) */
1566  {
1567  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "!",
1568  error);
1569  return error;
1570  }
1571 
1572  Stream_Read_UINT16(s, pdu.destPtsCount); /* destPtsCount (2 bytes) */
1573 
1574  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.destPtsCount, 4ull))
1575  return ERROR_INVALID_DATA;
1576 
1577  pdu.destPts = (RDPGFX_POINT16*)calloc(pdu.destPtsCount, sizeof(RDPGFX_POINT16));
1578 
1579  if (!pdu.destPts)
1580  {
1581  WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
1582  return CHANNEL_RC_NO_MEMORY;
1583  }
1584 
1585  for (UINT16 index = 0; index < pdu.destPtsCount; index++)
1586  {
1587  destPt = &(pdu.destPts[index]);
1588 
1589  if ((error = rdpgfx_read_point16(s, destPt)))
1590  {
1591  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_point16 failed with error %" PRIu32 "!",
1592  error);
1593  free(pdu.destPts);
1594  return error;
1595  }
1596  }
1597 
1598  DEBUG_RDPGFX(gfx->log,
1599  "RecvSurfaceToSurfacePdu: surfaceIdSrc: %" PRIu16 " surfaceIdDest: %" PRIu16 " "
1600  "left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16
1601  " destPtsCount: %" PRIu16 "",
1602  pdu.surfaceIdSrc, pdu.surfaceIdDest, pdu.rectSrc.left, pdu.rectSrc.top,
1603  pdu.rectSrc.right, pdu.rectSrc.bottom, pdu.destPtsCount);
1604 
1605  if (context)
1606  {
1607  IFCALLRET(context->SurfaceToSurface, error, context, &pdu);
1608 
1609  if (error)
1610  WLog_Print(gfx->log, WLOG_ERROR,
1611  "context->SurfaceToSurface failed with error %" PRIu32 "", error);
1612  }
1613 
1614  free(pdu.destPts);
1615  return error;
1616 }
1617 
1623 static UINT rdpgfx_recv_surface_to_cache_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1624 {
1625  RDPGFX_SURFACE_TO_CACHE_PDU pdu = { 0 };
1626  WINPR_ASSERT(callback);
1627  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1628 
1629  WINPR_ASSERT(gfx);
1630  RdpgfxClientContext* context = gfx->context;
1631  UINT error = 0;
1632 
1633  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
1634  return ERROR_INVALID_DATA;
1635 
1636  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1637  Stream_Read_UINT64(s, pdu.cacheKey); /* cacheKey (8 bytes) */
1638  Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
1639 
1640  if ((error = rdpgfx_read_rect16(s, &(pdu.rectSrc)))) /* rectSrc (8 bytes ) */
1641  {
1642  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_rect16 failed with error %" PRIu32 "!",
1643  error);
1644  return error;
1645  }
1646 
1647  DEBUG_RDPGFX(gfx->log,
1648  "RecvSurfaceToCachePdu: surfaceId: %" PRIu16 " cacheKey: 0x%016" PRIX64
1649  " cacheSlot: %" PRIu16 " "
1650  "left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16 " bottom: %" PRIu16 "",
1651  pdu.surfaceId, pdu.cacheKey, pdu.cacheSlot, pdu.rectSrc.left, pdu.rectSrc.top,
1652  pdu.rectSrc.right, pdu.rectSrc.bottom);
1653 
1654  if (context)
1655  {
1656  IFCALLRET(context->SurfaceToCache, error, context, &pdu);
1657 
1658  if (error)
1659  WLog_Print(gfx->log, WLOG_ERROR,
1660  "context->SurfaceToCache failed with error %" PRIu32 "", error);
1661  }
1662 
1663  return error;
1664 }
1665 
1671 static UINT rdpgfx_recv_cache_to_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1672 {
1673  RDPGFX_POINT16* destPt = NULL;
1674  RDPGFX_CACHE_TO_SURFACE_PDU pdu = { 0 };
1675  WINPR_ASSERT(callback);
1676  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1677 
1678  WINPR_ASSERT(gfx);
1679  RdpgfxClientContext* context = gfx->context;
1680  UINT error = CHANNEL_RC_OK;
1681 
1682  if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
1683  return ERROR_INVALID_DATA;
1684 
1685  Stream_Read_UINT16(s, pdu.cacheSlot); /* cacheSlot (2 bytes) */
1686  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1687  Stream_Read_UINT16(s, pdu.destPtsCount); /* destPtsCount (2 bytes) */
1688 
1689  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.destPtsCount, 4ull))
1690  return ERROR_INVALID_DATA;
1691 
1692  pdu.destPts = (RDPGFX_POINT16*)calloc(pdu.destPtsCount, sizeof(RDPGFX_POINT16));
1693 
1694  if (!pdu.destPts)
1695  {
1696  WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
1697  return CHANNEL_RC_NO_MEMORY;
1698  }
1699 
1700  for (UINT16 index = 0; index < pdu.destPtsCount; index++)
1701  {
1702  destPt = &(pdu.destPts[index]);
1703 
1704  if ((error = rdpgfx_read_point16(s, destPt)))
1705  {
1706  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_point16 failed with error %" PRIu32 "",
1707  error);
1708  free(pdu.destPts);
1709  return error;
1710  }
1711  }
1712 
1713  DEBUG_RDPGFX(gfx->log,
1714  "RdpGfxRecvCacheToSurfacePdu: cacheSlot: %" PRIu16 " surfaceId: %" PRIu16
1715  " destPtsCount: %" PRIu16 "",
1716  pdu.cacheSlot, pdu.surfaceId, pdu.destPtsCount);
1717 
1718  if (context)
1719  {
1720  IFCALLRET(context->CacheToSurface, error, context, &pdu);
1721 
1722  if (error)
1723  WLog_Print(gfx->log, WLOG_ERROR,
1724  "context->CacheToSurface failed with error %" PRIu32 "", error);
1725  }
1726 
1727  free(pdu.destPts);
1728  return error;
1729 }
1730 
1736 static UINT rdpgfx_recv_map_surface_to_output_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1737 {
1739  WINPR_ASSERT(callback);
1740  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1741 
1742  WINPR_ASSERT(gfx);
1743  RdpgfxClientContext* context = gfx->context;
1744  UINT error = CHANNEL_RC_OK;
1745 
1746  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
1747  return ERROR_INVALID_DATA;
1748 
1749  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1750  Stream_Read_UINT16(s, pdu.reserved); /* reserved (2 bytes) */
1751  Stream_Read_UINT32(s, pdu.outputOriginX); /* outputOriginX (4 bytes) */
1752  Stream_Read_UINT32(s, pdu.outputOriginY); /* outputOriginY (4 bytes) */
1753  DEBUG_RDPGFX(gfx->log,
1754  "RecvMapSurfaceToOutputPdu: surfaceId: %" PRIu16 " outputOriginX: %" PRIu32
1755  " outputOriginY: %" PRIu32 "",
1756  pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY);
1757 
1758  if (context)
1759  {
1760  IFCALLRET(context->MapSurfaceToOutput, error, context, &pdu);
1761 
1762  if (error)
1763  WLog_Print(gfx->log, WLOG_ERROR,
1764  "context->MapSurfaceToOutput failed with error %" PRIu32 "", error);
1765  }
1766 
1767  return error;
1768 }
1769 
1770 static UINT rdpgfx_recv_map_surface_to_scaled_output_pdu(GENERIC_CHANNEL_CALLBACK* callback,
1771  wStream* s)
1772 {
1774  WINPR_ASSERT(callback);
1775  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1776 
1777  WINPR_ASSERT(gfx);
1778  RdpgfxClientContext* context = gfx->context;
1779  UINT error = CHANNEL_RC_OK;
1780 
1781  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
1782  return ERROR_INVALID_DATA;
1783 
1784  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1785  Stream_Read_UINT16(s, pdu.reserved); /* reserved (2 bytes) */
1786  Stream_Read_UINT32(s, pdu.outputOriginX); /* outputOriginX (4 bytes) */
1787  Stream_Read_UINT32(s, pdu.outputOriginY); /* outputOriginY (4 bytes) */
1788  Stream_Read_UINT32(s, pdu.targetWidth); /* targetWidth (4 bytes) */
1789  Stream_Read_UINT32(s, pdu.targetHeight); /* targetHeight (4 bytes) */
1790  DEBUG_RDPGFX(gfx->log,
1791  "RecvMapSurfaceToScaledOutputPdu: surfaceId: %" PRIu16 " outputOriginX: %" PRIu32
1792  " outputOriginY: %" PRIu32 " targetWidth: %" PRIu32 " targetHeight: %" PRIu32,
1793  pdu.surfaceId, pdu.outputOriginX, pdu.outputOriginY, pdu.targetWidth,
1794  pdu.targetHeight);
1795 
1796  if (context)
1797  {
1798  IFCALLRET(context->MapSurfaceToScaledOutput, error, context, &pdu);
1799 
1800  if (error)
1801  WLog_Print(gfx->log, WLOG_ERROR,
1802  "context->MapSurfaceToScaledOutput failed with error %" PRIu32 "", error);
1803  }
1804 
1805  return error;
1806 }
1807 
1813 static UINT rdpgfx_recv_map_surface_to_window_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1814 {
1816  WINPR_ASSERT(callback);
1817  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1818 
1819  WINPR_ASSERT(gfx);
1820  RdpgfxClientContext* context = gfx->context;
1821  UINT error = CHANNEL_RC_OK;
1822 
1823  if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
1824  return ERROR_INVALID_DATA;
1825 
1826  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1827  Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
1828  Stream_Read_UINT32(s, pdu.mappedWidth); /* mappedWidth (4 bytes) */
1829  Stream_Read_UINT32(s, pdu.mappedHeight); /* mappedHeight (4 bytes) */
1830  DEBUG_RDPGFX(gfx->log,
1831  "RecvMapSurfaceToWindowPdu: surfaceId: %" PRIu16 " windowId: 0x%016" PRIX64
1832  " mappedWidth: %" PRIu32 " mappedHeight: %" PRIu32 "",
1833  pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight);
1834 
1835  if (context && context->MapSurfaceToWindow)
1836  {
1837  IFCALLRET(context->MapSurfaceToWindow, error, context, &pdu);
1838 
1839  if (error)
1840  WLog_Print(gfx->log, WLOG_ERROR,
1841  "context->MapSurfaceToWindow failed with error %" PRIu32 "", error);
1842  }
1843 
1844  return error;
1845 }
1846 
1847 static UINT rdpgfx_recv_map_surface_to_scaled_window_pdu(GENERIC_CHANNEL_CALLBACK* callback,
1848  wStream* s)
1849 {
1851  WINPR_ASSERT(callback);
1852  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1853  WINPR_ASSERT(gfx);
1854  RdpgfxClientContext* context = gfx->context;
1855  UINT error = CHANNEL_RC_OK;
1856 
1857  if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
1858  return ERROR_INVALID_DATA;
1859 
1860  Stream_Read_UINT16(s, pdu.surfaceId); /* surfaceId (2 bytes) */
1861  Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
1862  Stream_Read_UINT32(s, pdu.mappedWidth); /* mappedWidth (4 bytes) */
1863  Stream_Read_UINT32(s, pdu.mappedHeight); /* mappedHeight (4 bytes) */
1864  Stream_Read_UINT32(s, pdu.targetWidth); /* targetWidth (4 bytes) */
1865  Stream_Read_UINT32(s, pdu.targetHeight); /* targetHeight (4 bytes) */
1866  DEBUG_RDPGFX(gfx->log,
1867  "RecvMapSurfaceToScaledWindowPdu: surfaceId: %" PRIu16 " windowId: 0x%016" PRIX64
1868  " mappedWidth: %" PRIu32 " mappedHeight: %" PRIu32 " targetWidth: %" PRIu32
1869  " targetHeight: %" PRIu32 "",
1870  pdu.surfaceId, pdu.windowId, pdu.mappedWidth, pdu.mappedHeight, pdu.targetWidth,
1871  pdu.targetHeight);
1872 
1873  if (context && context->MapSurfaceToScaledWindow)
1874  {
1875  IFCALLRET(context->MapSurfaceToScaledWindow, error, context, &pdu);
1876 
1877  if (error)
1878  WLog_Print(gfx->log, WLOG_ERROR,
1879  "context->MapSurfaceToScaledWindow failed with error %" PRIu32 "", error);
1880  }
1881 
1882  return error;
1883 }
1884 
1890 static UINT rdpgfx_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
1891 {
1892  size_t end = 0;
1893  RDPGFX_HEADER header = { 0 };
1894  UINT error = 0;
1895  WINPR_ASSERT(callback);
1896  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
1897  const size_t beg = Stream_GetPosition(s);
1898 
1899  WINPR_ASSERT(gfx);
1900  if ((error = rdpgfx_read_header(s, &header)))
1901  {
1902  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_read_header failed with error %" PRIu32 "!",
1903  error);
1904  return error;
1905  }
1906 
1907  DEBUG_RDPGFX(
1908  gfx->log, "cmdId: %s (0x%04" PRIX16 ") flags: 0x%04" PRIX16 " pduLength: %" PRIu32 "",
1909  rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId, header.flags, header.pduLength);
1910 
1911  switch (header.cmdId)
1912  {
1913  case RDPGFX_CMDID_WIRETOSURFACE_1:
1914  if ((error = rdpgfx_recv_wire_to_surface_1_pdu(callback, s)))
1915  WLog_Print(gfx->log, WLOG_ERROR,
1916  "rdpgfx_recv_wire_to_surface_1_pdu failed with error %" PRIu32 "!",
1917  error);
1918 
1919  break;
1920 
1921  case RDPGFX_CMDID_WIRETOSURFACE_2:
1922  if ((error = rdpgfx_recv_wire_to_surface_2_pdu(callback, s)))
1923  WLog_Print(gfx->log, WLOG_ERROR,
1924  "rdpgfx_recv_wire_to_surface_2_pdu failed with error %" PRIu32 "!",
1925  error);
1926 
1927  break;
1928 
1929  case RDPGFX_CMDID_DELETEENCODINGCONTEXT:
1930  if ((error = rdpgfx_recv_delete_encoding_context_pdu(callback, s)))
1931  WLog_Print(gfx->log, WLOG_ERROR,
1932  "rdpgfx_recv_delete_encoding_context_pdu failed with error %" PRIu32 "!",
1933  error);
1934 
1935  break;
1936 
1937  case RDPGFX_CMDID_SOLIDFILL:
1938  if ((error = rdpgfx_recv_solid_fill_pdu(callback, s)))
1939  WLog_Print(gfx->log, WLOG_ERROR,
1940  "rdpgfx_recv_solid_fill_pdu failed with error %" PRIu32 "!", error);
1941 
1942  break;
1943 
1944  case RDPGFX_CMDID_SURFACETOSURFACE:
1945  if ((error = rdpgfx_recv_surface_to_surface_pdu(callback, s)))
1946  WLog_Print(gfx->log, WLOG_ERROR,
1947  "rdpgfx_recv_surface_to_surface_pdu failed with error %" PRIu32 "!",
1948  error);
1949 
1950  break;
1951 
1952  case RDPGFX_CMDID_SURFACETOCACHE:
1953  if ((error = rdpgfx_recv_surface_to_cache_pdu(callback, s)))
1954  WLog_Print(gfx->log, WLOG_ERROR,
1955  "rdpgfx_recv_surface_to_cache_pdu failed with error %" PRIu32 "!",
1956  error);
1957 
1958  break;
1959 
1960  case RDPGFX_CMDID_CACHETOSURFACE:
1961  if ((error = rdpgfx_recv_cache_to_surface_pdu(callback, s)))
1962  WLog_Print(gfx->log, WLOG_ERROR,
1963  "rdpgfx_recv_cache_to_surface_pdu failed with error %" PRIu32 "!",
1964  error);
1965 
1966  break;
1967 
1968  case RDPGFX_CMDID_EVICTCACHEENTRY:
1969  if ((error = rdpgfx_recv_evict_cache_entry_pdu(callback, s)))
1970  WLog_Print(gfx->log, WLOG_ERROR,
1971  "rdpgfx_recv_evict_cache_entry_pdu failed with error %" PRIu32 "!",
1972  error);
1973 
1974  break;
1975 
1976  case RDPGFX_CMDID_CREATESURFACE:
1977  if ((error = rdpgfx_recv_create_surface_pdu(callback, s)))
1978  WLog_Print(gfx->log, WLOG_ERROR,
1979  "rdpgfx_recv_create_surface_pdu failed with error %" PRIu32 "!", error);
1980 
1981  break;
1982 
1983  case RDPGFX_CMDID_DELETESURFACE:
1984  if ((error = rdpgfx_recv_delete_surface_pdu(callback, s)))
1985  WLog_Print(gfx->log, WLOG_ERROR,
1986  "rdpgfx_recv_delete_surface_pdu failed with error %" PRIu32 "!", error);
1987 
1988  break;
1989 
1990  case RDPGFX_CMDID_STARTFRAME:
1991  if ((error = rdpgfx_recv_start_frame_pdu(callback, s)))
1992  WLog_Print(gfx->log, WLOG_ERROR,
1993  "rdpgfx_recv_start_frame_pdu failed with error %" PRIu32 "!", error);
1994 
1995  break;
1996 
1997  case RDPGFX_CMDID_ENDFRAME:
1998  if ((error = rdpgfx_recv_end_frame_pdu(callback, s)))
1999  WLog_Print(gfx->log, WLOG_ERROR,
2000  "rdpgfx_recv_end_frame_pdu failed with error %" PRIu32 "!", error);
2001 
2002  break;
2003 
2004  case RDPGFX_CMDID_RESETGRAPHICS:
2005  if ((error = rdpgfx_recv_reset_graphics_pdu(callback, s)))
2006  WLog_Print(gfx->log, WLOG_ERROR,
2007  "rdpgfx_recv_reset_graphics_pdu failed with error %" PRIu32 "!", error);
2008 
2009  break;
2010 
2011  case RDPGFX_CMDID_MAPSURFACETOOUTPUT:
2012  if ((error = rdpgfx_recv_map_surface_to_output_pdu(callback, s)))
2013  WLog_Print(gfx->log, WLOG_ERROR,
2014  "rdpgfx_recv_map_surface_to_output_pdu failed with error %" PRIu32 "!",
2015  error);
2016 
2017  break;
2018 
2019  case RDPGFX_CMDID_CACHEIMPORTREPLY:
2020  if ((error = rdpgfx_recv_cache_import_reply_pdu(callback, s)))
2021  WLog_Print(gfx->log, WLOG_ERROR,
2022  "rdpgfx_recv_cache_import_reply_pdu failed with error %" PRIu32 "!",
2023  error);
2024 
2025  break;
2026 
2027  case RDPGFX_CMDID_CAPSCONFIRM:
2028  if ((error = rdpgfx_recv_caps_confirm_pdu(callback, s)))
2029  WLog_Print(gfx->log, WLOG_ERROR,
2030  "rdpgfx_recv_caps_confirm_pdu failed with error %" PRIu32 "!", error);
2031 
2032  if ((error = rdpgfx_send_cache_offer(gfx)))
2033  WLog_Print(gfx->log, WLOG_ERROR,
2034  "rdpgfx_send_cache_offer failed with error %" PRIu32 "!", error);
2035 
2036  break;
2037 
2038  case RDPGFX_CMDID_MAPSURFACETOWINDOW:
2039  if ((error = rdpgfx_recv_map_surface_to_window_pdu(callback, s)))
2040  WLog_Print(gfx->log, WLOG_ERROR,
2041  "rdpgfx_recv_map_surface_to_window_pdu failed with error %" PRIu32 "!",
2042  error);
2043 
2044  break;
2045 
2046  case RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW:
2047  if ((error = rdpgfx_recv_map_surface_to_scaled_window_pdu(callback, s)))
2048  WLog_Print(gfx->log, WLOG_ERROR,
2049  "rdpgfx_recv_map_surface_to_scaled_window_pdu failed with error %" PRIu32
2050  "!",
2051  error);
2052 
2053  break;
2054 
2055  case RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT:
2056  if ((error = rdpgfx_recv_map_surface_to_scaled_output_pdu(callback, s)))
2057  WLog_Print(gfx->log, WLOG_ERROR,
2058  "rdpgfx_recv_map_surface_to_scaled_output_pdu failed with error %" PRIu32
2059  "!",
2060  error);
2061 
2062  break;
2063 
2064  default:
2065  error = CHANNEL_RC_BAD_PROC;
2066  break;
2067  }
2068 
2069  if (error)
2070  {
2071  WLog_Print(gfx->log, WLOG_ERROR, "Error while processing GFX cmdId: %s (0x%04" PRIX16 ")",
2072  rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId);
2073  Stream_SetPosition(s, (beg + header.pduLength));
2074  return error;
2075  }
2076 
2077  end = Stream_GetPosition(s);
2078 
2079  if (end != (beg + header.pduLength))
2080  {
2081  WLog_Print(gfx->log, WLOG_ERROR,
2082  "Unexpected gfx pdu end: Actual: %" PRIuz ", Expected: %" PRIuz, end,
2083  (beg + header.pduLength));
2084  Stream_SetPosition(s, (beg + header.pduLength));
2085  }
2086 
2087  return error;
2088 }
2089 
2095 static UINT rdpgfx_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
2096 {
2097  int status = 0;
2098  UINT32 DstSize = 0;
2099  BYTE* pDstData = NULL;
2100  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2101  WINPR_ASSERT(callback);
2102  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2103  UINT error = CHANNEL_RC_OK;
2104 
2105  WINPR_ASSERT(gfx);
2106  status = zgfx_decompress(gfx->zgfx, Stream_ConstPointer(data),
2107  (UINT32)Stream_GetRemainingLength(data), &pDstData, &DstSize, 0);
2108 
2109  if (status < 0)
2110  {
2111  WLog_Print(gfx->log, WLOG_ERROR, "zgfx_decompress failure! status: %d", status);
2112  free(pDstData);
2113  return ERROR_INTERNAL_ERROR;
2114  }
2115 
2116  wStream sbuffer = { 0 };
2117  wStream* s = Stream_StaticConstInit(&sbuffer, pDstData, DstSize);
2118 
2119  if (!s)
2120  {
2121  WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
2122  free(pDstData);
2123  return CHANNEL_RC_NO_MEMORY;
2124  }
2125 
2126  while (Stream_GetPosition(s) < Stream_Length(s))
2127  {
2128  if ((error = rdpgfx_recv_pdu(callback, s)))
2129  {
2130  WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_recv_pdu failed with error %" PRIu32 "!",
2131  error);
2132  break;
2133  }
2134  }
2135 
2136  free(pDstData);
2137  return error;
2138 }
2139 
2145 static UINT rdpgfx_on_open(IWTSVirtualChannelCallback* pChannelCallback)
2146 {
2147  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2148  WINPR_ASSERT(callback);
2149  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2150  WINPR_ASSERT(gfx);
2151  RdpgfxClientContext* context = gfx->context;
2152  UINT error = CHANNEL_RC_OK;
2153  BOOL do_caps_advertise = TRUE;
2154  gfx->sendFrameAcks = TRUE;
2155 
2156  if (context)
2157  {
2158  IFCALLRET(context->OnOpen, error, context, &do_caps_advertise, &gfx->sendFrameAcks);
2159 
2160  if (error)
2161  WLog_Print(gfx->log, WLOG_ERROR, "context->OnOpen failed with error %" PRIu32 "",
2162  error);
2163  }
2164 
2165  if (do_caps_advertise)
2166  error = rdpgfx_send_supported_caps(callback);
2167 
2168  return error;
2169 }
2170 
2176 static UINT rdpgfx_on_close(IWTSVirtualChannelCallback* pChannelCallback)
2177 {
2178  UINT error = CHANNEL_RC_OK;
2179  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
2180  WINPR_ASSERT(callback);
2181  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)callback->plugin;
2182 
2183  if (!gfx)
2184  goto fail;
2185 
2186  RdpgfxClientContext* context = gfx->context;
2187 
2188  DEBUG_RDPGFX(gfx->log, "OnClose");
2189  error = rdpgfx_save_persistent_cache(gfx);
2190 
2191  if (error)
2192  {
2193  // print error, but don't consider this a hard failure
2194  WLog_Print(gfx->log, WLOG_ERROR,
2195  "rdpgfx_save_persistent_cache failed with error %" PRIu32 "", error);
2196  }
2197 
2198  free_surfaces(context, gfx->SurfaceTable);
2199  evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
2200 
2201  free(callback);
2202  gfx->UnacknowledgedFrames = 0;
2203  gfx->TotalDecodedFrames = 0;
2204 
2205  if (context)
2206  {
2207  IFCALL(context->OnClose, context);
2208  }
2209 
2210 fail:
2211  return CHANNEL_RC_OK;
2212 }
2213 
2214 static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
2215 {
2216  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)base;
2217  WINPR_ASSERT(gfx);
2218  RdpgfxClientContext* context = gfx->context;
2219 
2220  DEBUG_RDPGFX(gfx->log, "Terminated");
2221  rdpgfx_client_context_free(context);
2222 }
2223 
2229 static UINT rdpgfx_set_surface_data(RdpgfxClientContext* context, UINT16 surfaceId, void* pData)
2230 {
2231  ULONG_PTR key = 0;
2232  WINPR_ASSERT(context);
2233  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2234  WINPR_ASSERT(gfx);
2235  key = ((ULONG_PTR)surfaceId) + 1;
2236 
2237  if (pData)
2238  {
2239  if (!HashTable_Insert(gfx->SurfaceTable, (void*)key, pData))
2240  return ERROR_BAD_ARGUMENTS;
2241  }
2242  else
2243  HashTable_Remove(gfx->SurfaceTable, (void*)key);
2244 
2245  return CHANNEL_RC_OK;
2246 }
2247 
2253 static UINT rdpgfx_get_surface_ids(RdpgfxClientContext* context, UINT16** ppSurfaceIds,
2254  UINT16* count_out)
2255 {
2256  size_t count = 0;
2257  UINT16* pSurfaceIds = NULL;
2258  ULONG_PTR* pKeys = NULL;
2259  WINPR_ASSERT(context);
2260  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2261  WINPR_ASSERT(gfx);
2262  count = HashTable_GetKeys(gfx->SurfaceTable, &pKeys);
2263 
2264  WINPR_ASSERT(ppSurfaceIds);
2265  WINPR_ASSERT(count_out);
2266  if (count < 1)
2267  {
2268  *count_out = 0;
2269  return CHANNEL_RC_OK;
2270  }
2271 
2272  pSurfaceIds = (UINT16*)calloc(count, sizeof(UINT16));
2273 
2274  if (!pSurfaceIds)
2275  {
2276  WLog_Print(gfx->log, WLOG_ERROR, "calloc failed!");
2277  free(pKeys);
2278  return CHANNEL_RC_NO_MEMORY;
2279  }
2280 
2281  for (size_t index = 0; index < count; index++)
2282  {
2283  pSurfaceIds[index] = (UINT16)(pKeys[index] - 1);
2284  }
2285 
2286  free(pKeys);
2287  *ppSurfaceIds = pSurfaceIds;
2288  *count_out = (UINT16)count;
2289  return CHANNEL_RC_OK;
2290 }
2291 
2292 static void* rdpgfx_get_surface_data(RdpgfxClientContext* context, UINT16 surfaceId)
2293 {
2294  ULONG_PTR key = 0;
2295  void* pData = NULL;
2296  WINPR_ASSERT(context);
2297  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2298  WINPR_ASSERT(gfx);
2299  key = ((ULONG_PTR)surfaceId) + 1;
2300  pData = HashTable_GetItemValue(gfx->SurfaceTable, (void*)key);
2301  return pData;
2302 }
2303 
2309 static UINT rdpgfx_set_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot, void* pData)
2310 {
2311  WINPR_ASSERT(context);
2312  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2313 
2314  WINPR_ASSERT(gfx);
2315  /* Microsoft uses 1-based indexing for the egfx bitmap cache ! */
2316  if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
2317  {
2318  WLog_ERR(TAG, "invalid cache slot %" PRIu16 ", must be between 1 and %" PRIu16 "",
2319  cacheSlot, gfx->MaxCacheSlots);
2320  return ERROR_INVALID_INDEX;
2321  }
2322 
2323  gfx->CacheSlots[cacheSlot - 1] = pData;
2324  return CHANNEL_RC_OK;
2325 }
2326 
2327 static void* rdpgfx_get_cache_slot_data(RdpgfxClientContext* context, UINT16 cacheSlot)
2328 {
2329  void* pData = NULL;
2330  WINPR_ASSERT(context);
2331  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)context->handle;
2332  WINPR_ASSERT(gfx);
2333  /* Microsoft uses 1-based indexing for the egfx bitmap cache ! */
2334  if (cacheSlot == 0 || cacheSlot > gfx->MaxCacheSlots)
2335  {
2336  WLog_ERR(TAG, "invalid cache slot %" PRIu16 ", must be between 1 and %" PRIu16 "",
2337  cacheSlot, gfx->MaxCacheSlots);
2338  return NULL;
2339  }
2340 
2341  pData = gfx->CacheSlots[cacheSlot - 1];
2342  return pData;
2343 }
2344 
2345 static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
2346 {
2347  RdpgfxClientContext* context = NULL;
2348  RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*)base;
2349 
2350  WINPR_ASSERT(base);
2351  gfx->rdpcontext = rcontext;
2352  gfx->log = WLog_Get(TAG);
2353 
2354  gfx->SurfaceTable = HashTable_New(TRUE);
2355  if (!gfx->SurfaceTable)
2356  {
2357  WLog_ERR(TAG, "HashTable_New for surfaces failed !");
2358  return CHANNEL_RC_NO_MEMORY;
2359  }
2360 
2361  gfx->suspendFrameAcks =
2362  freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSuspendFrameAck);
2363  gfx->MaxCacheSlots =
2364  freerdp_settings_get_bool(gfx->rdpcontext->settings, FreeRDP_GfxSmallCache) ? 4096 : 25600;
2365 
2366  context = (RdpgfxClientContext*)calloc(1, sizeof(RdpgfxClientContext));
2367  if (!context)
2368  {
2369  WLog_ERR(TAG, "context calloc failed!");
2370  HashTable_Free(gfx->SurfaceTable);
2371  gfx->SurfaceTable = NULL;
2372  return CHANNEL_RC_NO_MEMORY;
2373  }
2374 
2375  gfx->zgfx = zgfx_context_new(FALSE);
2376  if (!gfx->zgfx)
2377  {
2378  WLog_ERR(TAG, "zgfx_context_new failed!");
2379  HashTable_Free(gfx->SurfaceTable);
2380  gfx->SurfaceTable = NULL;
2381  free(context);
2382  return CHANNEL_RC_NO_MEMORY;
2383  }
2384 
2385  context->handle = (void*)gfx;
2386  context->GetSurfaceIds = rdpgfx_get_surface_ids;
2387  context->SetSurfaceData = rdpgfx_set_surface_data;
2388  context->GetSurfaceData = rdpgfx_get_surface_data;
2389  context->SetCacheSlotData = rdpgfx_set_cache_slot_data;
2390  context->GetCacheSlotData = rdpgfx_get_cache_slot_data;
2391  context->CapsAdvertise = rdpgfx_send_caps_advertise_pdu;
2392  context->FrameAcknowledge = rdpgfx_send_frame_acknowledge_pdu;
2393  context->CacheImportOffer = rdpgfx_send_cache_import_offer_pdu;
2394  context->QoeFrameAcknowledge = rdpgfx_send_qoe_frame_acknowledge_pdu;
2395 
2396  gfx->base.iface.pInterface = (void*)context;
2397  gfx->context = context;
2398  return CHANNEL_RC_OK;
2399 }
2400 
2401 void rdpgfx_client_context_free(RdpgfxClientContext* context)
2402 {
2403 
2404  RDPGFX_PLUGIN* gfx = NULL;
2405 
2406  if (!context)
2407  return;
2408 
2409  gfx = (RDPGFX_PLUGIN*)context->handle;
2410 
2411  free_surfaces(context, gfx->SurfaceTable);
2412  evict_cache_slots(context, gfx->MaxCacheSlots, gfx->CacheSlots);
2413 
2414  if (gfx->zgfx)
2415  {
2416  zgfx_context_free(gfx->zgfx);
2417  gfx->zgfx = NULL;
2418  }
2419 
2420  HashTable_Free(gfx->SurfaceTable);
2421  free(context);
2422 }
2423 
2424 static const IWTSVirtualChannelCallback rdpgfx_callbacks = { rdpgfx_on_data_received,
2425  rdpgfx_on_open, rdpgfx_on_close,
2426  NULL };
2427 
2433 FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpgfx_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
2434 {
2435  return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPGFX_DVC_CHANNEL_NAME,
2436  sizeof(RDPGFX_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
2437  &rdpgfx_callbacks, init_plugin_cb, terminate_plugin_cb);
2438 }
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