summaryrefslogtreecommitdiffstats
path: root/libfreerdp/cache
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /libfreerdp/cache
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libfreerdp/cache')
-rw-r--r--libfreerdp/cache/CMakeLists.txt39
-rw-r--r--libfreerdp/cache/bitmap.c608
-rw-r--r--libfreerdp/cache/bitmap.h95
-rw-r--r--libfreerdp/cache/brush.c326
-rw-r--r--libfreerdp/cache/brush.h57
-rw-r--r--libfreerdp/cache/cache.c152
-rw-r--r--libfreerdp/cache/cache.h73
-rw-r--r--libfreerdp/cache/glyph.c892
-rw-r--r--libfreerdp/cache/glyph.h78
-rw-r--r--libfreerdp/cache/nine_grid.c169
-rw-r--r--libfreerdp/cache/nine_grid.h50
-rw-r--r--libfreerdp/cache/offscreen.c243
-rw-r--r--libfreerdp/cache/offscreen.h50
-rw-r--r--libfreerdp/cache/palette.c142
-rw-r--r--libfreerdp/cache/palette.h65
-rw-r--r--libfreerdp/cache/persistent.c374
-rw-r--r--libfreerdp/cache/pointer.c593
-rw-r--r--libfreerdp/cache/pointer.h95
18 files changed, 4101 insertions, 0 deletions
diff --git a/libfreerdp/cache/CMakeLists.txt b/libfreerdp/cache/CMakeLists.txt
new file mode 100644
index 0000000..369faec
--- /dev/null
+++ b/libfreerdp/cache/CMakeLists.txt
@@ -0,0 +1,39 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# libfreerdp-cache cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(MODULE_NAME "freerdp-cache")
+set(MODULE_PREFIX "FREERDP_CACHE")
+
+freerdp_module_add(
+ brush.c
+ brush.h
+ pointer.c
+ pointer.h
+ bitmap.c
+ bitmap.h
+ persistent.c
+ nine_grid.c
+ nine_grid.h
+ offscreen.c
+ offscreen.h
+ palette.c
+ palette.h
+ glyph.c
+ glyph.h
+ cache.c
+ cache.h)
+
diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c
new file mode 100644
index 0000000..dd5ae1d
--- /dev/null
+++ b/libfreerdp/cache/bitmap.c
@@ -0,0 +1,608 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Bitmap Cache V2
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/constants.h>
+#include <winpr/stream.h>
+
+#include <freerdp/log.h>
+#include <freerdp/gdi/bitmap.h>
+
+#include "../gdi/gdi.h"
+#include "../core/graphics.h"
+
+#include "bitmap.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.bitmap")
+
+static rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index);
+static BOOL bitmap_cache_put(rdpBitmapCache* bitmap_cache, UINT32 id, UINT32 index,
+ rdpBitmap* bitmap);
+
+static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
+{
+ rdpBitmap* bitmap = NULL;
+ rdpCache* cache = NULL;
+
+ cache = context->cache;
+
+ if (memblt->cacheId == 0xFF)
+ bitmap = offscreen_cache_get(cache->offscreen, memblt->cacheIndex);
+ else
+ bitmap = bitmap_cache_get(cache->bitmap, (BYTE)memblt->cacheId, memblt->cacheIndex);
+
+ /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
+ if (bitmap == NULL)
+ return TRUE;
+
+ memblt->bitmap = bitmap;
+ return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
+}
+
+static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
+{
+ BYTE style = 0;
+ rdpBitmap* bitmap = NULL;
+ rdpCache* cache = context->cache;
+ rdpBrush* brush = &mem3blt->brush;
+ BOOL ret = TRUE;
+
+ if (mem3blt->cacheId == 0xFF)
+ bitmap = offscreen_cache_get(cache->offscreen, mem3blt->cacheIndex);
+ else
+ bitmap = bitmap_cache_get(cache->bitmap, (BYTE)mem3blt->cacheId, mem3blt->cacheIndex);
+
+ /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
+ if (!bitmap)
+ return TRUE;
+
+ style = brush->style;
+
+ if (brush->style & CACHED_BRUSH)
+ {
+ brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
+
+ if (!brush->data)
+ return FALSE;
+
+ brush->style = 0x03;
+ }
+
+ mem3blt->bitmap = bitmap;
+ IFCALLRET(cache->bitmap->Mem3Blt, ret, context, mem3blt);
+ brush->style = style;
+ return ret;
+}
+
+static BOOL update_gdi_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cacheBitmap)
+{
+ rdpBitmap* bitmap = NULL;
+ rdpBitmap* prevBitmap = NULL;
+ rdpCache* cache = context->cache;
+ bitmap = Bitmap_Alloc(context);
+
+ if (!bitmap)
+ return FALSE;
+
+ Bitmap_SetDimensions(bitmap, cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight);
+
+ if (!bitmap->Decompress(context, bitmap, cacheBitmap->bitmapDataStream,
+ cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight,
+ cacheBitmap->bitmapBpp, cacheBitmap->bitmapLength,
+ cacheBitmap->compressed, RDP_CODEC_ID_NONE))
+ {
+ Bitmap_Free(context, bitmap);
+ return FALSE;
+ }
+
+ if (!bitmap->New(context, bitmap))
+ {
+ Bitmap_Free(context, bitmap);
+ return FALSE;
+ }
+
+ prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex);
+ Bitmap_Free(context, prevBitmap);
+ return bitmap_cache_put(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex, bitmap);
+}
+
+static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
+
+{
+ rdpBitmap* prevBitmap = NULL;
+ rdpCache* cache = context->cache;
+ rdpSettings* settings = context->settings;
+ rdpBitmap* bitmap = Bitmap_Alloc(context);
+
+ if (!bitmap)
+ return FALSE;
+
+ const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
+ bitmap->key64 = ((UINT64)cacheBitmapV2->key1 | (((UINT64)cacheBitmapV2->key2) << 32));
+
+ if (!cacheBitmapV2->bitmapBpp)
+ cacheBitmapV2->bitmapBpp = ColorDepth;
+
+ if ((ColorDepth == 15) && (cacheBitmapV2->bitmapBpp == 16))
+ cacheBitmapV2->bitmapBpp = ColorDepth;
+
+ Bitmap_SetDimensions(bitmap, cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight);
+
+ if (!bitmap->Decompress(context, bitmap, cacheBitmapV2->bitmapDataStream,
+ cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight,
+ cacheBitmapV2->bitmapBpp, cacheBitmapV2->bitmapLength,
+ cacheBitmapV2->compressed, RDP_CODEC_ID_NONE))
+ goto fail;
+
+ prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex);
+
+ if (!bitmap->New(context, bitmap))
+ goto fail;
+
+ Bitmap_Free(context, prevBitmap);
+ return bitmap_cache_put(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex,
+ bitmap);
+
+fail:
+ Bitmap_Free(context, bitmap);
+ return FALSE;
+}
+
+static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cacheBitmapV3)
+{
+ rdpBitmap* bitmap = NULL;
+ rdpBitmap* prevBitmap = NULL;
+ BOOL compressed = TRUE;
+ rdpCache* cache = context->cache;
+ rdpSettings* settings = context->settings;
+ BITMAP_DATA_EX* bitmapData = &cacheBitmapV3->bitmapData;
+ bitmap = Bitmap_Alloc(context);
+
+ if (!bitmap)
+ return FALSE;
+
+ const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
+ bitmap->key64 = ((UINT64)cacheBitmapV3->key1 | (((UINT64)cacheBitmapV3->key2) << 32));
+
+ if (!cacheBitmapV3->bpp)
+ cacheBitmapV3->bpp = ColorDepth;
+
+ compressed = (bitmapData->codecID != RDP_CODEC_ID_NONE);
+ Bitmap_SetDimensions(bitmap, bitmapData->width, bitmapData->height);
+
+ if (!bitmap->Decompress(context, bitmap, bitmapData->data, bitmapData->width,
+ bitmapData->height, bitmapData->bpp, bitmapData->length, compressed,
+ bitmapData->codecID))
+ goto fail;
+
+ if (!bitmap->New(context, bitmap))
+ goto fail;
+
+ prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex);
+ Bitmap_Free(context, prevBitmap);
+ return bitmap_cache_put(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex,
+ bitmap);
+
+fail:
+ Bitmap_Free(context, bitmap);
+ return FALSE;
+}
+
+rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index)
+{
+ rdpBitmap* bitmap = NULL;
+
+ if (id >= bitmapCache->maxCells)
+ {
+ WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
+ return NULL;
+ }
+
+ if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
+ {
+ index = bitmapCache->cells[id].number;
+ }
+ else if (index > bitmapCache->cells[id].number)
+ {
+ WLog_ERR(TAG, "get invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
+ return NULL;
+ }
+
+ bitmap = bitmapCache->cells[id].entries[index];
+ return bitmap;
+}
+
+BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index, rdpBitmap* bitmap)
+{
+ if (id > bitmapCache->maxCells)
+ {
+ WLog_ERR(TAG, "put invalid bitmap cell id: %" PRIu32 "", id);
+ return FALSE;
+ }
+
+ if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
+ {
+ index = bitmapCache->cells[id].number;
+ }
+ else if (index > bitmapCache->cells[id].number)
+ {
+ WLog_ERR(TAG, "put invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
+ return FALSE;
+ }
+
+ bitmapCache->cells[id].entries[index] = bitmap;
+ return TRUE;
+}
+
+void bitmap_cache_register_callbacks(rdpUpdate* update)
+{
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->context);
+ WINPR_ASSERT(update->context->cache);
+
+ cache = update->context->cache;
+ WINPR_ASSERT(cache);
+
+ if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
+ {
+ cache->bitmap->MemBlt = update->primary->MemBlt;
+ cache->bitmap->Mem3Blt = update->primary->Mem3Blt;
+ update->primary->MemBlt = update_gdi_memblt;
+ update->primary->Mem3Blt = update_gdi_mem3blt;
+ update->secondary->CacheBitmap = update_gdi_cache_bitmap;
+ update->secondary->CacheBitmapV2 = update_gdi_cache_bitmap_v2;
+ update->secondary->CacheBitmapV3 = update_gdi_cache_bitmap_v3;
+ update->BitmapUpdate = gdi_bitmap_update;
+ }
+}
+
+static int bitmap_cache_save_persistent(rdpBitmapCache* bitmapCache)
+{
+ rdpContext* context = bitmapCache->context;
+ rdpSettings* settings = context->settings;
+
+ const UINT32 version = freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheVersion);
+
+ if (version != 2)
+ return 0; /* persistent bitmap cache already saved in egfx channel */
+
+ if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
+ return 0;
+
+ const char* BitmapCachePersistFile =
+ freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
+ if (!BitmapCachePersistFile)
+ return 0;
+
+ rdpPersistentCache* persistent = persistent_cache_new();
+
+ if (!persistent)
+ return -1;
+
+ int status = persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, version);
+
+ if (status < 1)
+ goto end;
+
+ if (bitmapCache->cells)
+ {
+ for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
+ {
+ BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
+ for (UINT32 j = 0; j < cell->number + 1 && cell->entries; j++)
+ {
+ PERSISTENT_CACHE_ENTRY cacheEntry;
+ rdpBitmap* bitmap = cell->entries[j];
+
+ if (!bitmap || !bitmap->key64)
+ continue;
+
+ cacheEntry.key64 = bitmap->key64;
+ cacheEntry.width = bitmap->width;
+ cacheEntry.height = bitmap->height;
+ cacheEntry.size = (UINT32)(bitmap->width * bitmap->height * 4);
+ cacheEntry.flags = 0;
+ cacheEntry.data = bitmap->data;
+
+ if (persistent_cache_write_entry(persistent, &cacheEntry) < 1)
+ {
+ status = -1;
+ goto end;
+ }
+ }
+ }
+ }
+
+ status = 1;
+
+end:
+ persistent_cache_free(persistent);
+ return status;
+}
+
+rdpBitmapCache* bitmap_cache_new(rdpContext* context)
+{
+ rdpSettings* settings = NULL;
+ rdpBitmapCache* bitmapCache = NULL;
+
+ WINPR_ASSERT(context);
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ bitmapCache = (rdpBitmapCache*)calloc(1, sizeof(rdpBitmapCache));
+
+ if (!bitmapCache)
+ return NULL;
+
+ const UINT32 BitmapCacheV2NumCells =
+ freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
+ bitmapCache->context = context;
+ bitmapCache->cells = (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL));
+
+ if (!bitmapCache->cells)
+ goto fail;
+ bitmapCache->maxCells = BitmapCacheV2NumCells;
+
+ for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
+ {
+ const BITMAP_CACHE_V2_CELL_INFO* info =
+ freerdp_settings_get_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, i);
+ BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
+ UINT32 nr = info->numEntries;
+ /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
+ cell->entries = (rdpBitmap**)calloc((nr + 1), sizeof(rdpBitmap*));
+
+ if (!cell->entries)
+ goto fail;
+ cell->number = nr;
+ }
+
+ return bitmapCache;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ bitmap_cache_free(bitmapCache);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void bitmap_cache_free(rdpBitmapCache* bitmapCache)
+{
+ if (!bitmapCache)
+ return;
+
+ bitmap_cache_save_persistent(bitmapCache);
+
+ if (bitmapCache->cells)
+ {
+ for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
+ {
+ UINT32 j = 0;
+ BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
+
+ if (!cell->entries)
+ continue;
+
+ for (j = 0; j < cell->number + 1; j++)
+ {
+ rdpBitmap* bitmap = cell->entries[j];
+ Bitmap_Free(bitmapCache->context, bitmap);
+ }
+
+ free(cell->entries);
+ }
+
+ free(bitmapCache->cells);
+ }
+
+ persistent_cache_free(bitmapCache->persistent);
+
+ free(bitmapCache);
+}
+
+static void free_bitmap_data(BITMAP_DATA* data, size_t count)
+{
+ if (!data)
+ return;
+
+ for (size_t x = 0; x < count; x++)
+ free(data[x].bitmapDataStream);
+
+ free(data);
+}
+
+static BITMAP_DATA* copy_bitmap_data(const BITMAP_DATA* data, size_t count)
+{
+ BITMAP_DATA* dst = (BITMAP_DATA*)calloc(count, sizeof(BITMAP_DATA));
+
+ if (!dst)
+ goto fail;
+
+ for (size_t x = 0; x < count; x++)
+ {
+ dst[x] = data[x];
+
+ if (data[x].bitmapLength > 0)
+ {
+ dst[x].bitmapDataStream = malloc(data[x].bitmapLength);
+
+ if (!dst[x].bitmapDataStream)
+ goto fail;
+
+ memcpy(dst[x].bitmapDataStream, data[x].bitmapDataStream, data[x].bitmapLength);
+ }
+ }
+
+ return dst;
+fail:
+ free_bitmap_data(dst, count);
+ return NULL;
+}
+
+void free_bitmap_update(rdpContext* context, BITMAP_UPDATE* pointer)
+{
+ if (!pointer)
+ return;
+
+ free_bitmap_data(pointer->rectangles, pointer->number);
+ free(pointer);
+}
+
+BITMAP_UPDATE* copy_bitmap_update(rdpContext* context, const BITMAP_UPDATE* pointer)
+{
+ BITMAP_UPDATE* dst = calloc(1, sizeof(BITMAP_UPDATE));
+
+ if (!dst || !pointer)
+ goto fail;
+
+ *dst = *pointer;
+ dst->rectangles = copy_bitmap_data(pointer->rectangles, pointer->number);
+
+ if (!dst->rectangles)
+ goto fail;
+
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_bitmap_update(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context, const CACHE_BITMAP_ORDER* order)
+{
+ CACHE_BITMAP_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_ORDER));
+
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+
+ if (order->bitmapLength > 0)
+ {
+ dst->bitmapDataStream = malloc(order->bitmapLength);
+
+ if (!dst->bitmapDataStream)
+ goto fail;
+
+ memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
+ }
+
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_cache_bitmap_order(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void free_cache_bitmap_order(rdpContext* context, CACHE_BITMAP_ORDER* order)
+{
+ if (order)
+ free(order->bitmapDataStream);
+
+ free(order);
+}
+
+CACHE_BITMAP_V2_ORDER* copy_cache_bitmap_v2_order(rdpContext* context,
+ const CACHE_BITMAP_V2_ORDER* order)
+{
+ CACHE_BITMAP_V2_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V2_ORDER));
+
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+
+ if (order->bitmapLength > 0)
+ {
+ dst->bitmapDataStream = malloc(order->bitmapLength);
+
+ if (!dst->bitmapDataStream)
+ goto fail;
+
+ memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
+ }
+
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_cache_bitmap_v2_order(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void free_cache_bitmap_v2_order(rdpContext* context, CACHE_BITMAP_V2_ORDER* order)
+{
+ if (order)
+ free(order->bitmapDataStream);
+
+ free(order);
+}
+
+CACHE_BITMAP_V3_ORDER* copy_cache_bitmap_v3_order(rdpContext* context,
+ const CACHE_BITMAP_V3_ORDER* order)
+{
+ CACHE_BITMAP_V3_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V3_ORDER));
+
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+
+ if (order->bitmapData.length > 0)
+ {
+ dst->bitmapData.data = malloc(order->bitmapData.length);
+
+ if (!dst->bitmapData.data)
+ goto fail;
+
+ memcpy(dst->bitmapData.data, order->bitmapData.data, order->bitmapData.length);
+ }
+
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_cache_bitmap_v3_order(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void free_cache_bitmap_v3_order(rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
+{
+ if (order)
+ free(order->bitmapData.data);
+
+ free(order);
+}
diff --git a/libfreerdp/cache/bitmap.h b/libfreerdp/cache/bitmap.h
new file mode 100644
index 0000000..4e45170
--- /dev/null
+++ b/libfreerdp/cache/bitmap.h
@@ -0,0 +1,95 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_CACHE_BITMAP_H
+#define FREERDP_LIB_CACHE_BITMAP_H
+
+#include <freerdp/api.h>
+#include <freerdp/update.h>
+
+#include <freerdp/cache/persistent.h>
+
+typedef struct
+{
+ UINT32 number;
+ rdpBitmap** entries;
+} BITMAP_V2_CELL;
+
+typedef struct
+{
+ pMemBlt MemBlt; /* 0 */
+ pMem3Blt Mem3Blt; /* 1 */
+ pCacheBitmap CacheBitmap; /* 2 */
+ pCacheBitmapV2 CacheBitmapV2; /* 3 */
+ pCacheBitmapV3 CacheBitmapV3; /* 4 */
+ pBitmapUpdate BitmapUpdate; /* 5 */
+ UINT32 paddingA[16 - 6]; /* 6 */
+
+ UINT32 maxCells; /* 16 */
+ BITMAP_V2_CELL* cells; /* 17 */
+ UINT32 paddingB[32 - 18]; /* 18 */
+
+ /* internal */
+ rdpContext* context;
+ rdpPersistentCache* persistent;
+} rdpBitmapCache;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void bitmap_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void bitmap_cache_free(rdpBitmapCache* bitmap_cache);
+
+ WINPR_ATTR_MALLOC(bitmap_cache_free, 1)
+ FREERDP_LOCAL rdpBitmapCache* bitmap_cache_new(rdpContext* context);
+
+ FREERDP_LOCAL void free_bitmap_update(rdpContext* context, BITMAP_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_bitmap_update, 2)
+ FREERDP_LOCAL BITMAP_UPDATE* copy_bitmap_update(rdpContext* context,
+ const BITMAP_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_cache_bitmap_order(rdpContext* context, CACHE_BITMAP_ORDER* order);
+
+ WINPR_ATTR_MALLOC(free_cache_bitmap_order, 2)
+ FREERDP_LOCAL CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context,
+ const CACHE_BITMAP_ORDER* order);
+
+ FREERDP_LOCAL void free_cache_bitmap_v2_order(rdpContext* context,
+ CACHE_BITMAP_V2_ORDER* order);
+
+ WINPR_ATTR_MALLOC(free_cache_bitmap_v2_order, 2)
+ FREERDP_LOCAL CACHE_BITMAP_V2_ORDER*
+ copy_cache_bitmap_v2_order(rdpContext* context, const CACHE_BITMAP_V2_ORDER* order);
+
+ FREERDP_LOCAL void free_cache_bitmap_v3_order(rdpContext* context,
+ CACHE_BITMAP_V3_ORDER* order);
+
+ WINPR_ATTR_MALLOC(free_cache_bitmap_v3_order, 2)
+ FREERDP_LOCAL CACHE_BITMAP_V3_ORDER*
+ copy_cache_bitmap_v3_order(rdpContext* context, const CACHE_BITMAP_V3_ORDER* order);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_CACHE_BITMAP_H */
diff --git a/libfreerdp/cache/brush.c b/libfreerdp/cache/brush.c
new file mode 100644
index 0000000..9490076
--- /dev/null
+++ b/libfreerdp/cache/brush.c
@@ -0,0 +1,326 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Brush Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <freerdp/log.h>
+#include <freerdp/update.h>
+#include <freerdp/freerdp.h>
+#include <winpr/stream.h>
+
+#include "brush.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.brush")
+
+typedef struct
+{
+ UINT32 bpp;
+ void* entry;
+} BRUSH_ENTRY;
+
+struct rdp_brush_cache
+{
+ pPatBlt PatBlt; /* 0 */
+ pCacheBrush CacheBrush; /* 1 */
+ pPolygonSC PolygonSC; /* 2 */
+ pPolygonCB PolygonCB; /* 3 */
+ UINT32 paddingA[16 - 4]; /* 4 */
+
+ UINT32 maxEntries; /* 16 */
+ UINT32 maxMonoEntries; /* 17 */
+ BRUSH_ENTRY* entries; /* 18 */
+ BRUSH_ENTRY* monoEntries; /* 19 */
+ UINT32 paddingB[32 - 20]; /* 20 */
+
+ rdpContext* context;
+};
+
+static BOOL update_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt)
+{
+ BYTE style = 0;
+ BOOL ret = TRUE;
+ rdpBrush* brush = NULL;
+ const rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(patblt);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ brush = &patblt->brush;
+ style = brush->style;
+
+ if (brush->style & CACHED_BRUSH)
+ {
+ brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
+ brush->style = 0x03;
+ }
+
+ WINPR_ASSERT(cache->brush);
+ IFCALLRET(cache->brush->PatBlt, ret, context, patblt);
+ brush->style = style;
+ return ret;
+}
+
+static BOOL update_gdi_polygon_sc(rdpContext* context, const POLYGON_SC_ORDER* polygon_sc)
+{
+ rdpCache* cache = NULL;
+ WINPR_ASSERT(context);
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+ WINPR_ASSERT(cache->brush);
+ return IFCALLRESULT(TRUE, cache->brush->PolygonSC, context, polygon_sc);
+}
+
+static BOOL update_gdi_polygon_cb(rdpContext* context, POLYGON_CB_ORDER* polygon_cb)
+{
+ BYTE style = 0;
+ rdpBrush* brush = NULL;
+ rdpCache* cache = NULL;
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(polygon_cb);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ brush = &polygon_cb->brush;
+ style = brush->style;
+
+ if (brush->style & CACHED_BRUSH)
+ {
+ brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
+ brush->style = 0x03;
+ }
+
+ WINPR_ASSERT(cache->brush);
+ IFCALLRET(cache->brush->PolygonCB, ret, context, polygon_cb);
+ brush->style = style;
+ return ret;
+}
+
+static BOOL update_gdi_cache_brush(rdpContext* context, const CACHE_BRUSH_ORDER* cacheBrush)
+{
+ UINT32 length = 0;
+ void* data = NULL;
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(cacheBrush);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ length = cacheBrush->bpp * 64 / 8;
+ data = malloc(length);
+
+ if (!data)
+ return FALSE;
+
+ CopyMemory(data, cacheBrush->data, length);
+ brush_cache_put(cache->brush, cacheBrush->index, data, cacheBrush->bpp);
+ return TRUE;
+}
+
+void* brush_cache_get(rdpBrushCache* brushCache, UINT32 index, UINT32* bpp)
+{
+ void* entry = NULL;
+
+ if (!brushCache)
+ return NULL;
+
+ if (!bpp)
+ return NULL;
+
+ if (*bpp == 1)
+ {
+ if (index >= brushCache->maxMonoEntries)
+ {
+ WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", *bpp, index);
+ return NULL;
+ }
+
+ *bpp = brushCache->monoEntries[index].bpp;
+ entry = brushCache->monoEntries[index].entry;
+ }
+ else
+ {
+ if (index >= brushCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", *bpp, index);
+ return NULL;
+ }
+
+ *bpp = brushCache->entries[index].bpp;
+ entry = brushCache->entries[index].entry;
+ }
+
+ if (entry == NULL)
+ {
+ WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) at index: 0x%08" PRIX32 "", *bpp, index);
+ return NULL;
+ }
+
+ return entry;
+}
+
+void brush_cache_put(rdpBrushCache* brushCache, UINT32 index, void* entry, UINT32 bpp)
+{
+ WINPR_ASSERT(brushCache);
+
+ if (bpp == 1)
+ {
+ if (index >= brushCache->maxMonoEntries)
+ {
+ WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", bpp, index);
+ free(entry);
+ return;
+ }
+
+ WINPR_ASSERT(brushCache->monoEntries);
+ free(brushCache->monoEntries[index].entry);
+ brushCache->monoEntries[index].bpp = bpp;
+ brushCache->monoEntries[index].entry = entry;
+ }
+ else
+ {
+ if (index >= brushCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", bpp, index);
+ free(entry);
+ return;
+ }
+
+ WINPR_ASSERT(brushCache->entries);
+ free(brushCache->entries[index].entry);
+ brushCache->entries[index].bpp = bpp;
+ brushCache->entries[index].entry = entry;
+ }
+}
+
+void brush_cache_register_callbacks(rdpUpdate* update)
+{
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->context);
+ WINPR_ASSERT(update->primary);
+ WINPR_ASSERT(update->secondary);
+
+ if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
+ {
+ rdpCache* cache = update->context->cache;
+ WINPR_ASSERT(cache);
+ WINPR_ASSERT(cache->brush);
+
+ cache->brush->PatBlt = update->primary->PatBlt;
+ cache->brush->PolygonSC = update->primary->PolygonSC;
+ cache->brush->PolygonCB = update->primary->PolygonCB;
+ update->primary->PatBlt = update_gdi_patblt;
+ update->primary->PolygonSC = update_gdi_polygon_sc;
+ update->primary->PolygonCB = update_gdi_polygon_cb;
+ update->secondary->CacheBrush = update_gdi_cache_brush;
+ }
+}
+
+rdpBrushCache* brush_cache_new(rdpContext* context)
+{
+ rdpBrushCache* brushCache = NULL;
+
+ WINPR_ASSERT(context);
+
+ brushCache = (rdpBrushCache*)calloc(1, sizeof(rdpBrushCache));
+
+ if (!brushCache)
+ return NULL;
+
+ brushCache->context = context;
+ brushCache->maxEntries = 64;
+ brushCache->maxMonoEntries = 64;
+ brushCache->entries = (BRUSH_ENTRY*)calloc(brushCache->maxEntries, sizeof(BRUSH_ENTRY));
+
+ if (!brushCache->entries)
+ goto fail;
+
+ brushCache->monoEntries = (BRUSH_ENTRY*)calloc(brushCache->maxMonoEntries, sizeof(BRUSH_ENTRY));
+
+ if (!brushCache->monoEntries)
+ goto fail;
+
+ return brushCache;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ brush_cache_free(brushCache);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void brush_cache_free(rdpBrushCache* brushCache)
+{
+ if (brushCache)
+ {
+ if (brushCache->entries)
+ {
+ for (size_t i = 0; i < brushCache->maxEntries; i++)
+ free(brushCache->entries[i].entry);
+
+ free(brushCache->entries);
+ }
+
+ if (brushCache->monoEntries)
+ {
+ for (size_t i = 0; i < brushCache->maxMonoEntries; i++)
+ free(brushCache->monoEntries[i].entry);
+
+ free(brushCache->monoEntries);
+ }
+
+ free(brushCache);
+ }
+}
+
+void free_cache_brush_order(rdpContext* context, CACHE_BRUSH_ORDER* order)
+{
+ WINPR_UNUSED(context);
+ free(order);
+}
+
+CACHE_BRUSH_ORDER* copy_cache_brush_order(rdpContext* context, const CACHE_BRUSH_ORDER* order)
+{
+ CACHE_BRUSH_ORDER* dst = NULL;
+
+ WINPR_ASSERT(context);
+
+ dst = calloc(1, sizeof(CACHE_BRUSH_ORDER));
+
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+ return dst;
+fail:
+ free_cache_brush_order(context, dst);
+ return NULL;
+}
diff --git a/libfreerdp/cache/brush.h b/libfreerdp/cache/brush.h
new file mode 100644
index 0000000..101c237
--- /dev/null
+++ b/libfreerdp/cache/brush.h
@@ -0,0 +1,57 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Brush Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_BRUSH_CACHE_H
+#define FREERDP_LIB_BRUSH_CACHE_H
+
+#include <freerdp/api.h>
+#include <freerdp/types.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/update.h>
+
+#include <winpr/stream.h>
+
+typedef struct rdp_brush_cache rdpBrushCache;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void* brush_cache_get(rdpBrushCache* brush, UINT32 index, UINT32* bpp);
+ FREERDP_LOCAL void brush_cache_put(rdpBrushCache* brush, UINT32 index, void* entry, UINT32 bpp);
+
+ FREERDP_LOCAL void brush_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void brush_cache_free(rdpBrushCache* brush);
+
+ WINPR_ATTR_MALLOC(brush_cache_free, 1)
+ FREERDP_LOCAL rdpBrushCache* brush_cache_new(rdpContext* context);
+
+ FREERDP_LOCAL void free_cache_brush_order(rdpContext* context, CACHE_BRUSH_ORDER* order);
+
+ WINPR_ATTR_MALLOC(free_cache_brush_order, 1)
+ FREERDP_LOCAL CACHE_BRUSH_ORDER* copy_cache_brush_order(rdpContext* context,
+ const CACHE_BRUSH_ORDER* order);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_BRUSH_CACHE_H */
diff --git a/libfreerdp/cache/cache.c b/libfreerdp/cache/cache.c
new file mode 100644
index 0000000..162d861
--- /dev/null
+++ b/libfreerdp/cache/cache.c
@@ -0,0 +1,152 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RDP Caches
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+
+#include <winpr/stream.h>
+
+#include "cache.h"
+
+rdpCache* cache_new(rdpContext* context)
+{
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+
+ cache = (rdpCache*)calloc(1, sizeof(rdpCache));
+
+ if (!cache)
+ return NULL;
+
+ cache->glyph = glyph_cache_new(context);
+
+ if (!cache->glyph)
+ goto error;
+
+ cache->brush = brush_cache_new(context);
+
+ if (!cache->brush)
+ goto error;
+
+ cache->pointer = pointer_cache_new(context);
+
+ if (!cache->pointer)
+ goto error;
+
+ cache->bitmap = bitmap_cache_new(context);
+
+ if (!cache->bitmap)
+ goto error;
+
+ cache->offscreen = offscreen_cache_new(context);
+
+ if (!cache->offscreen)
+ goto error;
+
+ cache->palette = palette_cache_new(context);
+
+ if (!cache->palette)
+ goto error;
+
+ cache->nine_grid = nine_grid_cache_new(context);
+
+ if (!cache->nine_grid)
+ goto error;
+
+ return cache;
+error:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ cache_free(cache);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void cache_free(rdpCache* cache)
+{
+ if (cache != NULL)
+ {
+ glyph_cache_free(cache->glyph);
+ brush_cache_free(cache->brush);
+ pointer_cache_free(cache->pointer);
+ bitmap_cache_free(cache->bitmap);
+ offscreen_cache_free(cache->offscreen);
+ palette_cache_free(cache->palette);
+ nine_grid_cache_free(cache->nine_grid);
+ free(cache);
+ }
+}
+
+CACHE_COLOR_TABLE_ORDER* copy_cache_color_table_order(rdpContext* context,
+ const CACHE_COLOR_TABLE_ORDER* order)
+{
+ CACHE_COLOR_TABLE_ORDER* dst = calloc(1, sizeof(CACHE_COLOR_TABLE_ORDER));
+
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_cache_color_table_order(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void free_cache_color_table_order(rdpContext* context, CACHE_COLOR_TABLE_ORDER* order)
+{
+ free(order);
+}
+
+SURFACE_BITS_COMMAND* copy_surface_bits_command(rdpContext* context,
+ const SURFACE_BITS_COMMAND* order)
+{
+ SURFACE_BITS_COMMAND* dst = calloc(1, sizeof(SURFACE_BITS_COMMAND));
+ if (!dst || !order)
+ goto fail;
+
+ *dst = *order;
+
+ dst->bmp.bitmapData = (BYTE*)malloc(order->bmp.bitmapDataLength);
+
+ if (!dst->bmp.bitmapData)
+ goto fail;
+
+ CopyMemory(dst->bmp.bitmapData, order->bmp.bitmapData, order->bmp.bitmapDataLength);
+
+ return dst;
+
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_surface_bits_command(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void free_surface_bits_command(rdpContext* context, SURFACE_BITS_COMMAND* order)
+{
+ if (order)
+ free(order->bmp.bitmapData);
+ free(order);
+}
diff --git a/libfreerdp/cache/cache.h b/libfreerdp/cache/cache.h
new file mode 100644
index 0000000..614f1a9
--- /dev/null
+++ b/libfreerdp/cache/cache.h
@@ -0,0 +1,73 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_CACHE_CACHE_H
+#define FREERDP_LIB_CACHE_CACHE_H
+
+#include <freerdp/api.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/pointer.h>
+
+#include "glyph.h"
+#include "brush.h"
+#include "pointer.h"
+#include "bitmap.h"
+#include "nine_grid.h"
+#include "offscreen.h"
+#include "palette.h"
+
+struct rdp_cache
+{
+ rdpGlyphCache* glyph; /* 0 */
+ rdpBrushCache* brush; /* 1 */
+ rdpPointerCache* pointer; /* 2 */
+ rdpBitmapCache* bitmap; /* 3 */
+ rdpOffscreenCache* offscreen; /* 4 */
+ rdpPaletteCache* palette; /* 5 */
+ rdpNineGridCache* nine_grid; /* 6 */
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void cache_free(rdpCache* cache);
+
+ WINPR_ATTR_MALLOC(cache_free, 1)
+ FREERDP_LOCAL rdpCache* cache_new(rdpContext* context);
+
+ FREERDP_LOCAL void free_cache_color_table_order(rdpContext* context,
+ CACHE_COLOR_TABLE_ORDER* order);
+
+ WINPR_ATTR_MALLOC(free_cache_color_table_order, 2)
+ FREERDP_LOCAL CACHE_COLOR_TABLE_ORDER*
+ copy_cache_color_table_order(rdpContext* context, const CACHE_COLOR_TABLE_ORDER* order);
+
+ FREERDP_LOCAL void free_surface_bits_command(rdpContext* context, SURFACE_BITS_COMMAND* order);
+
+ WINPR_ATTR_MALLOC(free_surface_bits_command, 2)
+ FREERDP_LOCAL SURFACE_BITS_COMMAND*
+ copy_surface_bits_command(rdpContext* context, const SURFACE_BITS_COMMAND* order);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_CACHE_CACHE_H */
diff --git a/libfreerdp/cache/glyph.c b/libfreerdp/cache/glyph.c
new file mode 100644
index 0000000..ad394a9
--- /dev/null
+++ b/libfreerdp/cache/glyph.c
@@ -0,0 +1,892 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Glyph Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <freerdp/freerdp.h>
+#include <winpr/stream.h>
+
+#include <freerdp/log.h>
+
+#include "glyph.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.glyph")
+
+static rdpGlyph* glyph_cache_get(rdpGlyphCache* glyph_cache, UINT32 id, UINT32 index);
+static BOOL glyph_cache_put(rdpGlyphCache* glyph_cache, UINT32 id, UINT32 index, rdpGlyph* entry);
+
+static const void* glyph_cache_fragment_get(rdpGlyphCache* glyph, UINT32 index, UINT32* count);
+static BOOL glyph_cache_fragment_put(rdpGlyphCache* glyph, UINT32 index, UINT32 count,
+ const void* entry);
+
+static UINT32 update_glyph_offset(const BYTE* data, size_t length, UINT32 index, INT32* x, INT32* y,
+ UINT32 ulCharInc, UINT32 flAccel)
+{
+ if ((ulCharInc == 0) && (!(flAccel & SO_CHAR_INC_EQUAL_BM_BASE)))
+ {
+ UINT32 offset = data[index++];
+
+ if (offset & 0x80)
+ {
+
+ if (index + 1 < length)
+ {
+ offset = data[index++];
+ offset |= ((UINT32)data[index++]) << 8;
+ }
+ else
+ WLog_WARN(TAG, "[%s] glyph index out of bound %" PRIu32 " [max %" PRIuz "]", index,
+ length);
+ }
+
+ if (flAccel & SO_VERTICAL)
+ *y += offset;
+
+ if (flAccel & SO_HORIZONTAL)
+ *x += offset;
+ }
+
+ return index;
+}
+
+static BOOL update_process_glyph(rdpContext* context, const BYTE* data, UINT32 cacheIndex, INT32* x,
+ INT32* y, UINT32 cacheId, UINT32 flAccel, BOOL fOpRedundant,
+ const RDP_RECT* bound)
+{
+ INT32 sx = 0;
+ INT32 sy = 0;
+ INT32 dx = 0;
+ INT32 dy = 0;
+ rdpGlyph* glyph = NULL;
+ rdpGlyphCache* glyph_cache = NULL;
+
+ if (!context || !data || !x || !y || !context->graphics || !context->cache ||
+ !context->cache->glyph)
+ return FALSE;
+
+ glyph_cache = context->cache->glyph;
+ glyph = glyph_cache_get(glyph_cache, cacheId, cacheIndex);
+
+ if (!glyph)
+ return FALSE;
+
+ dx = glyph->x + *x;
+ dy = glyph->y + *y;
+
+ if (dx < bound->x)
+ {
+ sx = bound->x - dx;
+ dx = bound->x;
+ }
+
+ if (dy < bound->y)
+ {
+ sy = bound->y - dy;
+ dy = bound->y;
+ }
+
+ if ((dx <= (bound->x + bound->width)) && (dy <= (bound->y + bound->height)))
+ {
+ INT32 dw = glyph->cx - sx;
+ INT32 dh = glyph->cy - sy;
+
+ if ((dw + dx) > (bound->x + bound->width))
+ dw = (bound->x + bound->width) - (dw + dx);
+
+ if ((dh + dy) > (bound->y + bound->height))
+ dh = (bound->y + bound->height) - (dh + dy);
+
+ if ((dh > 0) && (dw > 0))
+ {
+ if (!glyph->Draw(context, glyph, dx, dy, dw, dh, sx, sy, fOpRedundant))
+ return FALSE;
+ }
+ }
+
+ if (flAccel & SO_CHAR_INC_EQUAL_BM_BASE)
+ *x += glyph->cx;
+
+ return TRUE;
+}
+
+static BOOL update_process_glyph_fragments(rdpContext* context, const BYTE* data, UINT32 length,
+ UINT32 cacheId, UINT32 ulCharInc, UINT32 flAccel,
+ UINT32 bgcolor, UINT32 fgcolor, INT32 x, INT32 y,
+ INT32 bkX, INT32 bkY, INT32 bkWidth, INT32 bkHeight,
+ INT32 opX, INT32 opY, INT32 opWidth, INT32 opHeight,
+ BOOL fOpRedundant)
+{
+ UINT32 id = 0;
+ UINT32 size = 0;
+ UINT32 index = 0;
+ const BYTE* fragments = NULL;
+ rdpGraphics* graphics = NULL;
+ rdpGlyphCache* glyph_cache = NULL;
+ rdpGlyph* glyph = NULL;
+ RDP_RECT bound;
+
+ if (!context || !data || !context->graphics || !context->cache || !context->cache->glyph)
+ return FALSE;
+
+ graphics = context->graphics;
+ glyph_cache = context->cache->glyph;
+ glyph = graphics->Glyph_Prototype;
+
+ if (!glyph)
+ return FALSE;
+
+ /* Limit op rectangle to visible screen. */
+ if (opX < 0)
+ {
+ opWidth += opX;
+ opX = 0;
+ }
+
+ if (opY < 0)
+ {
+ opHeight += opY;
+ opY = 0;
+ }
+
+ if (opWidth < 0)
+ opWidth = 0;
+
+ if (opHeight < 0)
+ opHeight = 0;
+
+ /* Limit bk rectangle to visible screen. */
+ if (bkX < 0)
+ {
+ bkWidth += bkX;
+ bkX = 0;
+ }
+
+ if (bkY < 0)
+ {
+ bkHeight += bkY;
+ bkY = 0;
+ }
+
+ if (bkWidth < 0)
+ bkWidth = 0;
+
+ if (bkHeight < 0)
+ bkHeight = 0;
+
+ if (opX + opWidth > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
+ {
+ /**
+ * Some Microsoft servers send erroneous high values close to the
+ * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
+ * FastGlyph drawing orders, probably a result of applications trying to
+ * clear the text line to the very right end.
+ * One example where this can be seen is typing in notepad.exe within
+ * a RDP session to Windows XP Professional SP3.
+ * This workaround prevents resulting problems in the UI callbacks.
+ */
+ opWidth = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth) - opX;
+ }
+
+ if (bkX + bkWidth > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
+ {
+ /**
+ * Some Microsoft servers send erroneous high values close to the
+ * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
+ * FastGlyph drawing orders, probably a result of applications trying to
+ * clear the text line to the very right end.
+ * One example where this can be seen is typing in notepad.exe within
+ * a RDP session to Windows XP Professional SP3.
+ * This workaround prevents resulting problems in the UI callbacks.
+ */
+ bkWidth = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth) - bkX;
+ }
+
+ bound.x = bkX;
+ bound.y = bkY;
+ bound.width = bkWidth;
+ bound.height = bkHeight;
+
+ if (!glyph->BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor, fOpRedundant))
+ return FALSE;
+
+ if (!IFCALLRESULT(TRUE, glyph->SetBounds, context, bkX, bkY, bkWidth, bkHeight))
+ return FALSE;
+
+ while (index < length)
+ {
+ const UINT32 op = data[index++];
+
+ switch (op)
+ {
+ case GLYPH_FRAGMENT_USE:
+ if (index + 1 >= length)
+ return FALSE;
+
+ id = data[index++];
+ fragments = (const BYTE*)glyph_cache_fragment_get(glyph_cache, id, &size);
+
+ if (fragments == NULL)
+ return FALSE;
+
+ for (size_t n = 0; n < size;)
+ {
+ const UINT32 fop = fragments[n++];
+ n = update_glyph_offset(fragments, size, n, &x, &y, ulCharInc, flAccel);
+
+ if (!update_process_glyph(context, fragments, fop, &x, &y, cacheId, flAccel,
+ fOpRedundant, &bound))
+ return FALSE;
+ }
+
+ break;
+
+ case GLYPH_FRAGMENT_ADD:
+ if (index + 2 > length)
+ return FALSE;
+
+ id = data[index++];
+ size = data[index++];
+ glyph_cache_fragment_put(glyph_cache, id, size, data);
+ break;
+
+ default:
+ index = update_glyph_offset(data, length, index, &x, &y, ulCharInc, flAccel);
+
+ if (!update_process_glyph(context, data, op, &x, &y, cacheId, flAccel, fOpRedundant,
+ &bound))
+ return FALSE;
+
+ break;
+ }
+ }
+
+ return glyph->EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor);
+}
+
+static BOOL update_gdi_glyph_index(rdpContext* context, GLYPH_INDEX_ORDER* glyphIndex)
+{
+ INT32 bkWidth = 0;
+ INT32 bkHeight = 0;
+ INT32 opWidth = 0;
+ INT32 opHeight = 0;
+
+ if (!context || !glyphIndex || !context->cache)
+ return FALSE;
+
+ if (glyphIndex->bkRight > glyphIndex->bkLeft)
+ bkWidth = glyphIndex->bkRight - glyphIndex->bkLeft + 1;
+
+ if (glyphIndex->opRight > glyphIndex->opLeft)
+ opWidth = glyphIndex->opRight - glyphIndex->opLeft + 1;
+
+ if (glyphIndex->bkBottom > glyphIndex->bkTop)
+ bkHeight = glyphIndex->bkBottom - glyphIndex->bkTop + 1;
+
+ if (glyphIndex->opBottom > glyphIndex->opTop)
+ opHeight = glyphIndex->opBottom - glyphIndex->opTop + 1;
+
+ return update_process_glyph_fragments(
+ context, glyphIndex->data, glyphIndex->cbData, glyphIndex->cacheId, glyphIndex->ulCharInc,
+ glyphIndex->flAccel, glyphIndex->backColor, glyphIndex->foreColor, glyphIndex->x,
+ glyphIndex->y, glyphIndex->bkLeft, glyphIndex->bkTop, bkWidth, bkHeight, glyphIndex->opLeft,
+ glyphIndex->opTop, opWidth, opHeight, glyphIndex->fOpRedundant);
+}
+
+static BOOL update_gdi_fast_index(rdpContext* context, const FAST_INDEX_ORDER* fastIndex)
+{
+ INT32 x = 0;
+ INT32 y = 0;
+ INT32 opLeft = 0;
+ INT32 opTop = 0;
+ INT32 opRight = 0;
+ INT32 opBottom = 0;
+ INT32 opWidth = 0;
+ INT32 opHeight = 0;
+ INT32 bkWidth = 0;
+ INT32 bkHeight = 0;
+
+ if (!context || !fastIndex || !context->cache)
+ return FALSE;
+
+ opLeft = fastIndex->opLeft;
+ opTop = fastIndex->opTop;
+ opRight = fastIndex->opRight;
+ opBottom = fastIndex->opBottom;
+ x = fastIndex->x;
+ y = fastIndex->y;
+
+ if (opBottom == -32768)
+ {
+ BYTE flags = (BYTE)(opTop & 0x0F);
+
+ if (flags & 0x01)
+ opBottom = fastIndex->bkBottom;
+
+ if (flags & 0x02)
+ opRight = fastIndex->bkRight;
+
+ if (flags & 0x04)
+ opTop = fastIndex->bkTop;
+
+ if (flags & 0x08)
+ opLeft = fastIndex->bkLeft;
+ }
+
+ if (opLeft == 0)
+ opLeft = fastIndex->bkLeft;
+
+ if (opRight == 0)
+ opRight = fastIndex->bkRight;
+
+ /* Server can send a massive number (32766) which appears to be
+ * undocumented special behavior for "Erase all the way right".
+ * X11 has nondeterministic results asking for a draw that wide. */
+ if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
+ opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
+
+ if (x == -32768)
+ x = fastIndex->bkLeft;
+
+ if (y == -32768)
+ y = fastIndex->bkTop;
+
+ if (fastIndex->bkRight > fastIndex->bkLeft)
+ bkWidth = fastIndex->bkRight - fastIndex->bkLeft + 1;
+
+ if (fastIndex->bkBottom > fastIndex->bkTop)
+ bkHeight = fastIndex->bkBottom - fastIndex->bkTop + 1;
+
+ if (opRight > opLeft)
+ opWidth = opRight - opLeft + 1;
+
+ if (opBottom > opTop)
+ opHeight = opBottom - opTop + 1;
+
+ return update_process_glyph_fragments(
+ context, fastIndex->data, fastIndex->cbData, fastIndex->cacheId, fastIndex->ulCharInc,
+ fastIndex->flAccel, fastIndex->backColor, fastIndex->foreColor, x, y, fastIndex->bkLeft,
+ fastIndex->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE);
+}
+
+static BOOL update_gdi_fast_glyph(rdpContext* context, const FAST_GLYPH_ORDER* fastGlyph)
+{
+ INT32 x = 0;
+ INT32 y = 0;
+ BYTE text_data[4] = { 0 };
+ INT32 opLeft = 0;
+ INT32 opTop = 0;
+ INT32 opRight = 0;
+ INT32 opBottom = 0;
+ INT32 opWidth = 0;
+ INT32 opHeight = 0;
+ INT32 bkWidth = 0;
+ INT32 bkHeight = 0;
+ rdpCache* cache = NULL;
+
+ if (!context || !fastGlyph || !context->cache)
+ return FALSE;
+
+ cache = context->cache;
+ opLeft = fastGlyph->opLeft;
+ opTop = fastGlyph->opTop;
+ opRight = fastGlyph->opRight;
+ opBottom = fastGlyph->opBottom;
+ x = fastGlyph->x;
+ y = fastGlyph->y;
+
+ if (opBottom == -32768)
+ {
+ BYTE flags = (BYTE)(opTop & 0x0F);
+
+ if (flags & 0x01)
+ opBottom = fastGlyph->bkBottom;
+
+ if (flags & 0x02)
+ opRight = fastGlyph->bkRight;
+
+ if (flags & 0x04)
+ opTop = fastGlyph->bkTop;
+
+ if (flags & 0x08)
+ opLeft = fastGlyph->bkLeft;
+ }
+
+ if (opLeft == 0)
+ opLeft = fastGlyph->bkLeft;
+
+ if (opRight == 0)
+ opRight = fastGlyph->bkRight;
+
+ /* See update_gdi_fast_index opRight comment. */
+ if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
+ opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
+
+ if (x == -32768)
+ x = fastGlyph->bkLeft;
+
+ if (y == -32768)
+ y = fastGlyph->bkTop;
+
+ if ((fastGlyph->cbData > 1) && (fastGlyph->glyphData.aj))
+ {
+ /* got option font that needs to go into cache */
+ rdpGlyph* glyph = NULL;
+ const GLYPH_DATA_V2* glyphData = &fastGlyph->glyphData;
+
+ glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx, glyphData->cy,
+ glyphData->cb, glyphData->aj);
+
+ if (!glyph)
+ return FALSE;
+
+ if (!glyph_cache_put(cache->glyph, fastGlyph->cacheId, fastGlyph->data[0], glyph))
+ {
+ glyph->Free(context, glyph);
+ return FALSE;
+ }
+ }
+
+ text_data[0] = fastGlyph->data[0];
+ text_data[1] = 0;
+
+ if (fastGlyph->bkRight > fastGlyph->bkLeft)
+ bkWidth = fastGlyph->bkRight - fastGlyph->bkLeft + 1;
+
+ if (fastGlyph->bkBottom > fastGlyph->bkTop)
+ bkHeight = fastGlyph->bkBottom - fastGlyph->bkTop + 1;
+
+ if (opRight > opLeft)
+ opWidth = opRight - opLeft + 1;
+
+ if (opBottom > opTop)
+ opHeight = opBottom - opTop + 1;
+
+ return update_process_glyph_fragments(
+ context, text_data, sizeof(text_data), fastGlyph->cacheId, fastGlyph->ulCharInc,
+ fastGlyph->flAccel, fastGlyph->backColor, fastGlyph->foreColor, x, y, fastGlyph->bkLeft,
+ fastGlyph->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE);
+}
+
+static BOOL update_gdi_cache_glyph(rdpContext* context, const CACHE_GLYPH_ORDER* cacheGlyph)
+{
+ rdpCache* cache = NULL;
+
+ if (!context || !cacheGlyph || !context->cache)
+ return FALSE;
+
+ cache = context->cache;
+
+ for (size_t i = 0; i < cacheGlyph->cGlyphs; i++)
+ {
+ const GLYPH_DATA* glyph_data = &cacheGlyph->glyphData[i];
+ rdpGlyph* glyph = NULL;
+
+ if (!glyph_data)
+ return FALSE;
+
+ if (!(glyph = Glyph_Alloc(context, glyph_data->x, glyph_data->y, glyph_data->cx,
+ glyph_data->cy, glyph_data->cb, glyph_data->aj)))
+ return FALSE;
+
+ if (!glyph_cache_put(cache->glyph, cacheGlyph->cacheId, glyph_data->cacheIndex, glyph))
+ {
+ glyph->Free(context, glyph);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL update_gdi_cache_glyph_v2(rdpContext* context, const CACHE_GLYPH_V2_ORDER* cacheGlyphV2)
+{
+ rdpCache* cache = NULL;
+
+ if (!context || !cacheGlyphV2 || !context->cache)
+ return FALSE;
+
+ cache = context->cache;
+
+ for (size_t i = 0; i < cacheGlyphV2->cGlyphs; i++)
+ {
+ const GLYPH_DATA_V2* glyphData = &cacheGlyphV2->glyphData[i];
+ rdpGlyph* glyph = NULL;
+
+ if (!glyphData)
+ return FALSE;
+
+ glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx, glyphData->cy,
+ glyphData->cb, glyphData->aj);
+
+ if (!glyph)
+ return FALSE;
+
+ if (!glyph_cache_put(cache->glyph, cacheGlyphV2->cacheId, glyphData->cacheIndex, glyph))
+ {
+ glyph->Free(context, glyph);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index)
+{
+ rdpGlyph* glyph = NULL;
+
+ WINPR_ASSERT(glyphCache);
+
+ WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCacheGet: id: %" PRIu32 " index: %" PRIu32 "", id,
+ index);
+
+ if (id > 9)
+ {
+ WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
+ return NULL;
+ }
+
+ WINPR_ASSERT(glyphCache->glyphCache);
+ if (index > glyphCache->glyphCache[id].number)
+ {
+ WLog_ERR(TAG, "index %" PRIu32 " out of range for cache id: %" PRIu32 "", index, id);
+ return NULL;
+ }
+
+ glyph = glyphCache->glyphCache[id].entries[index];
+
+ if (!glyph)
+ WLog_ERR(TAG, "no glyph found at cache index: %" PRIu32 " in cache id: %" PRIu32 "", index,
+ id);
+
+ return glyph;
+}
+
+BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index, rdpGlyph* glyph)
+{
+ rdpGlyph* prevGlyph = NULL;
+
+ WINPR_ASSERT(glyphCache);
+
+ if (id > 9)
+ {
+ WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
+ return FALSE;
+ }
+
+ WINPR_ASSERT(glyphCache->glyphCache);
+ if (index >= glyphCache->glyphCache[id].number)
+ {
+ WLog_ERR(TAG, "invalid glyph cache index: %" PRIu32 " in cache id: %" PRIu32 "", index, id);
+ return FALSE;
+ }
+
+ WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCachePut: id: %" PRIu32 " index: %" PRIu32 "", id,
+ index);
+ prevGlyph = glyphCache->glyphCache[id].entries[index];
+
+ if (prevGlyph)
+ {
+ WINPR_ASSERT(prevGlyph->Free);
+ prevGlyph->Free(glyphCache->context, prevGlyph);
+ }
+
+ glyphCache->glyphCache[id].entries[index] = glyph;
+ return TRUE;
+}
+
+const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size)
+{
+ void* fragment = NULL;
+
+ WINPR_ASSERT(glyphCache);
+ WINPR_ASSERT(glyphCache->fragCache.entries);
+
+ if (index > 255)
+ {
+ WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
+ return NULL;
+ }
+
+ fragment = glyphCache->fragCache.entries[index].fragment;
+ *size = (BYTE)glyphCache->fragCache.entries[index].size;
+ WLog_Print(glyphCache->log, WLOG_DEBUG,
+ "GlyphCacheFragmentGet: index: %" PRIu32 " size: %" PRIu32 "", index, *size);
+
+ if (!fragment)
+ WLog_ERR(TAG, "invalid glyph fragment at index:%" PRIu32 "", index);
+
+ return fragment;
+}
+
+BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
+ const void* fragment)
+{
+ void* prevFragment = NULL;
+ void* copy = NULL;
+
+ WINPR_ASSERT(glyphCache);
+ WINPR_ASSERT(glyphCache->fragCache.entries);
+
+ if (index > 255)
+ {
+ WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
+ return FALSE;
+ }
+
+ copy = malloc(size);
+
+ if (!copy)
+ return FALSE;
+
+ WLog_Print(glyphCache->log, WLOG_DEBUG,
+ "GlyphCacheFragmentPut: index: %" PRIu32 " size: %" PRIu32 "", index, size);
+ CopyMemory(copy, fragment, size);
+ prevFragment = glyphCache->fragCache.entries[index].fragment;
+ glyphCache->fragCache.entries[index].fragment = copy;
+ glyphCache->fragCache.entries[index].size = size;
+ free(prevFragment);
+ return TRUE;
+}
+
+void glyph_cache_register_callbacks(rdpUpdate* update)
+{
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->context);
+ WINPR_ASSERT(update->primary);
+ WINPR_ASSERT(update->secondary);
+
+ if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
+ {
+ update->primary->GlyphIndex = update_gdi_glyph_index;
+ update->primary->FastIndex = update_gdi_fast_index;
+ update->primary->FastGlyph = update_gdi_fast_glyph;
+ update->secondary->CacheGlyph = update_gdi_cache_glyph;
+ update->secondary->CacheGlyphV2 = update_gdi_cache_glyph_v2;
+ }
+}
+
+rdpGlyphCache* glyph_cache_new(rdpContext* context)
+{
+ rdpGlyphCache* glyphCache = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(context);
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ glyphCache = (rdpGlyphCache*)calloc(1, sizeof(rdpGlyphCache));
+
+ if (!glyphCache)
+ return NULL;
+
+ glyphCache->log = WLog_Get("com.freerdp.cache.glyph");
+ glyphCache->context = context;
+
+ for (size_t i = 0; i < 10; i++)
+ {
+ const GLYPH_CACHE_DEFINITION* currentGlyph =
+ freerdp_settings_get_pointer_array(settings, FreeRDP_GlyphCache, i);
+ GLYPH_CACHE* currentCache = &glyphCache->glyphCache[i];
+ currentCache->number = currentGlyph->cacheEntries;
+ currentCache->maxCellSize = currentGlyph->cacheMaximumCellSize;
+ currentCache->entries = (rdpGlyph**)calloc(currentCache->number, sizeof(rdpGlyph*));
+
+ if (!currentCache->entries)
+ goto fail;
+ }
+
+ return glyphCache;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ glyph_cache_free(glyphCache);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void glyph_cache_free(rdpGlyphCache* glyphCache)
+{
+ if (glyphCache)
+ {
+ GLYPH_CACHE* cache = glyphCache->glyphCache;
+
+ for (size_t i = 0; i < 10; i++)
+ {
+ rdpGlyph** entries = cache[i].entries;
+
+ if (!entries)
+ continue;
+
+ for (size_t j = 0; j < cache[i].number; j++)
+ {
+ rdpGlyph* glyph = entries[j];
+
+ if (glyph)
+ {
+ glyph->Free(glyphCache->context, glyph);
+ entries[j] = NULL;
+ }
+ }
+
+ free(entries);
+ cache[i].entries = NULL;
+ }
+
+ for (size_t i = 0; i < ARRAYSIZE(glyphCache->fragCache.entries); i++)
+ {
+ free(glyphCache->fragCache.entries[i].fragment);
+ glyphCache->fragCache.entries[i].fragment = NULL;
+ }
+
+ free(glyphCache);
+ }
+}
+
+CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context, const CACHE_GLYPH_ORDER* glyph)
+{
+ CACHE_GLYPH_ORDER* dst = NULL;
+
+ WINPR_ASSERT(context);
+
+ dst = calloc(1, sizeof(CACHE_GLYPH_ORDER));
+
+ if (!dst || !glyph)
+ goto fail;
+
+ *dst = *glyph;
+
+ for (size_t x = 0; x < glyph->cGlyphs; x++)
+ {
+ const GLYPH_DATA* src = &glyph->glyphData[x];
+ GLYPH_DATA* data = &dst->glyphData[x];
+
+ if (src->aj)
+ {
+ const size_t size = src->cb;
+ data->aj = malloc(size);
+
+ if (!data->aj)
+ goto fail;
+
+ memcpy(data->aj, src->aj, size);
+ }
+ }
+
+ if (glyph->unicodeCharacters)
+ {
+ if (glyph->cGlyphs == 0)
+ goto fail;
+
+ dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
+
+ if (!dst->unicodeCharacters)
+ goto fail;
+
+ memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
+ }
+
+ return dst;
+fail:
+ free_cache_glyph_order(context, dst);
+ return NULL;
+}
+
+void free_cache_glyph_order(rdpContext* context, CACHE_GLYPH_ORDER* glyph)
+{
+ if (glyph)
+ {
+ for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
+ free(glyph->glyphData[x].aj);
+
+ free(glyph->unicodeCharacters);
+ }
+
+ free(glyph);
+}
+
+CACHE_GLYPH_V2_ORDER* copy_cache_glyph_v2_order(rdpContext* context,
+ const CACHE_GLYPH_V2_ORDER* glyph)
+{
+ CACHE_GLYPH_V2_ORDER* dst = NULL;
+
+ WINPR_ASSERT(context);
+
+ dst = calloc(1, sizeof(CACHE_GLYPH_V2_ORDER));
+
+ if (!dst || !glyph)
+ goto fail;
+
+ *dst = *glyph;
+
+ for (size_t x = 0; x < glyph->cGlyphs; x++)
+ {
+ const GLYPH_DATA_V2* src = &glyph->glyphData[x];
+ GLYPH_DATA_V2* data = &dst->glyphData[x];
+
+ if (src->aj)
+ {
+ const size_t size = src->cb;
+ data->aj = malloc(size);
+
+ if (!data->aj)
+ goto fail;
+
+ memcpy(data->aj, src->aj, size);
+ }
+ }
+
+ if (glyph->unicodeCharacters)
+ {
+ if (glyph->cGlyphs == 0)
+ goto fail;
+
+ dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
+
+ if (!dst->unicodeCharacters)
+ goto fail;
+
+ memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
+ }
+
+ return dst;
+fail:
+ free_cache_glyph_v2_order(context, dst);
+ return NULL;
+}
+
+void free_cache_glyph_v2_order(rdpContext* context, CACHE_GLYPH_V2_ORDER* glyph)
+{
+ if (glyph)
+ {
+ for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
+ free(glyph->glyphData[x].aj);
+
+ free(glyph->unicodeCharacters);
+ }
+
+ free(glyph);
+}
diff --git a/libfreerdp/cache/glyph.h b/libfreerdp/cache/glyph.h
new file mode 100644
index 0000000..50e57aa
--- /dev/null
+++ b/libfreerdp/cache/glyph.h
@@ -0,0 +1,78 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_CACHE_GLYPH_H
+#define FREERDP_LIB_CACHE_GLYPH_H
+
+#include <freerdp/api.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/pointer.h>
+
+typedef struct
+{
+ UINT32 number;
+ UINT32 maxCellSize;
+ rdpGlyph** entries;
+} GLYPH_CACHE;
+
+typedef struct
+{
+ void* fragment;
+ UINT32 size;
+} FRAGMENT_CACHE_ENTRY;
+
+typedef struct
+{
+ FRAGMENT_CACHE_ENTRY entries[256];
+} FRAGMENT_CACHE;
+
+typedef struct
+{
+ FRAGMENT_CACHE fragCache;
+ GLYPH_CACHE glyphCache[10];
+
+ wLog* log;
+ rdpContext* context;
+} rdpGlyphCache;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void glyph_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void glyph_cache_free(rdpGlyphCache* glyph);
+
+ WINPR_ATTR_MALLOC(glyph_cache_free, 1)
+ FREERDP_LOCAL rdpGlyphCache* glyph_cache_new(rdpContext* context);
+
+ FREERDP_LOCAL CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context,
+ const CACHE_GLYPH_ORDER* glyph);
+ FREERDP_LOCAL void free_cache_glyph_order(rdpContext* context, CACHE_GLYPH_ORDER* glyph);
+
+ FREERDP_LOCAL CACHE_GLYPH_V2_ORDER*
+ copy_cache_glyph_v2_order(rdpContext* context, const CACHE_GLYPH_V2_ORDER* glyph);
+ FREERDP_LOCAL void free_cache_glyph_v2_order(rdpContext* context, CACHE_GLYPH_V2_ORDER* glyph);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_CACHE_GLYPH_H */
diff --git a/libfreerdp/cache/nine_grid.c b/libfreerdp/cache/nine_grid.c
new file mode 100644
index 0000000..c169a4f
--- /dev/null
+++ b/libfreerdp/cache/nine_grid.c
@@ -0,0 +1,169 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * NineGrid Cache
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/log.h>
+#include <freerdp/update.h>
+#include <freerdp/freerdp.h>
+#include <winpr/stream.h>
+
+#include "nine_grid.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.nine_grid")
+
+typedef struct
+{
+ void* entry;
+} NINE_GRID_ENTRY;
+
+struct rdp_nine_grid_cache
+{
+ pDrawNineGrid DrawNineGrid; /* 0 */
+ pMultiDrawNineGrid MultiDrawNineGrid; /* 1 */
+ UINT32 paddingA[16 - 2]; /* 2 */
+
+ UINT32 maxEntries; /* 16 */
+ UINT32 maxSize; /* 17 */
+ NINE_GRID_ENTRY* entries; /* 18 */
+ UINT32 paddingB[32 - 19]; /* 19 */
+
+ rdpContext* context;
+};
+
+static void* nine_grid_cache_get(rdpNineGridCache* nine_grid, UINT32 index);
+static void nine_grid_cache_put(rdpNineGridCache* nine_grid, UINT32 index, void* entry);
+
+static BOOL update_gdi_draw_nine_grid(rdpContext* context,
+ const DRAW_NINE_GRID_ORDER* draw_nine_grid)
+{
+ rdpCache* cache = context->cache;
+ return IFCALLRESULT(TRUE, cache->nine_grid->DrawNineGrid, context, draw_nine_grid);
+}
+
+static BOOL update_gdi_multi_draw_nine_grid(rdpContext* context,
+ const MULTI_DRAW_NINE_GRID_ORDER* multi_draw_nine_grid)
+{
+ rdpCache* cache = context->cache;
+ return IFCALLRESULT(TRUE, cache->nine_grid->MultiDrawNineGrid, context, multi_draw_nine_grid);
+}
+
+void nine_grid_cache_register_callbacks(rdpUpdate* update)
+{
+ rdpCache* cache = update->context->cache;
+
+ cache->nine_grid->DrawNineGrid = update->primary->DrawNineGrid;
+ cache->nine_grid->MultiDrawNineGrid = update->primary->MultiDrawNineGrid;
+
+ update->primary->DrawNineGrid = update_gdi_draw_nine_grid;
+ update->primary->MultiDrawNineGrid = update_gdi_multi_draw_nine_grid;
+}
+
+void* nine_grid_cache_get(rdpNineGridCache* nine_grid, UINT32 index)
+{
+ void* entry = NULL;
+
+ if (index >= nine_grid->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid NineGrid index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ entry = nine_grid->entries[index].entry;
+
+ if (entry == NULL)
+ {
+ WLog_ERR(TAG, "invalid NineGrid at index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ return entry;
+}
+
+void nine_grid_cache_put(rdpNineGridCache* nine_grid, UINT32 index, void* entry)
+{
+ if (index >= nine_grid->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid NineGrid index: 0x%08" PRIX32 "", index);
+ return;
+ }
+
+ free(nine_grid->entries[index].entry);
+ nine_grid->entries[index].entry = entry;
+}
+
+rdpNineGridCache* nine_grid_cache_new(rdpContext* context)
+{
+ rdpNineGridCache* nine_grid = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(context);
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ nine_grid = (rdpNineGridCache*)calloc(1, sizeof(rdpNineGridCache));
+ if (!nine_grid)
+ return NULL;
+
+ nine_grid->context = context;
+
+ nine_grid->maxSize = 2560;
+ nine_grid->maxEntries = 256;
+
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_DrawNineGridCacheSize, nine_grid->maxSize))
+ goto fail;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_DrawNineGridCacheEntries,
+ nine_grid->maxEntries))
+ goto fail;
+
+ nine_grid->entries = (NINE_GRID_ENTRY*)calloc(nine_grid->maxEntries, sizeof(NINE_GRID_ENTRY));
+ if (!nine_grid->entries)
+ goto fail;
+
+ return nine_grid;
+
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ nine_grid_cache_free(nine_grid);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void nine_grid_cache_free(rdpNineGridCache* nine_grid)
+{
+ if (nine_grid != NULL)
+ {
+ if (nine_grid->entries != NULL)
+ {
+ for (size_t i = 0; i < nine_grid->maxEntries; i++)
+ free(nine_grid->entries[i].entry);
+
+ free(nine_grid->entries);
+ }
+
+ free(nine_grid);
+ }
+}
diff --git a/libfreerdp/cache/nine_grid.h b/libfreerdp/cache/nine_grid.h
new file mode 100644
index 0000000..4f0ce02
--- /dev/null
+++ b/libfreerdp/cache/nine_grid.h
@@ -0,0 +1,50 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * NineGrid Cache
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_NINE_GRID_CACHE_H
+#define FREERDP_LIB_NINE_GRID_CACHE_H
+
+#include <freerdp/api.h>
+#include <freerdp/types.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/update.h>
+
+#include <winpr/stream.h>
+
+typedef struct rdp_nine_grid_cache rdpNineGridCache;
+
+#include "nine_grid.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void nine_grid_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void nine_grid_cache_free(rdpNineGridCache* nine_grid);
+
+ WINPR_ATTR_MALLOC(nine_grid_cache_free, 1)
+ FREERDP_LOCAL rdpNineGridCache* nine_grid_cache_new(rdpContext* context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_NINE_GRID_CACHE_H */
diff --git a/libfreerdp/cache/offscreen.c b/libfreerdp/cache/offscreen.c
new file mode 100644
index 0000000..3b8b43e
--- /dev/null
+++ b/libfreerdp/cache/offscreen.c
@@ -0,0 +1,243 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Offscreen Bitmap Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/stream.h>
+
+#include <freerdp/log.h>
+
+#include "../core/graphics.h"
+
+#include "offscreen.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.offscreen")
+
+struct rdp_offscreen_cache
+{
+ UINT32 maxSize; /* 0 */
+ UINT32 maxEntries; /* 1 */
+ rdpBitmap** entries; /* 2 */
+ UINT32 currentSurface; /* 3 */
+
+ rdpContext* context;
+};
+
+static void offscreen_cache_put(rdpOffscreenCache* offscreen_cache, UINT32 index,
+ rdpBitmap* bitmap);
+static void offscreen_cache_delete(rdpOffscreenCache* offscreen, UINT32 index);
+
+static BOOL
+update_gdi_create_offscreen_bitmap(rdpContext* context,
+ const CREATE_OFFSCREEN_BITMAP_ORDER* createOffscreenBitmap)
+{
+ UINT16 index = 0;
+ rdpBitmap* bitmap = NULL;
+ rdpCache* cache = NULL;
+
+ if (!context || !createOffscreenBitmap || !context->cache)
+ return FALSE;
+
+ cache = context->cache;
+ bitmap = Bitmap_Alloc(context);
+
+ if (!bitmap)
+ return FALSE;
+
+ Bitmap_SetDimensions(bitmap, createOffscreenBitmap->cx, createOffscreenBitmap->cy);
+
+ if (!bitmap->New(context, bitmap))
+ {
+ Bitmap_Free(context, bitmap);
+ return FALSE;
+ }
+
+ offscreen_cache_delete(cache->offscreen, createOffscreenBitmap->id);
+ offscreen_cache_put(cache->offscreen, createOffscreenBitmap->id, bitmap);
+
+ if (cache->offscreen->currentSurface == createOffscreenBitmap->id)
+ bitmap->SetSurface(context, bitmap, FALSE);
+
+ for (UINT32 i = 0; i < createOffscreenBitmap->deleteList.cIndices; i++)
+ {
+ index = createOffscreenBitmap->deleteList.indices[i];
+ offscreen_cache_delete(cache->offscreen, index);
+ }
+
+ return TRUE;
+}
+
+static BOOL update_gdi_switch_surface(rdpContext* context,
+ const SWITCH_SURFACE_ORDER* switchSurface)
+{
+ rdpCache* cache = NULL;
+ rdpBitmap* bitmap = NULL;
+
+ if (!context || !context->cache || !switchSurface || !context->graphics)
+ return FALSE;
+
+ cache = context->cache;
+ bitmap = context->graphics->Bitmap_Prototype;
+ if (!bitmap)
+ return FALSE;
+
+ if (switchSurface->bitmapId == SCREEN_BITMAP_SURFACE)
+ {
+ bitmap->SetSurface(context, NULL, TRUE);
+ }
+ else
+ {
+ rdpBitmap* bmp = NULL;
+ bmp = offscreen_cache_get(cache->offscreen, switchSurface->bitmapId);
+ if (bmp == NULL)
+ return FALSE;
+
+ bitmap->SetSurface(context, bmp, FALSE);
+ }
+
+ cache->offscreen->currentSurface = switchSurface->bitmapId;
+ return TRUE;
+}
+
+rdpBitmap* offscreen_cache_get(rdpOffscreenCache* offscreenCache, UINT32 index)
+{
+ rdpBitmap* bitmap = NULL;
+
+ WINPR_ASSERT(offscreenCache);
+
+ if (index >= offscreenCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid offscreen bitmap index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ bitmap = offscreenCache->entries[index];
+
+ if (!bitmap)
+ {
+ WLog_ERR(TAG, "invalid offscreen bitmap at index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ return bitmap;
+}
+
+void offscreen_cache_put(rdpOffscreenCache* offscreenCache, UINT32 index, rdpBitmap* bitmap)
+{
+ WINPR_ASSERT(offscreenCache);
+
+ if (index >= offscreenCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid offscreen bitmap index: 0x%08" PRIX32 "", index);
+ return;
+ }
+
+ offscreen_cache_delete(offscreenCache, index);
+ offscreenCache->entries[index] = bitmap;
+}
+
+void offscreen_cache_delete(rdpOffscreenCache* offscreenCache, UINT32 index)
+{
+ rdpBitmap* prevBitmap = NULL;
+
+ WINPR_ASSERT(offscreenCache);
+
+ if (index >= offscreenCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid offscreen bitmap index (delete): 0x%08" PRIX32 "", index);
+ return;
+ }
+
+ prevBitmap = offscreenCache->entries[index];
+
+ if (prevBitmap != NULL)
+ Bitmap_Free(offscreenCache->context, prevBitmap);
+
+ offscreenCache->entries[index] = NULL;
+}
+
+void offscreen_cache_register_callbacks(rdpUpdate* update)
+{
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->altsec);
+
+ update->altsec->CreateOffscreenBitmap = update_gdi_create_offscreen_bitmap;
+ update->altsec->SwitchSurface = update_gdi_switch_surface;
+}
+
+rdpOffscreenCache* offscreen_cache_new(rdpContext* context)
+{
+ rdpOffscreenCache* offscreenCache = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(context);
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ offscreenCache = (rdpOffscreenCache*)calloc(1, sizeof(rdpOffscreenCache));
+
+ if (!offscreenCache)
+ return NULL;
+
+ offscreenCache->context = context;
+ offscreenCache->currentSurface = SCREEN_BITMAP_SURFACE;
+ offscreenCache->maxSize = 7680;
+ offscreenCache->maxEntries = 2000;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_OffscreenCacheSize, offscreenCache->maxSize))
+ goto fail;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_OffscreenCacheEntries,
+ offscreenCache->maxEntries))
+ goto fail;
+ offscreenCache->entries = (rdpBitmap**)calloc(offscreenCache->maxEntries, sizeof(rdpBitmap*));
+
+ if (!offscreenCache->entries)
+ goto fail;
+
+ return offscreenCache;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ offscreen_cache_free(offscreenCache);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void offscreen_cache_free(rdpOffscreenCache* offscreenCache)
+{
+ if (offscreenCache)
+ {
+ if (offscreenCache->entries)
+ {
+ for (size_t i = 0; i < offscreenCache->maxEntries; i++)
+ {
+ rdpBitmap* bitmap = offscreenCache->entries[i];
+ Bitmap_Free(offscreenCache->context, bitmap);
+ }
+ }
+
+ free(offscreenCache->entries);
+ free(offscreenCache);
+ }
+}
diff --git a/libfreerdp/cache/offscreen.h b/libfreerdp/cache/offscreen.h
new file mode 100644
index 0000000..bdc89cc
--- /dev/null
+++ b/libfreerdp/cache/offscreen.h
@@ -0,0 +1,50 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Offscreen Bitmap Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_OFFSCREEN_CACHE_H
+#define FREERDP_LIB_OFFSCREEN_CACHE_H
+
+#include <freerdp/api.h>
+#include <freerdp/types.h>
+#include <freerdp/update.h>
+#include <freerdp/freerdp.h>
+
+#include <winpr/stream.h>
+
+typedef struct rdp_offscreen_cache rdpOffscreenCache;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL rdpBitmap* offscreen_cache_get(rdpOffscreenCache* offscreen_cache, UINT32 index);
+
+ FREERDP_LOCAL void offscreen_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void offscreen_cache_free(rdpOffscreenCache* offscreen);
+
+ WINPR_ATTR_MALLOC(offscreen_cache_free, 1)
+ FREERDP_LOCAL rdpOffscreenCache* offscreen_cache_new(rdpContext* context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_OFFSCREEN_CACHE_H */
diff --git a/libfreerdp/cache/palette.c b/libfreerdp/cache/palette.c
new file mode 100644
index 0000000..8a99abc
--- /dev/null
+++ b/libfreerdp/cache/palette.c
@@ -0,0 +1,142 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Palette (Color Table) Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/log.h>
+
+#include "palette.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.palette")
+
+static void* palette_cache_get(rdpPaletteCache* palette, UINT32 index);
+
+static void palette_cache_put(rdpPaletteCache* palette, UINT32 index, void* entry);
+
+static BOOL update_gdi_cache_color_table(rdpContext* context,
+ const CACHE_COLOR_TABLE_ORDER* cacheColorTable)
+{
+ UINT32* colorTable = NULL;
+ rdpCache* cache = context->cache;
+ colorTable = (UINT32*)malloc(sizeof(UINT32) * 256);
+
+ if (!colorTable)
+ return FALSE;
+
+ CopyMemory(colorTable, cacheColorTable->colorTable, sizeof(UINT32) * 256);
+ palette_cache_put(cache->palette, cacheColorTable->cacheIndex, (void*)colorTable);
+ return TRUE;
+}
+
+void* palette_cache_get(rdpPaletteCache* paletteCache, UINT32 index)
+{
+ void* entry = NULL;
+
+ if (index >= paletteCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid color table index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ entry = paletteCache->entries[index].entry;
+
+ if (!entry)
+ {
+ WLog_ERR(TAG, "invalid color table at index: 0x%08" PRIX32 "", index);
+ return NULL;
+ }
+
+ return entry;
+}
+
+void palette_cache_put(rdpPaletteCache* paletteCache, UINT32 index, void* entry)
+{
+ if (index >= paletteCache->maxEntries)
+ {
+ WLog_ERR(TAG, "invalid color table index: 0x%08" PRIX32 "", index);
+ free(entry);
+ return;
+ }
+
+ free(paletteCache->entries[index].entry);
+ paletteCache->entries[index].entry = entry;
+}
+
+void palette_cache_register_callbacks(rdpUpdate* update)
+{
+ update->secondary->CacheColorTable = update_gdi_cache_color_table;
+}
+
+rdpPaletteCache* palette_cache_new(rdpContext* context)
+{
+ rdpPaletteCache* paletteCache = NULL;
+
+ WINPR_ASSERT(context);
+
+ paletteCache = (rdpPaletteCache*)calloc(1, sizeof(rdpPaletteCache));
+
+ if (paletteCache)
+ {
+ paletteCache->context = context;
+ paletteCache->maxEntries = 6;
+ paletteCache->entries =
+ (PALETTE_TABLE_ENTRY*)calloc(paletteCache->maxEntries, sizeof(PALETTE_TABLE_ENTRY));
+ }
+
+ return paletteCache;
+}
+
+void palette_cache_free(rdpPaletteCache* paletteCache)
+{
+ if (paletteCache)
+ {
+ for (UINT32 i = 0; i < paletteCache->maxEntries; i++)
+ free(paletteCache->entries[i].entry);
+
+ free(paletteCache->entries);
+ free(paletteCache);
+ }
+}
+
+void free_palette_update(rdpContext* context, PALETTE_UPDATE* pointer)
+{
+ free(pointer);
+}
+
+PALETTE_UPDATE* copy_palette_update(rdpContext* context, const PALETTE_UPDATE* pointer)
+{
+ PALETTE_UPDATE* dst = calloc(1, sizeof(PALETTE_UPDATE));
+
+ if (!dst || !pointer)
+ goto fail;
+
+ *dst = *pointer;
+ return dst;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ free_palette_update(context, dst);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
diff --git a/libfreerdp/cache/palette.h b/libfreerdp/cache/palette.h
new file mode 100644
index 0000000..fb5be6a
--- /dev/null
+++ b/libfreerdp/cache/palette.h
@@ -0,0 +1,65 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_CACHE_PALETTE_H
+#define FREERDP_LIB_CACHE_PALETTE_H
+
+#include <freerdp/api.h>
+#include <freerdp/update.h>
+
+typedef struct rdp_palette_cache rdpPaletteCache;
+
+typedef struct
+{
+ void* entry;
+} PALETTE_TABLE_ENTRY;
+
+struct rdp_palette_cache
+{
+ UINT32 maxEntries; /* 0 */
+ PALETTE_TABLE_ENTRY* entries; /* 1 */
+
+ /* internal */
+
+ rdpContext* context;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void palette_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void palette_cache_free(rdpPaletteCache* palette_cache);
+
+ WINPR_ATTR_MALLOC(palette_cache_free, 1)
+ FREERDP_LOCAL rdpPaletteCache* palette_cache_new(rdpContext* context);
+
+ FREERDP_LOCAL void free_palette_update(rdpContext* context, PALETTE_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_palette_update, 2)
+ FREERDP_LOCAL PALETTE_UPDATE* copy_palette_update(rdpContext* context,
+ const PALETTE_UPDATE* pointer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_CACHE_PALETTE_H */
diff --git a/libfreerdp/cache/persistent.c b/libfreerdp/cache/persistent.c
new file mode 100644
index 0000000..8499c7e
--- /dev/null
+++ b/libfreerdp/cache/persistent.c
@@ -0,0 +1,374 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Persistent Bitmap Cache
+ *
+ * Copyright 2016 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/stream.h>
+#include <winpr/assert.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/constants.h>
+
+#include <freerdp/log.h>
+#include <freerdp/cache/persistent.h>
+
+#define TAG FREERDP_TAG("cache.persistent")
+
+struct rdp_persistent_cache
+{
+ FILE* fp;
+ BOOL write;
+ UINT32 version;
+ int count;
+ char* filename;
+ BYTE* bmpData;
+ UINT32 bmpSize;
+};
+
+int persistent_cache_get_version(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+ return persistent->version;
+}
+
+int persistent_cache_get_count(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+ return persistent->count;
+}
+
+static int persistent_cache_read_entry_v2(rdpPersistentCache* persistent,
+ PERSISTENT_CACHE_ENTRY* entry)
+{
+ PERSISTENT_CACHE_ENTRY_V2 entry2 = { 0 };
+
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+
+ if (fread((void*)&entry2, sizeof(entry2), 1, persistent->fp) != 1)
+ return -1;
+
+ entry->key64 = entry2.key64;
+ entry->width = entry2.width;
+ entry->height = entry2.height;
+ entry->size = entry2.width * entry2.height * 4;
+ entry->flags = entry2.flags;
+
+ entry->data = persistent->bmpData;
+
+ if (fread((void*)entry->data, 0x4000, 1, persistent->fp) != 1)
+ return -1;
+
+ return 1;
+}
+
+static int persistent_cache_write_entry_v2(rdpPersistentCache* persistent,
+ const PERSISTENT_CACHE_ENTRY* entry)
+{
+ int padding = 0;
+ PERSISTENT_CACHE_ENTRY_V2 entry2 = { 0 };
+
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+ entry2.key64 = entry->key64;
+ entry2.width = entry->width;
+ entry2.height = entry->height;
+ entry2.size = entry->size;
+ entry2.flags = entry->flags;
+
+ if (!entry2.flags)
+ entry2.flags = 0x00000011;
+
+ if (fwrite((void*)&entry2, sizeof(entry2), 1, persistent->fp) != 1)
+ return -1;
+
+ if (fwrite((void*)entry->data, entry->size, 1, persistent->fp) != 1)
+ return -1;
+
+ if (0x4000 > entry->size)
+ {
+ padding = 0x4000 - entry->size;
+
+ if (fwrite((void*)persistent->bmpData, padding, 1, persistent->fp) != 1)
+ return -1;
+ }
+
+ persistent->count++;
+
+ return 1;
+}
+
+static int persistent_cache_read_v2(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+ while (1)
+ {
+ PERSISTENT_CACHE_ENTRY_V2 entry = { 0 };
+
+ if (fread((void*)&entry, sizeof(entry), 1, persistent->fp) != 1)
+ break;
+
+ if (fseek(persistent->fp, 0x4000, SEEK_CUR) != 0)
+ break;
+
+ persistent->count++;
+ }
+
+ return 1;
+}
+
+static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent,
+ PERSISTENT_CACHE_ENTRY* entry)
+{
+ PERSISTENT_CACHE_ENTRY_V3 entry3 = { 0 };
+
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+
+ if (fread((void*)&entry3, sizeof(entry3), 1, persistent->fp) != 1)
+ return -1;
+
+ entry->key64 = entry3.key64;
+ entry->width = entry3.width;
+ entry->height = entry3.height;
+ entry->size = entry3.width * entry3.height * 4;
+ entry->flags = 0;
+
+ if (entry->size > persistent->bmpSize)
+ {
+ persistent->bmpSize = entry->size;
+ BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize,
+ sizeof(BYTE), 32);
+
+ if (!bmpData)
+ return -1;
+
+ persistent->bmpData = bmpData;
+ }
+
+ entry->data = persistent->bmpData;
+
+ if (fread((void*)entry->data, entry->size, 1, persistent->fp) != 1)
+ return -1;
+
+ return 1;
+}
+
+static int persistent_cache_write_entry_v3(rdpPersistentCache* persistent,
+ const PERSISTENT_CACHE_ENTRY* entry)
+{
+ PERSISTENT_CACHE_ENTRY_V3 entry3 = { 0 };
+
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+
+ entry3.key64 = entry->key64;
+ entry3.width = entry->width;
+ entry3.height = entry->height;
+
+ if (fwrite((void*)&entry3, sizeof(entry3), 1, persistent->fp) != 1)
+ return -1;
+
+ if (fwrite((void*)entry->data, entry->size, 1, persistent->fp) != 1)
+ return -1;
+
+ persistent->count++;
+
+ return 1;
+}
+
+static int persistent_cache_read_v3(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+ while (1)
+ {
+ PERSISTENT_CACHE_ENTRY_V3 entry = { 0 };
+
+ if (fread((void*)&entry, sizeof(entry), 1, persistent->fp) != 1)
+ break;
+
+ if (fseek(persistent->fp, (entry.width * entry.height * 4), SEEK_CUR) != 0)
+ break;
+
+ persistent->count++;
+ }
+
+ return 1;
+}
+
+int persistent_cache_read_entry(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
+{
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+
+ if (persistent->version == 3)
+ return persistent_cache_read_entry_v3(persistent, entry);
+ else if (persistent->version == 2)
+ return persistent_cache_read_entry_v2(persistent, entry);
+
+ return -1;
+}
+
+int persistent_cache_write_entry(rdpPersistentCache* persistent,
+ const PERSISTENT_CACHE_ENTRY* entry)
+{
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(entry);
+
+ if (persistent->version == 3)
+ return persistent_cache_write_entry_v3(persistent, entry);
+ else if (persistent->version == 2)
+ return persistent_cache_write_entry_v2(persistent, entry);
+
+ return -1;
+}
+
+static int persistent_cache_open_read(rdpPersistentCache* persistent)
+{
+ BYTE sig[8] = { 0 };
+ int status = 1;
+ long offset = 0;
+
+ WINPR_ASSERT(persistent);
+ persistent->fp = winpr_fopen(persistent->filename, "rb");
+
+ if (!persistent->fp)
+ return -1;
+
+ if (fread(sig, 8, 1, persistent->fp) != 1)
+ return -1;
+
+ if (!strncmp((const char*)sig, "RDP8bmp", 8))
+ persistent->version = 3;
+ else
+ persistent->version = 2;
+
+ fseek(persistent->fp, 0, SEEK_SET);
+
+ if (persistent->version == 3)
+ {
+ PERSISTENT_CACHE_HEADER_V3 header;
+
+ if (fread(&header, sizeof(header), 1, persistent->fp) != 1)
+ return -1;
+
+ status = persistent_cache_read_v3(persistent);
+ offset = sizeof(header);
+ }
+ else
+ {
+ status = persistent_cache_read_v2(persistent);
+ offset = 0;
+ }
+
+ fseek(persistent->fp, offset, SEEK_SET);
+
+ return status;
+}
+
+static int persistent_cache_open_write(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+
+ persistent->fp = winpr_fopen(persistent->filename, "w+b");
+
+ if (!persistent->fp)
+ return -1;
+
+ if (persistent->version == 3)
+ {
+ PERSISTENT_CACHE_HEADER_V3 header = { 0 };
+ strncpy((char*)header.sig, "RDP8bmp", 8);
+ header.flags = 0x00000006;
+
+ if (fwrite(&header, sizeof(header), 1, persistent->fp) != 1)
+ return -1;
+ }
+
+ ZeroMemory(persistent->bmpData, persistent->bmpSize);
+
+ return 1;
+}
+
+int persistent_cache_open(rdpPersistentCache* persistent, const char* filename, BOOL write,
+ UINT32 version)
+{
+ WINPR_ASSERT(persistent);
+ WINPR_ASSERT(filename);
+ persistent->write = write;
+
+ persistent->filename = _strdup(filename);
+
+ if (!persistent->filename)
+ return -1;
+
+ if (persistent->write)
+ {
+ persistent->version = version;
+ return persistent_cache_open_write(persistent);
+ }
+
+ return persistent_cache_open_read(persistent);
+}
+
+int persistent_cache_close(rdpPersistentCache* persistent)
+{
+ WINPR_ASSERT(persistent);
+ if (persistent->fp)
+ {
+ fclose(persistent->fp);
+ persistent->fp = NULL;
+ }
+
+ return 1;
+}
+
+rdpPersistentCache* persistent_cache_new(void)
+{
+ rdpPersistentCache* persistent = calloc(1, sizeof(rdpPersistentCache));
+
+ if (!persistent)
+ return NULL;
+
+ persistent->bmpSize = 0x4000;
+ persistent->bmpData = calloc(1, persistent->bmpSize);
+
+ if (!persistent->bmpData)
+ {
+ free(persistent);
+ return NULL;
+ }
+
+ return persistent;
+}
+
+void persistent_cache_free(rdpPersistentCache* persistent)
+{
+ if (!persistent)
+ return;
+
+ persistent_cache_close(persistent);
+
+ free(persistent->filename);
+
+ winpr_aligned_free(persistent->bmpData);
+
+ free(persistent);
+}
diff --git a/libfreerdp/cache/pointer.c b/libfreerdp/cache/pointer.c
new file mode 100644
index 0000000..cf9fb4c
--- /dev/null
+++ b/libfreerdp/cache/pointer.c
@@ -0,0 +1,593 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Glyph Cache
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/stream.h>
+
+#include <freerdp/log.h>
+
+#include "pointer.h"
+#include "cache.h"
+
+#define TAG FREERDP_TAG("cache.pointer")
+
+static BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
+ BOOL colorCache);
+static rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index);
+
+static void pointer_clear(rdpPointer* pointer)
+{
+ if (pointer)
+ {
+ pointer->lengthAndMask = 0;
+ free(pointer->andMaskData);
+ pointer->andMaskData = NULL;
+
+ pointer->lengthXorMask = 0;
+ free(pointer->xorMaskData);
+ pointer->xorMaskData = NULL;
+ }
+}
+
+static void pointer_free(rdpContext* context, rdpPointer* pointer)
+{
+ if (pointer)
+ {
+ IFCALL(pointer->Free, context, pointer);
+ pointer_clear(pointer);
+ }
+ free(pointer);
+}
+
+static BOOL update_pointer_position(rdpContext* context,
+ const POINTER_POSITION_UPDATE* pointer_position)
+{
+ if (!context || !context->graphics || !context->graphics->Pointer_Prototype ||
+ !pointer_position)
+ return FALSE;
+
+ const BOOL GrabMouse = freerdp_settings_get_bool(context->settings, FreeRDP_GrabMouse);
+ if (!GrabMouse)
+ return TRUE;
+
+ const rdpPointer* pointer = context->graphics->Pointer_Prototype;
+ WINPR_ASSERT(pointer);
+
+ return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos,
+ pointer_position->yPos);
+}
+
+static BOOL update_pointer_system(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system)
+{
+ rdpPointer* pointer = NULL;
+
+ if (!context || !context->graphics || !context->graphics->Pointer_Prototype || !pointer_system)
+ return FALSE;
+
+ pointer = context->graphics->Pointer_Prototype;
+
+ switch (pointer_system->type)
+ {
+ case SYSPTR_NULL:
+ return IFCALLRESULT(TRUE, pointer->SetNull, context);
+
+ case SYSPTR_DEFAULT:
+ return IFCALLRESULT(TRUE, pointer->SetDefault, context);
+
+ default:
+ WLog_ERR(TAG, "Unknown system pointer type (0x%08" PRIX32 ")", pointer_system->type);
+ }
+ return TRUE;
+}
+
+static BOOL upate_pointer_copy_andxor(rdpPointer* pointer, const BYTE* andMaskData,
+ size_t lengthAndMask, const BYTE* xorMaskData,
+ size_t lengthXorMask)
+{
+ WINPR_ASSERT(pointer);
+
+ pointer_clear(pointer);
+ if (lengthAndMask && andMaskData)
+ {
+ pointer->lengthAndMask = lengthAndMask;
+ pointer->andMaskData = (BYTE*)malloc(lengthAndMask);
+ if (!pointer->andMaskData)
+ return FALSE;
+
+ CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask);
+ }
+
+ if (lengthXorMask && xorMaskData)
+ {
+ pointer->lengthXorMask = lengthXorMask;
+ pointer->xorMaskData = (BYTE*)malloc(lengthXorMask);
+ if (!pointer->xorMaskData)
+ return FALSE;
+
+ CopyMemory(pointer->xorMaskData, xorMaskData, lengthXorMask);
+ }
+
+ return TRUE;
+}
+
+static BOOL update_pointer_color(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color)
+{
+ rdpPointer* pointer = NULL;
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(pointer_color);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ pointer = Pointer_Alloc(context);
+
+ if (pointer == NULL)
+ return FALSE;
+ pointer->xorBpp = 24;
+ pointer->xPos = pointer_color->hotSpotX;
+ pointer->yPos = pointer_color->hotSpotY;
+ pointer->width = pointer_color->width;
+ pointer->height = pointer_color->height;
+
+ if (!upate_pointer_copy_andxor(pointer, pointer_color->andMaskData,
+ pointer_color->lengthAndMask, pointer_color->xorMaskData,
+ pointer_color->lengthXorMask))
+ goto out_fail;
+
+ if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
+ goto out_fail;
+
+ if (!pointer_cache_put(cache->pointer, pointer_color->cacheIndex, pointer, TRUE))
+ goto out_fail;
+
+ return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
+
+out_fail:
+ pointer_free(context, pointer);
+ return FALSE;
+}
+
+static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large)
+{
+ rdpPointer* pointer = NULL;
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(pointer_large);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ pointer = Pointer_Alloc(context);
+ if (pointer == NULL)
+ return FALSE;
+ pointer->xorBpp = pointer_large->xorBpp;
+ pointer->xPos = pointer_large->hotSpotX;
+ pointer->yPos = pointer_large->hotSpotY;
+ pointer->width = pointer_large->width;
+ pointer->height = pointer_large->height;
+
+ if (!upate_pointer_copy_andxor(pointer, pointer_large->andMaskData,
+ pointer_large->lengthAndMask, pointer_large->xorMaskData,
+ pointer_large->lengthXorMask))
+ goto out_fail;
+
+ if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
+ goto out_fail;
+
+ if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer, FALSE))
+ goto out_fail;
+
+ return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
+
+out_fail:
+ pointer_free(context, pointer);
+ return FALSE;
+}
+
+static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
+{
+ if (!context || !pointer_new)
+ return FALSE;
+
+ rdpCache* cache = context->cache;
+ rdpPointer* pointer = Pointer_Alloc(context);
+
+ if (!pointer)
+ return FALSE;
+
+ pointer->xorBpp = pointer_new->xorBpp;
+ pointer->xPos = pointer_new->colorPtrAttr.hotSpotX;
+ pointer->yPos = pointer_new->colorPtrAttr.hotSpotY;
+ pointer->width = pointer_new->colorPtrAttr.width;
+ pointer->height = pointer_new->colorPtrAttr.height;
+ if (!upate_pointer_copy_andxor(
+ pointer, pointer_new->colorPtrAttr.andMaskData, pointer_new->colorPtrAttr.lengthAndMask,
+ pointer_new->colorPtrAttr.xorMaskData, pointer_new->colorPtrAttr.lengthXorMask))
+ goto out_fail;
+
+ if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
+ goto out_fail;
+
+ if (!pointer_cache_put(cache->pointer, pointer_new->colorPtrAttr.cacheIndex, pointer, FALSE))
+ goto out_fail;
+
+ return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
+
+out_fail:
+ pointer_free(context, pointer);
+ return FALSE;
+}
+
+static BOOL update_pointer_cached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached)
+{
+ rdpPointer* pointer = NULL;
+ rdpCache* cache = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(pointer_cached);
+
+ cache = context->cache;
+ WINPR_ASSERT(cache);
+
+ pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex);
+
+ if (pointer != NULL)
+ return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
+
+ return FALSE;
+}
+
+rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index)
+{
+ rdpPointer* pointer = NULL;
+
+ WINPR_ASSERT(pointer_cache);
+
+ if (index >= pointer_cache->cacheSize)
+ {
+ WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index,
+ pointer_cache->cacheSize);
+ return NULL;
+ }
+
+ WINPR_ASSERT(pointer_cache->entries);
+ pointer = pointer_cache->entries[index];
+ return pointer;
+}
+
+BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
+ BOOL colorCache)
+{
+ rdpPointer* prevPointer = NULL;
+ const FreeRDP_Settings_Keys_UInt32 id =
+ colorCache ? FreeRDP_ColorPointerCacheSize : FreeRDP_PointerCacheSize;
+
+ WINPR_ASSERT(pointer_cache);
+ WINPR_ASSERT(pointer_cache->context);
+
+ const UINT32 size = freerdp_settings_get_uint32(pointer_cache->context->settings, id);
+ if (index >= pointer_cache->cacheSize)
+ {
+ WLog_ERR(TAG,
+ "invalid pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32 "]",
+ index, pointer_cache->cacheSize,
+ colorCache ? "color-pointer-cache" : "pointer-cache", size);
+ return FALSE;
+ }
+ if (index >= size)
+ {
+ WLog_WARN(TAG,
+ "suspicious pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32
+ "]",
+ index, pointer_cache->cacheSize,
+ colorCache ? "color-pointer-cache" : "pointer-cache", size);
+ }
+
+ WINPR_ASSERT(pointer_cache->entries);
+ prevPointer = pointer_cache->entries[index];
+ pointer_free(pointer_cache->context, prevPointer);
+ pointer_cache->entries[index] = pointer;
+ return TRUE;
+}
+
+void pointer_cache_register_callbacks(rdpUpdate* update)
+{
+ rdpPointerUpdate* pointer = NULL;
+
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->context);
+
+ pointer = update->pointer;
+ WINPR_ASSERT(pointer);
+
+ if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
+ {
+ pointer->PointerPosition = update_pointer_position;
+ pointer->PointerSystem = update_pointer_system;
+ pointer->PointerColor = update_pointer_color;
+ pointer->PointerLarge = update_pointer_large;
+ pointer->PointerNew = update_pointer_new;
+ pointer->PointerCached = update_pointer_cached;
+ }
+}
+
+rdpPointerCache* pointer_cache_new(rdpContext* context)
+{
+ rdpPointerCache* pointer_cache = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(context);
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
+
+ if (!pointer_cache)
+ return NULL;
+
+ pointer_cache->context = context;
+
+ /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size
+ * matches */
+ const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
+ const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
+ pointer_cache->cacheSize = MAX(size, colorSize) + 1;
+
+ pointer_cache->entries = (rdpPointer**)calloc(pointer_cache->cacheSize, sizeof(rdpPointer*));
+
+ if (!pointer_cache->entries)
+ {
+ free(pointer_cache);
+ return NULL;
+ }
+
+ return pointer_cache;
+}
+
+void pointer_cache_free(rdpPointerCache* pointer_cache)
+{
+ if (pointer_cache != NULL)
+ {
+ if (pointer_cache->entries)
+ {
+ for (UINT32 i = 0; i < pointer_cache->cacheSize; i++)
+ {
+ rdpPointer* pointer = pointer_cache->entries[i];
+ pointer_free(pointer_cache->context, pointer);
+ }
+ }
+
+ free(pointer_cache->entries);
+ free(pointer_cache);
+ }
+}
+
+POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
+ const POINTER_COLOR_UPDATE* src)
+{
+ POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
+
+ if (!dst || !src)
+ goto fail;
+
+ *dst = *src;
+
+ if (src->lengthAndMask > 0)
+ {
+ dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE));
+
+ if (!dst->andMaskData)
+ goto fail;
+
+ memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask);
+ }
+
+ if (src->lengthXorMask > 0)
+ {
+ dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE));
+
+ if (!dst->xorMaskData)
+ goto fail;
+
+ memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask);
+ }
+
+ return dst;
+fail:
+ free_pointer_color_update(context, dst);
+ return NULL;
+}
+
+void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer)
+{
+ WINPR_UNUSED(context);
+
+ if (!pointer)
+ return;
+
+ free(pointer->xorMaskData);
+ free(pointer->andMaskData);
+ free(pointer);
+}
+
+POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
+ const POINTER_LARGE_UPDATE* src)
+{
+ POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
+
+ if (!dst || !src)
+ goto fail;
+
+ *dst = *src;
+
+ if (src->lengthAndMask > 0)
+ {
+ dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE));
+
+ if (!dst->andMaskData)
+ goto fail;
+
+ memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask);
+ }
+
+ if (src->lengthXorMask > 0)
+ {
+ dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE));
+
+ if (!dst->xorMaskData)
+ goto fail;
+
+ memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask);
+ }
+
+ return dst;
+fail:
+ free_pointer_large_update(context, dst);
+ return NULL;
+}
+
+void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer)
+{
+ WINPR_UNUSED(context);
+ if (!pointer)
+ return;
+
+ free(pointer->xorMaskData);
+ free(pointer->andMaskData);
+ free(pointer);
+}
+
+POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* src)
+{
+ POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
+
+ if (!dst || !src)
+ goto fail;
+
+ *dst = *src;
+
+ if (src->colorPtrAttr.lengthAndMask > 0)
+ {
+ dst->colorPtrAttr.andMaskData = calloc(src->colorPtrAttr.lengthAndMask, sizeof(BYTE));
+
+ if (!dst->colorPtrAttr.andMaskData)
+ goto fail;
+
+ memcpy(dst->colorPtrAttr.andMaskData, src->colorPtrAttr.andMaskData,
+ src->colorPtrAttr.lengthAndMask);
+ }
+
+ if (src->colorPtrAttr.lengthXorMask > 0)
+ {
+ dst->colorPtrAttr.xorMaskData = calloc(src->colorPtrAttr.lengthXorMask, sizeof(BYTE));
+
+ if (!dst->colorPtrAttr.xorMaskData)
+ goto fail;
+
+ memcpy(dst->colorPtrAttr.xorMaskData, src->colorPtrAttr.xorMaskData,
+ src->colorPtrAttr.lengthXorMask);
+ }
+
+ return dst;
+fail:
+ free_pointer_new_update(context, dst);
+ return NULL;
+}
+
+void free_pointer_new_update(rdpContext* context, POINTER_NEW_UPDATE* pointer)
+{
+ if (!pointer)
+ return;
+
+ free(pointer->colorPtrAttr.xorMaskData);
+ free(pointer->colorPtrAttr.andMaskData);
+ free(pointer);
+}
+
+POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context,
+ const POINTER_CACHED_UPDATE* pointer)
+{
+ POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE));
+
+ if (!dst)
+ goto fail;
+
+ *dst = *pointer;
+ return dst;
+fail:
+ free_pointer_cached_update(context, dst);
+ return NULL;
+}
+
+void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer)
+{
+ WINPR_UNUSED(context);
+ free(pointer);
+}
+
+void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer)
+{
+ WINPR_UNUSED(context);
+ free(pointer);
+}
+
+POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context,
+ const POINTER_POSITION_UPDATE* pointer)
+{
+ POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE));
+
+ if (!dst || !pointer)
+ goto fail;
+
+ *dst = *pointer;
+ return dst;
+fail:
+ free_pointer_position_update(context, dst);
+ return NULL;
+}
+
+void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer)
+{
+ WINPR_UNUSED(context);
+ free(pointer);
+}
+
+POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context,
+ const POINTER_SYSTEM_UPDATE* pointer)
+{
+ POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
+
+ if (!dst || !pointer)
+ goto fail;
+
+ *dst = *pointer;
+ return dst;
+fail:
+ free_pointer_system_update(context, dst);
+ return NULL;
+}
diff --git a/libfreerdp/cache/pointer.h b/libfreerdp/cache/pointer.h
new file mode 100644
index 0000000..c54e4f6
--- /dev/null
+++ b/libfreerdp/cache/pointer.h
@@ -0,0 +1,95 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LIB_CACHE_POINTER_H
+#define FREERDP_LIB_CACHE_POINTER_H
+
+#include <freerdp/api.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/pointer.h>
+
+typedef struct rdp_pointer_cache rdpPointerCache;
+
+struct rdp_pointer_cache
+{
+ UINT32 cacheSize; /* 0 */
+ rdpPointer** entries; /* 1 */
+
+ /* internal */
+ rdpContext* context;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_LOCAL void pointer_cache_register_callbacks(rdpUpdate* update);
+
+ FREERDP_LOCAL void pointer_cache_free(rdpPointerCache* pointer_cache);
+
+ WINPR_ATTR_MALLOC(pointer_cache_free, 1)
+ FREERDP_LOCAL rdpPointerCache* pointer_cache_new(rdpContext* context);
+
+ FREERDP_LOCAL void free_pointer_color_update(rdpContext* context,
+ POINTER_COLOR_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_color_update, 1)
+ FREERDP_LOCAL POINTER_COLOR_UPDATE*
+ copy_pointer_color_update(rdpContext* context, const POINTER_COLOR_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_pointer_large_update(rdpContext* context,
+ POINTER_LARGE_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_large_update, 1)
+ FREERDP_LOCAL POINTER_LARGE_UPDATE*
+ copy_pointer_large_update(rdpContext* context, const POINTER_LARGE_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_pointer_new_update(rdpContext* context, POINTER_NEW_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_new_update, 1)
+ FREERDP_LOCAL POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context,
+ const POINTER_NEW_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_pointer_cached_update(rdpContext* context,
+ POINTER_CACHED_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_cached_update, 1)
+ FREERDP_LOCAL POINTER_CACHED_UPDATE*
+ copy_pointer_cached_update(rdpContext* context, const POINTER_CACHED_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_pointer_position_update(rdpContext* context,
+ POINTER_POSITION_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_position_update, 1)
+ FREERDP_LOCAL POINTER_POSITION_UPDATE*
+ copy_pointer_position_update(rdpContext* context, const POINTER_POSITION_UPDATE* pointer);
+
+ FREERDP_LOCAL void free_pointer_system_update(rdpContext* context,
+ POINTER_SYSTEM_UPDATE* pointer);
+
+ WINPR_ATTR_MALLOC(free_pointer_system_update, 1)
+ FREERDP_LOCAL POINTER_SYSTEM_UPDATE*
+ copy_pointer_system_update(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_LIB_CACHE_POINTER_H */