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