FreeRDP
Loading...
Searching...
No Matches
pointer.c
1
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
35WINPR_ATTR_NODISCARD
36static const char* pointerType(bool colorCache)
37{
38
39 return colorCache ? "ColorPointerCache" : "PointerCache";
40}
41
42WINPR_ATTR_NODISCARD
43static BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
44 BOOL colorCache);
45
46WINPR_ATTR_NODISCARD
47static rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index);
48
49static void pointer_clear(rdpPointer* pointer)
50{
51 if (pointer)
52 {
53 pointer->lengthAndMask = 0;
54 free(pointer->andMaskData);
55 pointer->andMaskData = nullptr;
56
57 pointer->lengthXorMask = 0;
58 free(pointer->xorMaskData);
59 pointer->xorMaskData = nullptr;
60 }
61}
62
63static void pointer_free(rdpContext* context, rdpPointer* pointer)
64{
65 if (pointer)
66 {
67 IFCALL(pointer->Free, context, pointer);
68 pointer_clear(pointer);
69 }
70 free(pointer);
71}
72
73WINPR_ATTR_NODISCARD
74static BOOL update_pointer_position(rdpContext* context,
75 const POINTER_POSITION_UPDATE* pointer_position)
76{
77 if (!context || !context->graphics || !context->graphics->Pointer_Prototype ||
78 !pointer_position)
79 return FALSE;
80
81 const BOOL GrabMouse = freerdp_settings_get_bool(context->settings, FreeRDP_GrabMouse);
82 if (!GrabMouse)
83 return TRUE;
84
85 const rdpPointer* pointer = context->graphics->Pointer_Prototype;
86 WINPR_ASSERT(pointer);
87
88 return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos,
89 pointer_position->yPos);
90}
91
92WINPR_ATTR_NODISCARD
93static BOOL update_pointer_system(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system)
94{
95 if (!context || !context->graphics || !context->graphics->Pointer_Prototype || !pointer_system)
96 return FALSE;
97
98 rdpPointer* pointer = context->graphics->Pointer_Prototype;
99
100 switch (pointer_system->type)
101 {
102 case SYSPTR_NULL:
103 return IFCALLRESULT(TRUE, pointer->SetNull, context);
104
105 case SYSPTR_DEFAULT:
106 return IFCALLRESULT(TRUE, pointer->SetDefault, context);
107
108 default:
109 WLog_ERR(TAG, "Unknown system pointer type (0x%08" PRIX32 ")", pointer_system->type);
110 }
111 return TRUE;
112}
113
114WINPR_ATTR_NODISCARD
115static BOOL update_pointer_copy_andxor(rdpPointer* pointer, const BYTE* andMaskData,
116 size_t lengthAndMask, const BYTE* xorMaskData,
117 size_t lengthXorMask)
118{
119 WINPR_ASSERT(pointer);
120
121 pointer_clear(pointer);
122 if (lengthAndMask && andMaskData)
123 {
124 if (lengthAndMask > UINT32_MAX)
125 return FALSE;
126 pointer->lengthAndMask = (UINT32)lengthAndMask;
127 pointer->andMaskData = (BYTE*)malloc(lengthAndMask);
128 if (!pointer->andMaskData)
129 return FALSE;
130
131 CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask);
132 }
133
134 if (lengthXorMask && xorMaskData)
135 {
136 if (lengthXorMask > UINT32_MAX)
137 return FALSE;
138 pointer->lengthXorMask = (UINT32)lengthXorMask;
139 pointer->xorMaskData = (BYTE*)malloc(lengthXorMask);
140 if (!pointer->xorMaskData)
141 return FALSE;
142
143 CopyMemory(pointer->xorMaskData, xorMaskData, lengthXorMask);
144 }
145
146 return TRUE;
147}
148
149WINPR_ATTR_NODISCARD
150static BOOL update_pointer_color(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color)
151{
152 WINPR_ASSERT(context);
153 WINPR_ASSERT(pointer_color);
154
155 rdpCache* cache = context->cache;
156 WINPR_ASSERT(cache);
157
158 rdpPointer* pointer = Pointer_Alloc(context);
159
160 if (pointer == nullptr)
161 return FALSE;
162 pointer->xorBpp = 24;
163 pointer->xPos = pointer_color->hotSpotX;
164 pointer->yPos = pointer_color->hotSpotY;
165 pointer->width = pointer_color->width;
166 pointer->height = pointer_color->height;
167
168 if (!update_pointer_copy_andxor(pointer, pointer_color->andMaskData,
169 pointer_color->lengthAndMask, pointer_color->xorMaskData,
170 pointer_color->lengthXorMask))
171 goto out_fail;
172
173 if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
174 goto out_fail;
175
176 if (!pointer_cache_put(cache->pointer, pointer_color->cacheIndex, pointer, TRUE))
177 goto out_fail;
178
179 return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
180
181out_fail:
182 pointer_free(context, pointer);
183 return FALSE;
184}
185
186WINPR_ATTR_NODISCARD
187static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large)
188{
189 WINPR_ASSERT(context);
190 WINPR_ASSERT(pointer_large);
191
192 rdpCache* cache = context->cache;
193 WINPR_ASSERT(cache);
194
195 rdpPointer* pointer = Pointer_Alloc(context);
196 if (pointer == nullptr)
197 return FALSE;
198 pointer->xorBpp = pointer_large->xorBpp;
199 pointer->xPos = pointer_large->hotSpotX;
200 pointer->yPos = pointer_large->hotSpotY;
201 pointer->width = pointer_large->width;
202 pointer->height = pointer_large->height;
203
204 if (!update_pointer_copy_andxor(pointer, pointer_large->andMaskData,
205 pointer_large->lengthAndMask, pointer_large->xorMaskData,
206 pointer_large->lengthXorMask))
207 goto out_fail;
208
209 if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
210 goto out_fail;
211
212 if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer, FALSE))
213 goto out_fail;
214
215 return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
216
217out_fail:
218 pointer_free(context, pointer);
219 return FALSE;
220}
221
222WINPR_ATTR_NODISCARD
223static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
224{
225 if (!context || !pointer_new)
226 return FALSE;
227
228 rdpCache* cache = context->cache;
229 rdpPointer* pointer = Pointer_Alloc(context);
230
231 if (!pointer)
232 return FALSE;
233
234 pointer->xorBpp = pointer_new->xorBpp;
235 pointer->xPos = pointer_new->colorPtrAttr.hotSpotX;
236 pointer->yPos = pointer_new->colorPtrAttr.hotSpotY;
237 pointer->width = pointer_new->colorPtrAttr.width;
238 pointer->height = pointer_new->colorPtrAttr.height;
239 if (!update_pointer_copy_andxor(
240 pointer, pointer_new->colorPtrAttr.andMaskData, pointer_new->colorPtrAttr.lengthAndMask,
241 pointer_new->colorPtrAttr.xorMaskData, pointer_new->colorPtrAttr.lengthXorMask))
242 goto out_fail;
243
244 if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
245 goto out_fail;
246
247 if (!pointer_cache_put(cache->pointer, pointer_new->colorPtrAttr.cacheIndex, pointer, FALSE))
248 goto out_fail;
249
250 return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
251
252out_fail:
253 pointer_free(context, pointer);
254 return FALSE;
255}
256
257WINPR_ATTR_NODISCARD
258static BOOL update_pointer_cached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached)
259{
260 WINPR_ASSERT(context);
261 WINPR_ASSERT(pointer_cached);
262
263 rdpCache* cache = context->cache;
264 WINPR_ASSERT(cache);
265
266 rdpPointer* pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex);
267
268 if (pointer != nullptr)
269 return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
270
271 return FALSE;
272}
273
274rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index)
275{
276 WINPR_ASSERT(pointer_cache);
277
278 if (index >= pointer_cache->cacheSize)
279 {
280 WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index,
281 pointer_cache->cacheSize);
282 return nullptr;
283 }
284
285 WINPR_ASSERT(pointer_cache->entries);
286 return pointer_cache->entries[index];
287}
288
289BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
290 BOOL colorCache)
291{
292 const FreeRDP_Settings_Keys_UInt32 id =
293 colorCache ? FreeRDP_ColorPointerCacheSize : FreeRDP_PointerCacheSize;
294
295 WINPR_ASSERT(pointer_cache);
296 WINPR_ASSERT(pointer_cache->context);
297
298 const UINT32 size = freerdp_settings_get_uint32(pointer_cache->context->settings, id);
299 WLog_DBG(TAG, "[%s] pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32 "]",
300 pointerType(colorCache), index, pointer_cache->cacheSize, size);
301 if (index >= pointer_cache->cacheSize)
302 {
303 WLog_ERR(TAG,
304 "[%s] invalid pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32 "]",
305 pointerType(colorCache), index, pointer_cache->cacheSize, size);
306 return FALSE;
307 }
308 if (index >= size)
309 {
310 WLog_WARN(TAG,
311 "[%s] suspicious pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32
312 "]",
313 pointerType(colorCache), index, pointer_cache->cacheSize, size);
314 }
315
316 WINPR_ASSERT(pointer_cache->entries);
317 rdpPointer* prevPointer = pointer_cache->entries[index];
318 pointer_free(pointer_cache->context, prevPointer);
319 pointer_cache->entries[index] = pointer;
320 return TRUE;
321}
322
323void pointer_cache_register_callbacks(rdpUpdate* update)
324{
325 rdpPointerUpdate* pointer = nullptr;
326
327 WINPR_ASSERT(update);
328 WINPR_ASSERT(update->context);
329
330 pointer = update->pointer;
331 WINPR_ASSERT(pointer);
332
333 if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
334 {
335 pointer->PointerPosition = update_pointer_position;
336 pointer->PointerSystem = update_pointer_system;
337 pointer->PointerColor = update_pointer_color;
338 pointer->PointerLarge = update_pointer_large;
339 pointer->PointerNew = update_pointer_new;
340 pointer->PointerCached = update_pointer_cached;
341 }
342}
343
344static void resetPointer(rdpPointerCache* pointerCache)
345{
346 WINPR_ASSERT(pointerCache);
347
348 if (!pointerCache->context || !pointerCache->context->graphics)
349 return;
350
351 rdpPointer* pointer = pointerCache->context->graphics->Pointer_Prototype;
352 if (!pointer || !pointer->SetDefault)
353 return;
354
355 if (!pointer->SetDefault(pointerCache->context))
356 WLog_WARN(TAG, "rdpPointer::SetDefault failed");
357}
358
359BOOL pointer_cache_resize(rdpPointerCache* pointerCache)
360{
361 WINPR_ASSERT(pointerCache);
362
363 rdpContext* context = pointerCache->context;
364 WINPR_ASSERT(context);
365
366 rdpSettings* settings = context->settings;
367 WINPR_ASSERT(settings);
368
369 /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size
370 * matches */
371 const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
372 const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
373
374 const size_t cacheSize = MAX(size, colorSize) + 1;
375 if (cacheSize > SIZE_MAX / sizeof(rdpPointer*))
376 return FALSE;
377
378 WLog_DBG(TAG,
379 "setting cacheSize=%" PRIuz "[ColorPointerCache=%" PRIu32 ", PointerCache=%" PRIu32
380 "]",
381 cacheSize, colorSize, size);
382
383 /* Reset pointer to default before deleting the cache.
384 */
385 resetPointer(pointerCache);
386
387 for (size_t i = cacheSize; i < pointerCache->cacheSize; i++)
388 {
389 rdpPointer* pointer = pointerCache->entries[i];
390 pointer_free(pointerCache->context, pointer);
391 }
392
393 void* tmp = realloc((void*)pointerCache->entries, cacheSize * sizeof(rdpPointer*));
394 if (!tmp)
395 return FALSE;
396
397 pointerCache->entries = (rdpPointer**)tmp;
398 if (cacheSize > pointerCache->cacheSize)
399 {
400 const size_t rsize = (cacheSize - pointerCache->cacheSize) * sizeof(rdpPointer*);
401 void* dst = (void*)&pointerCache->entries[pointerCache->cacheSize];
402 memset(dst, 0, rsize);
403 }
404 pointerCache->cacheSize = WINPR_ASSERTING_INT_CAST(UINT32, cacheSize);
405
406 return TRUE;
407}
408
409rdpPointerCache* pointer_cache_new(rdpContext* context)
410{
411 WINPR_ASSERT(context);
412
413 rdpPointerCache* pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
414
415 if (!pointer_cache)
416 return nullptr;
417
418 pointer_cache->context = context;
419
420 if (!pointer_cache_resize(pointer_cache))
421 {
422 free(pointer_cache);
423 return nullptr;
424 }
425
426 return pointer_cache;
427}
428
429void pointer_cache_free(rdpPointerCache* pointer_cache)
430{
431 if (pointer_cache != nullptr)
432 {
433 /* Reset pointer to default before deleting the cache.
434 */
435 resetPointer(pointer_cache);
436
437 if (pointer_cache->entries)
438 {
439 for (UINT32 i = 0; i < pointer_cache->cacheSize; i++)
440 {
441 rdpPointer* pointer = pointer_cache->entries[i];
442 pointer_free(pointer_cache->context, pointer);
443 }
444 }
445
446 free((void*)pointer_cache->entries);
447 free(pointer_cache);
448 }
449}
450
451POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
452 const POINTER_COLOR_UPDATE* pointer)
453{
454 POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
455
456 if (!dst || !pointer)
457 goto fail;
458
459 *dst = *pointer;
460
461 if (pointer->lengthAndMask > 0)
462 {
463 dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
464
465 if (!dst->andMaskData)
466 goto fail;
467
468 memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
469 }
470
471 if (pointer->lengthXorMask > 0)
472 {
473 dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
474
475 if (!dst->xorMaskData)
476 goto fail;
477
478 memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
479 }
480
481 return dst;
482fail:
483 free_pointer_color_update(context, dst);
484 return nullptr;
485}
486
487void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer)
488{
489 WINPR_UNUSED(context);
490
491 if (!pointer)
492 return;
493
494 free(pointer->xorMaskData);
495 free(pointer->andMaskData);
496 free(pointer);
497}
498
499POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
500 const POINTER_LARGE_UPDATE* pointer)
501{
502 POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
503
504 if (!dst || !pointer)
505 goto fail;
506
507 *dst = *pointer;
508
509 if (pointer->lengthAndMask > 0)
510 {
511 dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
512
513 if (!dst->andMaskData)
514 goto fail;
515
516 memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
517 }
518
519 if (pointer->lengthXorMask > 0)
520 {
521 dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
522
523 if (!dst->xorMaskData)
524 goto fail;
525
526 memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
527 }
528
529 return dst;
530fail:
531 free_pointer_large_update(context, dst);
532 return nullptr;
533}
534
535void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer)
536{
537 WINPR_UNUSED(context);
538 if (!pointer)
539 return;
540
541 free(pointer->xorMaskData);
542 free(pointer->andMaskData);
543 free(pointer);
544}
545
546POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* pointer)
547{
548 POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
549
550 if (!dst || !pointer)
551 goto fail;
552
553 *dst = *pointer;
554
555 if (pointer->colorPtrAttr.lengthAndMask > 0)
556 {
557 dst->colorPtrAttr.andMaskData = calloc(pointer->colorPtrAttr.lengthAndMask, sizeof(BYTE));
558
559 if (!dst->colorPtrAttr.andMaskData)
560 goto fail;
561
562 memcpy(dst->colorPtrAttr.andMaskData, pointer->colorPtrAttr.andMaskData,
563 pointer->colorPtrAttr.lengthAndMask);
564 }
565
566 if (pointer->colorPtrAttr.lengthXorMask > 0)
567 {
568 dst->colorPtrAttr.xorMaskData = calloc(pointer->colorPtrAttr.lengthXorMask, sizeof(BYTE));
569
570 if (!dst->colorPtrAttr.xorMaskData)
571 goto fail;
572
573 memcpy(dst->colorPtrAttr.xorMaskData, pointer->colorPtrAttr.xorMaskData,
574 pointer->colorPtrAttr.lengthXorMask);
575 }
576
577 return dst;
578fail:
579 free_pointer_new_update(context, dst);
580 return nullptr;
581}
582
583void free_pointer_new_update(WINPR_ATTR_UNUSED rdpContext* context, POINTER_NEW_UPDATE* pointer)
584{
585 if (!pointer)
586 return;
587
588 free(pointer->colorPtrAttr.xorMaskData);
589 free(pointer->colorPtrAttr.andMaskData);
590 free(pointer);
591}
592
593POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context,
594 const POINTER_CACHED_UPDATE* pointer)
595{
596 POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE));
597
598 if (!dst)
599 goto fail;
600
601 *dst = *pointer;
602 return dst;
603fail:
604 free_pointer_cached_update(context, dst);
605 return nullptr;
606}
607
608void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer)
609{
610 WINPR_UNUSED(context);
611 free(pointer);
612}
613
614void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer)
615{
616 WINPR_UNUSED(context);
617 free(pointer);
618}
619
620POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context,
621 const POINTER_POSITION_UPDATE* pointer)
622{
623 POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE));
624
625 if (!dst || !pointer)
626 goto fail;
627
628 *dst = *pointer;
629 return dst;
630fail:
631 free_pointer_position_update(context, dst);
632 return nullptr;
633}
634
635void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer)
636{
637 WINPR_UNUSED(context);
638 free(pointer);
639}
640
641POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context,
642 const POINTER_SYSTEM_UPDATE* pointer)
643{
644 POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
645
646 if (!dst || !pointer)
647 goto fail;
648
649 *dst = *pointer;
650 return dst;
651fail:
652 free_pointer_system_update(context, dst);
653 return nullptr;
654}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.