FreeRDP
Loading...
Searching...
No Matches
cache/bitmap.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/cast.h>
27
28#include <freerdp/freerdp.h>
29#include <freerdp/constants.h>
30#include <winpr/stream.h>
31
32#include <freerdp/log.h>
33#include <freerdp/gdi/bitmap.h>
34
35#include "../gdi/gdi.h"
36#include "../core/graphics.h"
37
38#include "bitmap.h"
39#include "cache.h"
40
41#define TAG FREERDP_TAG("cache.bitmap")
42
43static rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index);
44static BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index,
45 rdpBitmap* bitmap);
46
47static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
48{
49 rdpBitmap* bitmap = nullptr;
50 rdpCache* cache = nullptr;
51
52 cache = context->cache;
53
54 if (memblt->cacheId == 0xFF)
55 bitmap = offscreen_cache_get(cache->offscreen, memblt->cacheIndex);
56 else
57 bitmap = bitmap_cache_get(cache->bitmap, (BYTE)memblt->cacheId, memblt->cacheIndex);
58
59 /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
60 if (bitmap == nullptr)
61 return TRUE;
62
63 memblt->bitmap = bitmap;
64 return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
65}
66
67static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
68{
69 rdpBitmap* bitmap = nullptr;
70 rdpCache* cache = context->cache;
71 rdpBrush* brush = &mem3blt->brush;
72 BOOL ret = TRUE;
73
74 if (mem3blt->cacheId == 0xFF)
75 bitmap = offscreen_cache_get(cache->offscreen, mem3blt->cacheIndex);
76 else
77 bitmap = bitmap_cache_get(cache->bitmap, (BYTE)mem3blt->cacheId, mem3blt->cacheIndex);
78
79 /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
80 if (!bitmap)
81 return TRUE;
82
83 const BYTE style = WINPR_ASSERTING_INT_CAST(UINT8, brush->style);
84 BYTE* data = brush->data;
85
86 if (brush->style & CACHED_BRUSH)
87 {
88 brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
89
90 if (!brush->data)
91 return FALSE;
92
93 brush->style = 0x03;
94 }
95
96 mem3blt->bitmap = bitmap;
97 IFCALLRET(cache->bitmap->Mem3Blt, ret, context, mem3blt);
98 brush->style = style;
99 brush->data = data;
100 return ret;
101}
102
103static BOOL update_gdi_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cacheBitmap)
104{
105 rdpBitmap* bitmap = nullptr;
106 rdpBitmap* prevBitmap = nullptr;
107 rdpCache* cache = context->cache;
108 bitmap = Bitmap_Alloc(context);
109
110 if (!bitmap)
111 return FALSE;
112
113 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapWidth),
114 WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapHeight)))
115 goto fail;
116
117 if (!bitmap->Decompress(context, bitmap, cacheBitmap->bitmapDataStream,
118 cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight,
119 cacheBitmap->bitmapBpp, cacheBitmap->bitmapLength,
120 cacheBitmap->compressed, RDP_CODEC_ID_NONE))
121 goto fail;
122
123 if (!bitmap->New(context, bitmap))
124 goto fail;
125
126 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex);
127 Bitmap_Free(context, prevBitmap);
128 if (!bitmap_cache_put(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex, bitmap))
129 goto fail;
130 return TRUE;
131
132fail:
133 Bitmap_Free(context, bitmap);
134 return FALSE;
135}
136
137static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
138
139{
140 rdpBitmap* prevBitmap = nullptr;
141 rdpCache* cache = context->cache;
142 rdpSettings* settings = context->settings;
143 rdpBitmap* bitmap = Bitmap_Alloc(context);
144
145 if (!bitmap)
146 return FALSE;
147
148 const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
149 bitmap->key64 = ((UINT64)cacheBitmapV2->key1 | (((UINT64)cacheBitmapV2->key2) << 32));
150
151 if (!cacheBitmapV2->bitmapBpp)
152 cacheBitmapV2->bitmapBpp = ColorDepth;
153
154 if ((ColorDepth == 15) && (cacheBitmapV2->bitmapBpp == 16))
155 cacheBitmapV2->bitmapBpp = ColorDepth;
156
157 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapWidth),
158 WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapHeight)))
159 goto fail;
160
161 if (!bitmap->Decompress(context, bitmap, cacheBitmapV2->bitmapDataStream,
162 cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight,
163 cacheBitmapV2->bitmapBpp, cacheBitmapV2->bitmapLength,
164 cacheBitmapV2->compressed, RDP_CODEC_ID_NONE))
165 goto fail;
166
167 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex);
168
169 if (!bitmap->New(context, bitmap))
170 goto fail;
171
172 Bitmap_Free(context, prevBitmap);
173 if (!bitmap_cache_put(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex, bitmap))
174 goto fail;
175 return TRUE;
176
177fail:
178 Bitmap_Free(context, bitmap);
179 return FALSE;
180}
181
182static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cacheBitmapV3)
183{
184 rdpBitmap* bitmap = nullptr;
185 rdpBitmap* prevBitmap = nullptr;
186 BOOL compressed = TRUE;
187 rdpCache* cache = context->cache;
188 rdpSettings* settings = context->settings;
189 BITMAP_DATA_EX* bitmapData = &cacheBitmapV3->bitmapData;
190 bitmap = Bitmap_Alloc(context);
191
192 if (!bitmap)
193 return FALSE;
194
195 const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
196 bitmap->key64 = ((UINT64)cacheBitmapV3->key1 | (((UINT64)cacheBitmapV3->key2) << 32));
197
198 if (!cacheBitmapV3->bpp)
199 cacheBitmapV3->bpp = ColorDepth;
200
201 compressed = (bitmapData->codecID != RDP_CODEC_ID_NONE);
202
203 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->width),
204 WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->height)))
205 goto fail;
206
207 if (!bitmap->Decompress(context, bitmap, bitmapData->data, bitmapData->width,
208 bitmapData->height, bitmapData->bpp, bitmapData->length, compressed,
209 bitmapData->codecID))
210 goto fail;
211
212 if (!bitmap->New(context, bitmap))
213 goto fail;
214
215 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex);
216 Bitmap_Free(context, prevBitmap);
217 if (!bitmap_cache_put(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex, bitmap))
218 goto fail;
219 return TRUE;
220
221fail:
222 Bitmap_Free(context, bitmap);
223 return FALSE;
224}
225
226rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index)
227{
228 rdpBitmap* bitmap = nullptr;
229
230 if (id >= bitmapCache->maxCells)
231 {
232 WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
233 return nullptr;
234 }
235
236 if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
237 {
238 index = bitmapCache->cells[id].number;
239 }
240 else if (index > bitmapCache->cells[id].number)
241 {
242 WLog_ERR(TAG, "get invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
243 return nullptr;
244 }
245
246 bitmap = bitmapCache->cells[id].entries[index];
247 return bitmap;
248}
249
250BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index, rdpBitmap* bitmap)
251{
252 WINPR_ASSERT(bitmapCache);
253 if (id > bitmapCache->maxCells)
254 {
255 WLog_ERR(TAG, "put invalid bitmap cell id: %" PRIu32 "", id);
256 return FALSE;
257 }
258
259 if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
260 {
261 index = bitmapCache->cells[id].number;
262 }
263 else if (index > bitmapCache->cells[id].number)
264 {
265 WLog_ERR(TAG, "put invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
266 return FALSE;
267 }
268
269 bitmapCache->cells[id].entries[index] = bitmap;
270 return TRUE;
271}
272
273void bitmap_cache_register_callbacks(rdpUpdate* update)
274{
275 rdpCache* cache = nullptr;
276
277 WINPR_ASSERT(update);
278 WINPR_ASSERT(update->context);
279 WINPR_ASSERT(update->context->cache);
280
281 cache = update->context->cache;
282 WINPR_ASSERT(cache);
283
284 if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
285 {
286 cache->bitmap->MemBlt = update->primary->MemBlt;
287 cache->bitmap->Mem3Blt = update->primary->Mem3Blt;
288 update->primary->MemBlt = update_gdi_memblt;
289 update->primary->Mem3Blt = update_gdi_mem3blt;
290 update->secondary->CacheBitmap = update_gdi_cache_bitmap;
291 update->secondary->CacheBitmapV2 = update_gdi_cache_bitmap_v2;
292 update->secondary->CacheBitmapV3 = update_gdi_cache_bitmap_v3;
293 update->BitmapUpdate = gdi_bitmap_update;
294 }
295}
296
297static int bitmap_cache_save_persistent(rdpBitmapCache* bitmapCache)
298{
299 WINPR_ASSERT(bitmapCache);
300
301 rdpContext* context = bitmapCache->context;
302 rdpSettings* settings = context->settings;
303
304 const UINT32 version = freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheVersion);
305
306 if (version != 2)
307 return 0; /* persistent bitmap cache already saved in egfx channel */
308
309 if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
310 return 0;
311
312 const char* BitmapCachePersistFile =
313 freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
314 if (!BitmapCachePersistFile)
315 return 0;
316
317 rdpPersistentCache* persistent = persistent_cache_new();
318
319 if (!persistent)
320 return -1;
321
322 int status = persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, version);
323
324 if (status < 1)
325 goto end;
326
327 if (bitmapCache->cells)
328 {
329 for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
330 {
331 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
332 for (UINT32 j = 0; j < cell->number + 1 && cell->entries; j++)
333 {
334 PERSISTENT_CACHE_ENTRY cacheEntry = WINPR_C_ARRAY_INIT;
335 rdpBitmap* bitmap = cell->entries[j];
336
337 if (!bitmap || !bitmap->key64)
338 continue;
339
340 cacheEntry.key64 = bitmap->key64;
341
342 cacheEntry.width = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->width);
343 cacheEntry.height = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->height);
344 const UINT64 size = 4ULL * bitmap->width * bitmap->height;
345 if (size > UINT32_MAX)
346 continue;
347 cacheEntry.size = (UINT32)size;
348 cacheEntry.flags = 0;
349 cacheEntry.data = bitmap->data;
350
351 if (persistent_cache_write_entry(persistent, &cacheEntry) < 1)
352 {
353 status = -1;
354 goto end;
355 }
356 }
357 }
358 }
359
360 status = 1;
361
362end:
363 persistent_cache_free(persistent);
364 return status;
365}
366
367static void bitmap_cache_cell_free(rdpBitmapCache* bitmapCache)
368{
369 WINPR_ASSERT(bitmapCache);
370 if (!bitmapCache->cells)
371 return;
372
373 /* iterate through maxCells + 1 to also free the overallocated extra slot */
374 for (UINT32 i = 0; i <= bitmapCache->maxCells; i++)
375 {
376 UINT32 j = 0;
377 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
378
379 if (!cell->entries)
380 continue;
381
382 for (j = 0; j < cell->number + 1; j++)
383 {
384 rdpBitmap* bitmap = cell->entries[j];
385 Bitmap_Free(bitmapCache->context, bitmap);
386 }
387
388 free((void*)cell->entries);
389 }
390
391 free(bitmapCache->cells);
392
393 bitmapCache->cells = nullptr;
394}
395
396BOOL bitmap_cache_resize(rdpBitmapCache* bitmapCache)
397{
398 if (!bitmapCache)
399 return FALSE;
400
401 rdpContext* context = bitmapCache->context;
402 WINPR_ASSERT(context);
403
404 rdpSettings* settings = context->settings;
405 WINPR_ASSERT(settings);
406
407 const UINT32 BitmapCacheV2NumCells =
408 freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
409
410 if (BitmapCacheV2NumCells == bitmapCache->maxCells)
411 return TRUE;
412
413 bitmap_cache_cell_free(bitmapCache);
414
415 /* overallocate by 1. older RDP servers do send a off by 1 cache index. */
416 bitmapCache->cells =
417 (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL));
418
419 if (!bitmapCache->cells)
420 return FALSE;
421 bitmapCache->maxCells = BitmapCacheV2NumCells;
422
423 for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
424 {
425 const BITMAP_CACHE_V2_CELL_INFO* info =
426 freerdp_settings_get_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, i);
427 if (!info)
428 return FALSE;
429
430 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
431 UINT32 nr = info->numEntries;
432 /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
433 cell->entries = (rdpBitmap**)calloc((nr + 1), sizeof(rdpBitmap*));
434
435 if (!cell->entries)
436 return FALSE;
437 cell->number = nr;
438 }
439
440 /* initialize the overallocated extra slot for old RDP servers that send
441 * cacheId == maxCells; use a minimal allocation since no protocol-negotiated
442 * capacity exists for this slot */
443 {
444 BITMAP_V2_CELL* extra = &bitmapCache->cells[bitmapCache->maxCells];
445 /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
446 extra->entries = (rdpBitmap**)calloc(1, sizeof(rdpBitmap*));
447
448 if (!extra->entries)
449 return FALSE;
450 extra->number = 0;
451 }
452
453 return TRUE;
454}
455
456rdpBitmapCache* bitmap_cache_new(rdpContext* context)
457{
458 WINPR_ASSERT(context);
459
460 rdpBitmapCache* bitmapCache = (rdpBitmapCache*)calloc(1, sizeof(rdpBitmapCache));
461
462 if (!bitmapCache)
463 return nullptr;
464
465 bitmapCache->context = context;
466
467 if (!bitmap_cache_resize(bitmapCache))
468 goto fail;
469
470 return bitmapCache;
471fail:
472 WINPR_PRAGMA_DIAG_PUSH
473 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
474 bitmap_cache_free(bitmapCache);
475 WINPR_PRAGMA_DIAG_POP
476 return nullptr;
477}
478
479void bitmap_cache_free(rdpBitmapCache* bitmapCache)
480{
481 if (!bitmapCache)
482 return;
483
484 bitmap_cache_save_persistent(bitmapCache);
485
486 bitmap_cache_cell_free(bitmapCache);
487
488 persistent_cache_free(bitmapCache->persistent);
489
490 free(bitmapCache);
491}
492
493static void free_bitmap_data(BITMAP_DATA* data, size_t count)
494{
495 if (!data)
496 return;
497
498 for (size_t x = 0; x < count; x++)
499 free(data[x].bitmapDataStream);
500
501 free(data);
502}
503
504static BITMAP_DATA* copy_bitmap_data(const BITMAP_DATA* data, size_t count)
505{
506 BITMAP_DATA* dst = (BITMAP_DATA*)calloc(count, sizeof(BITMAP_DATA));
507
508 if (!dst)
509 goto fail;
510
511 for (size_t x = 0; x < count; x++)
512 {
513 dst[x] = data[x];
514
515 if (data[x].bitmapLength > 0)
516 {
517 dst[x].bitmapDataStream = malloc(data[x].bitmapLength);
518
519 if (!dst[x].bitmapDataStream)
520 goto fail;
521
522 memcpy(dst[x].bitmapDataStream, data[x].bitmapDataStream, data[x].bitmapLength);
523 }
524 }
525
526 return dst;
527fail:
528 free_bitmap_data(dst, count);
529 return nullptr;
530}
531
532void free_bitmap_update(WINPR_ATTR_UNUSED rdpContext* context,
533 WINPR_ATTR_UNUSED BITMAP_UPDATE* pointer)
534{
535 if (!pointer)
536 return;
537
538 free_bitmap_data(pointer->rectangles, pointer->number);
539 free(pointer);
540}
541
542BITMAP_UPDATE* copy_bitmap_update(rdpContext* context, const BITMAP_UPDATE* pointer)
543{
544 BITMAP_UPDATE* dst = calloc(1, sizeof(BITMAP_UPDATE));
545
546 if (!dst || !pointer)
547 goto fail;
548
549 *dst = *pointer;
550 dst->rectangles = copy_bitmap_data(pointer->rectangles, pointer->number);
551
552 if (!dst->rectangles)
553 goto fail;
554
555 return dst;
556fail:
557 WINPR_PRAGMA_DIAG_PUSH
558 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
559 free_bitmap_update(context, dst);
560 WINPR_PRAGMA_DIAG_POP
561 return nullptr;
562}
563
564CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context, const CACHE_BITMAP_ORDER* order)
565{
566 CACHE_BITMAP_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_ORDER));
567
568 if (!dst || !order)
569 goto fail;
570
571 *dst = *order;
572
573 if (order->bitmapLength > 0)
574 {
575 dst->bitmapDataStream = malloc(order->bitmapLength);
576
577 if (!dst->bitmapDataStream)
578 goto fail;
579
580 memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
581 }
582
583 return dst;
584fail:
585 WINPR_PRAGMA_DIAG_PUSH
586 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
587 free_cache_bitmap_order(context, dst);
588 WINPR_PRAGMA_DIAG_POP
589 return nullptr;
590}
591
592void free_cache_bitmap_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_BITMAP_ORDER* order)
593{
594 if (order)
595 free(order->bitmapDataStream);
596
597 free(order);
598}
599
600CACHE_BITMAP_V2_ORDER* copy_cache_bitmap_v2_order(rdpContext* context,
601 const CACHE_BITMAP_V2_ORDER* order)
602{
603 CACHE_BITMAP_V2_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V2_ORDER));
604
605 if (!dst || !order)
606 goto fail;
607
608 *dst = *order;
609
610 if (order->bitmapLength > 0)
611 {
612 dst->bitmapDataStream = malloc(order->bitmapLength);
613
614 if (!dst->bitmapDataStream)
615 goto fail;
616
617 memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
618 }
619
620 return dst;
621fail:
622 WINPR_PRAGMA_DIAG_PUSH
623 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
624 free_cache_bitmap_v2_order(context, dst);
625 WINPR_PRAGMA_DIAG_POP
626 return nullptr;
627}
628
629void free_cache_bitmap_v2_order(WINPR_ATTR_UNUSED rdpContext* context,
630 WINPR_ATTR_UNUSED CACHE_BITMAP_V2_ORDER* order)
631{
632 if (order)
633 free(order->bitmapDataStream);
634
635 free(order);
636}
637
638CACHE_BITMAP_V3_ORDER* copy_cache_bitmap_v3_order(rdpContext* context,
639 const CACHE_BITMAP_V3_ORDER* order)
640{
641 CACHE_BITMAP_V3_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V3_ORDER));
642
643 if (!dst || !order)
644 goto fail;
645
646 *dst = *order;
647
648 if (order->bitmapData.length > 0)
649 {
650 dst->bitmapData.data = malloc(order->bitmapData.length);
651
652 if (!dst->bitmapData.data)
653 goto fail;
654
655 memcpy(dst->bitmapData.data, order->bitmapData.data, order->bitmapData.length);
656 }
657
658 return dst;
659fail:
660 WINPR_PRAGMA_DIAG_PUSH
661 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
662 free_cache_bitmap_v3_order(context, dst);
663 WINPR_PRAGMA_DIAG_POP
664 return nullptr;
665}
666
667void free_cache_bitmap_v3_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
668{
669 if (order)
670 free(order->bitmapData.data);
671
672 free(order);
673}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
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.
Definition persistent.h:70