Coverage Report

Created: 2024-05-20 06:11

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