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
344rdpPointerCache* pointer_cache_new(rdpContext* context)
345{
346 WINPR_ASSERT(context);
347
348 rdpSettings* settings = context->settings;
349 WINPR_ASSERT(settings);
350
351 rdpPointerCache* pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
352
353 if (!pointer_cache)
354 return nullptr;
355
356 pointer_cache->context = context;
357
358 /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size
359 * matches */
360 const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
361 const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
362 pointer_cache->cacheSize = MAX(size, colorSize) + 1;
363
364 WLog_DBG(TAG,
365 "setting cacheSize=%" PRIu32 "[ColorPointerCache=%" PRIu32 ", PointerCache=%" PRIu32
366 "]",
367 pointer_cache->cacheSize, colorSize, size);
368 pointer_cache->entries = (rdpPointer**)calloc(pointer_cache->cacheSize, sizeof(rdpPointer*));
369
370 if (!pointer_cache->entries)
371 {
372 free(pointer_cache);
373 return nullptr;
374 }
375
376 return pointer_cache;
377}
378
379void pointer_cache_free(rdpPointerCache* pointer_cache)
380{
381 if (pointer_cache != nullptr)
382 {
383 /* Reset pointer to default before deleting the cache.
384 */
385 if (pointer_cache->context && pointer_cache->context->graphics)
386 {
387 rdpPointer* pointer = pointer_cache->context->graphics->Pointer_Prototype;
388 if (pointer && pointer->SetDefault)
389 (void)pointer->SetDefault(pointer_cache->context);
390 }
391
392 if (pointer_cache->entries)
393 {
394 for (UINT32 i = 0; i < pointer_cache->cacheSize; i++)
395 {
396 rdpPointer* pointer = pointer_cache->entries[i];
397 pointer_free(pointer_cache->context, pointer);
398 }
399 }
400
401 free((void*)pointer_cache->entries);
402 free(pointer_cache);
403 }
404}
405
406POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
407 const POINTER_COLOR_UPDATE* pointer)
408{
409 POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
410
411 if (!dst || !pointer)
412 goto fail;
413
414 *dst = *pointer;
415
416 if (pointer->lengthAndMask > 0)
417 {
418 dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
419
420 if (!dst->andMaskData)
421 goto fail;
422
423 memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
424 }
425
426 if (pointer->lengthXorMask > 0)
427 {
428 dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
429
430 if (!dst->xorMaskData)
431 goto fail;
432
433 memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
434 }
435
436 return dst;
437fail:
438 free_pointer_color_update(context, dst);
439 return nullptr;
440}
441
442void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer)
443{
444 WINPR_UNUSED(context);
445
446 if (!pointer)
447 return;
448
449 free(pointer->xorMaskData);
450 free(pointer->andMaskData);
451 free(pointer);
452}
453
454POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
455 const POINTER_LARGE_UPDATE* pointer)
456{
457 POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
458
459 if (!dst || !pointer)
460 goto fail;
461
462 *dst = *pointer;
463
464 if (pointer->lengthAndMask > 0)
465 {
466 dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
467
468 if (!dst->andMaskData)
469 goto fail;
470
471 memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
472 }
473
474 if (pointer->lengthXorMask > 0)
475 {
476 dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
477
478 if (!dst->xorMaskData)
479 goto fail;
480
481 memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
482 }
483
484 return dst;
485fail:
486 free_pointer_large_update(context, dst);
487 return nullptr;
488}
489
490void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer)
491{
492 WINPR_UNUSED(context);
493 if (!pointer)
494 return;
495
496 free(pointer->xorMaskData);
497 free(pointer->andMaskData);
498 free(pointer);
499}
500
501POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* pointer)
502{
503 POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
504
505 if (!dst || !pointer)
506 goto fail;
507
508 *dst = *pointer;
509
510 if (pointer->colorPtrAttr.lengthAndMask > 0)
511 {
512 dst->colorPtrAttr.andMaskData = calloc(pointer->colorPtrAttr.lengthAndMask, sizeof(BYTE));
513
514 if (!dst->colorPtrAttr.andMaskData)
515 goto fail;
516
517 memcpy(dst->colorPtrAttr.andMaskData, pointer->colorPtrAttr.andMaskData,
518 pointer->colorPtrAttr.lengthAndMask);
519 }
520
521 if (pointer->colorPtrAttr.lengthXorMask > 0)
522 {
523 dst->colorPtrAttr.xorMaskData = calloc(pointer->colorPtrAttr.lengthXorMask, sizeof(BYTE));
524
525 if (!dst->colorPtrAttr.xorMaskData)
526 goto fail;
527
528 memcpy(dst->colorPtrAttr.xorMaskData, pointer->colorPtrAttr.xorMaskData,
529 pointer->colorPtrAttr.lengthXorMask);
530 }
531
532 return dst;
533fail:
534 free_pointer_new_update(context, dst);
535 return nullptr;
536}
537
538void free_pointer_new_update(WINPR_ATTR_UNUSED rdpContext* context, POINTER_NEW_UPDATE* pointer)
539{
540 if (!pointer)
541 return;
542
543 free(pointer->colorPtrAttr.xorMaskData);
544 free(pointer->colorPtrAttr.andMaskData);
545 free(pointer);
546}
547
548POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context,
549 const POINTER_CACHED_UPDATE* pointer)
550{
551 POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE));
552
553 if (!dst)
554 goto fail;
555
556 *dst = *pointer;
557 return dst;
558fail:
559 free_pointer_cached_update(context, dst);
560 return nullptr;
561}
562
563void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer)
564{
565 WINPR_UNUSED(context);
566 free(pointer);
567}
568
569void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer)
570{
571 WINPR_UNUSED(context);
572 free(pointer);
573}
574
575POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context,
576 const POINTER_POSITION_UPDATE* pointer)
577{
578 POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE));
579
580 if (!dst || !pointer)
581 goto fail;
582
583 *dst = *pointer;
584 return dst;
585fail:
586 free_pointer_position_update(context, dst);
587 return nullptr;
588}
589
590void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer)
591{
592 WINPR_UNUSED(context);
593 free(pointer);
594}
595
596POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context,
597 const POINTER_SYSTEM_UPDATE* pointer)
598{
599 POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
600
601 if (!dst || !pointer)
602 goto fail;
603
604 *dst = *pointer;
605 return dst;
606fail:
607 free_pointer_system_update(context, dst);
608 return nullptr;
609}
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.