Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/cache/pointer.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Glyph Cache
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <stdio.h>
23
24
#include <winpr/crt.h>
25
#include <winpr/assert.h>
26
#include <winpr/stream.h>
27
28
#include <freerdp/log.h>
29
30
#include "pointer.h"
31
#include "cache.h"
32
33
#define TAG FREERDP_TAG("cache.pointer")
34
35
static BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
36
                              BOOL colorCache);
37
static rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index);
38
39
static void pointer_clear(rdpPointer* pointer)
40
0
{
41
0
  if (pointer)
42
0
  {
43
0
    pointer->lengthAndMask = 0;
44
0
    free(pointer->andMaskData);
45
0
    pointer->andMaskData = NULL;
46
47
0
    pointer->lengthXorMask = 0;
48
0
    free(pointer->xorMaskData);
49
0
    pointer->xorMaskData = NULL;
50
0
  }
51
0
}
52
53
static void pointer_free(rdpContext* context, rdpPointer* pointer)
54
0
{
55
0
  if (pointer)
56
0
  {
57
0
    IFCALL(pointer->Free, context, pointer);
58
0
    pointer_clear(pointer);
59
0
  }
60
0
  free(pointer);
61
0
}
62
63
static BOOL update_pointer_position(rdpContext* context,
64
                                    const POINTER_POSITION_UPDATE* pointer_position)
65
0
{
66
0
  if (!context || !context->graphics || !context->graphics->Pointer_Prototype ||
67
0
      !pointer_position)
68
0
    return FALSE;
69
70
0
  const BOOL GrabMouse = freerdp_settings_get_bool(context->settings, FreeRDP_GrabMouse);
71
0
  if (!GrabMouse)
72
0
    return TRUE;
73
74
0
  const rdpPointer* pointer = context->graphics->Pointer_Prototype;
75
0
  WINPR_ASSERT(pointer);
76
77
0
  return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos,
78
0
                      pointer_position->yPos);
79
0
}
80
81
static BOOL update_pointer_system(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system)
82
0
{
83
0
  rdpPointer* pointer = NULL;
84
85
0
  if (!context || !context->graphics || !context->graphics->Pointer_Prototype || !pointer_system)
86
0
    return FALSE;
87
88
0
  pointer = context->graphics->Pointer_Prototype;
89
90
0
  switch (pointer_system->type)
91
0
  {
92
0
    case SYSPTR_NULL:
93
0
      return IFCALLRESULT(TRUE, pointer->SetNull, context);
94
95
0
    case SYSPTR_DEFAULT:
96
0
      return IFCALLRESULT(TRUE, pointer->SetDefault, context);
97
98
0
    default:
99
0
      WLog_ERR(TAG, "Unknown system pointer type (0x%08" PRIX32 ")", pointer_system->type);
100
0
  }
101
0
  return TRUE;
102
0
}
103
104
static BOOL upate_pointer_copy_andxor(rdpPointer* pointer, const BYTE* andMaskData,
105
                                      size_t lengthAndMask, const BYTE* xorMaskData,
106
                                      size_t lengthXorMask)
107
0
{
108
0
  WINPR_ASSERT(pointer);
109
110
0
  pointer_clear(pointer);
111
0
  if (lengthAndMask && andMaskData)
112
0
  {
113
0
    if (lengthAndMask > UINT32_MAX)
114
0
      return FALSE;
115
0
    pointer->lengthAndMask = (UINT32)lengthAndMask;
116
0
    pointer->andMaskData = (BYTE*)malloc(lengthAndMask);
117
0
    if (!pointer->andMaskData)
118
0
      return FALSE;
119
120
0
    CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask);
121
0
  }
122
123
0
  if (lengthXorMask && xorMaskData)
124
0
  {
125
0
    if (lengthXorMask > UINT32_MAX)
126
0
      return FALSE;
127
0
    pointer->lengthXorMask = (UINT32)lengthXorMask;
128
0
    pointer->xorMaskData = (BYTE*)malloc(lengthXorMask);
129
0
    if (!pointer->xorMaskData)
130
0
      return FALSE;
131
132
0
    CopyMemory(pointer->xorMaskData, xorMaskData, lengthXorMask);
133
0
  }
134
135
0
  return TRUE;
136
0
}
137
138
static BOOL update_pointer_color(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color)
139
0
{
140
0
  rdpPointer* pointer = NULL;
141
0
  rdpCache* cache = NULL;
142
143
0
  WINPR_ASSERT(context);
144
0
  WINPR_ASSERT(pointer_color);
145
146
0
  cache = context->cache;
147
0
  WINPR_ASSERT(cache);
148
149
0
  pointer = Pointer_Alloc(context);
150
151
0
  if (pointer == NULL)
152
0
    return FALSE;
153
0
  pointer->xorBpp = 24;
154
0
  pointer->xPos = pointer_color->hotSpotX;
155
0
  pointer->yPos = pointer_color->hotSpotY;
156
0
  pointer->width = pointer_color->width;
157
0
  pointer->height = pointer_color->height;
158
159
0
  if (!upate_pointer_copy_andxor(pointer, pointer_color->andMaskData,
160
0
                                 pointer_color->lengthAndMask, pointer_color->xorMaskData,
161
0
                                 pointer_color->lengthXorMask))
162
0
    goto out_fail;
163
164
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
165
0
    goto out_fail;
166
167
0
  if (!pointer_cache_put(cache->pointer, pointer_color->cacheIndex, pointer, TRUE))
168
0
    goto out_fail;
169
170
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
171
172
0
out_fail:
173
0
  pointer_free(context, pointer);
174
0
  return FALSE;
175
0
}
176
177
static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large)
178
0
{
179
0
  rdpPointer* pointer = NULL;
180
0
  rdpCache* cache = NULL;
181
182
0
  WINPR_ASSERT(context);
183
0
  WINPR_ASSERT(pointer_large);
184
185
0
  cache = context->cache;
186
0
  WINPR_ASSERT(cache);
187
188
0
  pointer = Pointer_Alloc(context);
189
0
  if (pointer == NULL)
190
0
    return FALSE;
191
0
  pointer->xorBpp = pointer_large->xorBpp;
192
0
  pointer->xPos = pointer_large->hotSpotX;
193
0
  pointer->yPos = pointer_large->hotSpotY;
194
0
  pointer->width = pointer_large->width;
195
0
  pointer->height = pointer_large->height;
196
197
0
  if (!upate_pointer_copy_andxor(pointer, pointer_large->andMaskData,
198
0
                                 pointer_large->lengthAndMask, pointer_large->xorMaskData,
199
0
                                 pointer_large->lengthXorMask))
200
0
    goto out_fail;
201
202
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
203
0
    goto out_fail;
204
205
0
  if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer, FALSE))
206
0
    goto out_fail;
207
208
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
209
210
0
out_fail:
211
0
  pointer_free(context, pointer);
212
0
  return FALSE;
213
0
}
214
215
static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
216
0
{
217
0
  if (!context || !pointer_new)
218
0
    return FALSE;
219
220
0
  rdpCache* cache = context->cache;
221
0
  rdpPointer* pointer = Pointer_Alloc(context);
222
223
0
  if (!pointer)
224
0
    return FALSE;
225
226
0
  pointer->xorBpp = pointer_new->xorBpp;
227
0
  pointer->xPos = pointer_new->colorPtrAttr.hotSpotX;
228
0
  pointer->yPos = pointer_new->colorPtrAttr.hotSpotY;
229
0
  pointer->width = pointer_new->colorPtrAttr.width;
230
0
  pointer->height = pointer_new->colorPtrAttr.height;
231
0
  if (!upate_pointer_copy_andxor(
232
0
          pointer, pointer_new->colorPtrAttr.andMaskData, pointer_new->colorPtrAttr.lengthAndMask,
233
0
          pointer_new->colorPtrAttr.xorMaskData, pointer_new->colorPtrAttr.lengthXorMask))
234
0
    goto out_fail;
235
236
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
237
0
    goto out_fail;
238
239
0
  if (!pointer_cache_put(cache->pointer, pointer_new->colorPtrAttr.cacheIndex, pointer, FALSE))
240
0
    goto out_fail;
241
242
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
243
244
0
out_fail:
245
0
  pointer_free(context, pointer);
246
0
  return FALSE;
247
0
}
248
249
static BOOL update_pointer_cached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached)
250
0
{
251
0
  rdpPointer* pointer = NULL;
252
0
  rdpCache* cache = NULL;
253
254
0
  WINPR_ASSERT(context);
255
0
  WINPR_ASSERT(pointer_cached);
256
257
0
  cache = context->cache;
258
0
  WINPR_ASSERT(cache);
259
260
0
  pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex);
261
262
0
  if (pointer != NULL)
263
0
    return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
264
265
0
  return FALSE;
266
0
}
267
268
rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index)
269
0
{
270
0
  rdpPointer* pointer = NULL;
271
272
0
  WINPR_ASSERT(pointer_cache);
273
274
0
  if (index >= pointer_cache->cacheSize)
275
0
  {
276
0
    WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index,
277
0
             pointer_cache->cacheSize);
278
0
    return NULL;
279
0
  }
280
281
0
  WINPR_ASSERT(pointer_cache->entries);
282
0
  pointer = pointer_cache->entries[index];
283
0
  return pointer;
284
0
}
285
286
BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
287
                       BOOL colorCache)
288
0
{
289
0
  rdpPointer* prevPointer = NULL;
290
0
  const FreeRDP_Settings_Keys_UInt32 id =
291
0
      colorCache ? FreeRDP_ColorPointerCacheSize : FreeRDP_PointerCacheSize;
292
293
0
  WINPR_ASSERT(pointer_cache);
294
0
  WINPR_ASSERT(pointer_cache->context);
295
296
0
  const UINT32 size = freerdp_settings_get_uint32(pointer_cache->context->settings, id);
297
0
  if (index >= pointer_cache->cacheSize)
298
0
  {
299
0
    WLog_ERR(TAG,
300
0
             "invalid pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32 "]",
301
0
             index, pointer_cache->cacheSize,
302
0
             colorCache ? "color-pointer-cache" : "pointer-cache", size);
303
0
    return FALSE;
304
0
  }
305
0
  if (index >= size)
306
0
  {
307
0
    WLog_WARN(TAG,
308
0
              "suspicious pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32
309
0
              "]",
310
0
              index, pointer_cache->cacheSize,
311
0
              colorCache ? "color-pointer-cache" : "pointer-cache", size);
312
0
  }
313
314
0
  WINPR_ASSERT(pointer_cache->entries);
315
0
  prevPointer = pointer_cache->entries[index];
316
0
  pointer_free(pointer_cache->context, prevPointer);
317
0
  pointer_cache->entries[index] = pointer;
318
0
  return TRUE;
319
0
}
320
321
void pointer_cache_register_callbacks(rdpUpdate* update)
322
0
{
323
0
  rdpPointerUpdate* pointer = NULL;
324
325
0
  WINPR_ASSERT(update);
326
0
  WINPR_ASSERT(update->context);
327
328
0
  pointer = update->pointer;
329
0
  WINPR_ASSERT(pointer);
330
331
0
  if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
332
0
  {
333
0
    pointer->PointerPosition = update_pointer_position;
334
0
    pointer->PointerSystem = update_pointer_system;
335
0
    pointer->PointerColor = update_pointer_color;
336
0
    pointer->PointerLarge = update_pointer_large;
337
0
    pointer->PointerNew = update_pointer_new;
338
0
    pointer->PointerCached = update_pointer_cached;
339
0
  }
340
0
}
341
342
rdpPointerCache* pointer_cache_new(rdpContext* context)
343
0
{
344
0
  rdpPointerCache* pointer_cache = NULL;
345
0
  rdpSettings* settings = NULL;
346
347
0
  WINPR_ASSERT(context);
348
349
0
  settings = context->settings;
350
0
  WINPR_ASSERT(settings);
351
352
0
  pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
353
354
0
  if (!pointer_cache)
355
0
    return NULL;
356
357
0
  pointer_cache->context = context;
358
359
  /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size
360
   * matches */
361
0
  const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
362
0
  const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
363
0
  pointer_cache->cacheSize = MAX(size, colorSize) + 1;
364
365
0
  pointer_cache->entries = (rdpPointer**)calloc(pointer_cache->cacheSize, sizeof(rdpPointer*));
366
367
0
  if (!pointer_cache->entries)
368
0
  {
369
0
    free(pointer_cache);
370
0
    return NULL;
371
0
  }
372
373
0
  return pointer_cache;
374
0
}
375
376
void pointer_cache_free(rdpPointerCache* pointer_cache)
377
0
{
378
0
  if (pointer_cache != NULL)
379
0
  {
380
0
    if (pointer_cache->entries)
381
0
    {
382
0
      for (UINT32 i = 0; i < pointer_cache->cacheSize; i++)
383
0
      {
384
0
        rdpPointer* pointer = pointer_cache->entries[i];
385
0
        pointer_free(pointer_cache->context, pointer);
386
0
      }
387
0
    }
388
389
0
    free((void*)pointer_cache->entries);
390
0
    free(pointer_cache);
391
0
  }
392
0
}
393
394
POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
395
                                                const POINTER_COLOR_UPDATE* src)
396
0
{
397
0
  POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
398
399
0
  if (!dst || !src)
400
0
    goto fail;
401
402
0
  *dst = *src;
403
404
0
  if (src->lengthAndMask > 0)
405
0
  {
406
0
    dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE));
407
408
0
    if (!dst->andMaskData)
409
0
      goto fail;
410
411
0
    memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask);
412
0
  }
413
414
0
  if (src->lengthXorMask > 0)
415
0
  {
416
0
    dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE));
417
418
0
    if (!dst->xorMaskData)
419
0
      goto fail;
420
421
0
    memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask);
422
0
  }
423
424
0
  return dst;
425
0
fail:
426
0
  free_pointer_color_update(context, dst);
427
0
  return NULL;
428
0
}
429
430
void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer)
431
0
{
432
0
  WINPR_UNUSED(context);
433
434
0
  if (!pointer)
435
0
    return;
436
437
0
  free(pointer->xorMaskData);
438
0
  free(pointer->andMaskData);
439
0
  free(pointer);
440
0
}
441
442
POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
443
                                                const POINTER_LARGE_UPDATE* src)
444
0
{
445
0
  POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
446
447
0
  if (!dst || !src)
448
0
    goto fail;
449
450
0
  *dst = *src;
451
452
0
  if (src->lengthAndMask > 0)
453
0
  {
454
0
    dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE));
455
456
0
    if (!dst->andMaskData)
457
0
      goto fail;
458
459
0
    memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask);
460
0
  }
461
462
0
  if (src->lengthXorMask > 0)
463
0
  {
464
0
    dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE));
465
466
0
    if (!dst->xorMaskData)
467
0
      goto fail;
468
469
0
    memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask);
470
0
  }
471
472
0
  return dst;
473
0
fail:
474
0
  free_pointer_large_update(context, dst);
475
0
  return NULL;
476
0
}
477
478
void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer)
479
0
{
480
0
  WINPR_UNUSED(context);
481
0
  if (!pointer)
482
0
    return;
483
484
0
  free(pointer->xorMaskData);
485
0
  free(pointer->andMaskData);
486
0
  free(pointer);
487
0
}
488
489
POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* src)
490
0
{
491
0
  POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
492
493
0
  if (!dst || !src)
494
0
    goto fail;
495
496
0
  *dst = *src;
497
498
0
  if (src->colorPtrAttr.lengthAndMask > 0)
499
0
  {
500
0
    dst->colorPtrAttr.andMaskData = calloc(src->colorPtrAttr.lengthAndMask, sizeof(BYTE));
501
502
0
    if (!dst->colorPtrAttr.andMaskData)
503
0
      goto fail;
504
505
0
    memcpy(dst->colorPtrAttr.andMaskData, src->colorPtrAttr.andMaskData,
506
0
           src->colorPtrAttr.lengthAndMask);
507
0
  }
508
509
0
  if (src->colorPtrAttr.lengthXorMask > 0)
510
0
  {
511
0
    dst->colorPtrAttr.xorMaskData = calloc(src->colorPtrAttr.lengthXorMask, sizeof(BYTE));
512
513
0
    if (!dst->colorPtrAttr.xorMaskData)
514
0
      goto fail;
515
516
0
    memcpy(dst->colorPtrAttr.xorMaskData, src->colorPtrAttr.xorMaskData,
517
0
           src->colorPtrAttr.lengthXorMask);
518
0
  }
519
520
0
  return dst;
521
0
fail:
522
0
  free_pointer_new_update(context, dst);
523
0
  return NULL;
524
0
}
525
526
void free_pointer_new_update(WINPR_ATTR_UNUSED rdpContext* context, POINTER_NEW_UPDATE* pointer)
527
0
{
528
0
  if (!pointer)
529
0
    return;
530
531
0
  free(pointer->colorPtrAttr.xorMaskData);
532
0
  free(pointer->colorPtrAttr.andMaskData);
533
0
  free(pointer);
534
0
}
535
536
POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context,
537
                                                  const POINTER_CACHED_UPDATE* pointer)
538
0
{
539
0
  POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE));
540
541
0
  if (!dst)
542
0
    goto fail;
543
544
0
  *dst = *pointer;
545
0
  return dst;
546
0
fail:
547
0
  free_pointer_cached_update(context, dst);
548
0
  return NULL;
549
0
}
550
551
void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer)
552
0
{
553
0
  WINPR_UNUSED(context);
554
0
  free(pointer);
555
0
}
556
557
void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer)
558
0
{
559
0
  WINPR_UNUSED(context);
560
0
  free(pointer);
561
0
}
562
563
POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context,
564
                                                      const POINTER_POSITION_UPDATE* pointer)
565
0
{
566
0
  POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE));
567
568
0
  if (!dst || !pointer)
569
0
    goto fail;
570
571
0
  *dst = *pointer;
572
0
  return dst;
573
0
fail:
574
0
  free_pointer_position_update(context, dst);
575
0
  return NULL;
576
0
}
577
578
void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer)
579
0
{
580
0
  WINPR_UNUSED(context);
581
0
  free(pointer);
582
0
}
583
584
POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context,
585
                                                  const POINTER_SYSTEM_UPDATE* pointer)
586
0
{
587
0
  POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
588
589
0
  if (!dst || !pointer)
590
0
    goto fail;
591
592
0
  *dst = *pointer;
593
0
  return dst;
594
0
fail:
595
0
  free_pointer_system_update(context, dst);
596
0
  return NULL;
597
0
}