diff options
Diffstat (limited to 'libfreerdp/cache')
-rw-r--r-- | libfreerdp/cache/CMakeLists.txt | 39 | ||||
-rw-r--r-- | libfreerdp/cache/bitmap.c | 608 | ||||
-rw-r--r-- | libfreerdp/cache/bitmap.h | 95 | ||||
-rw-r--r-- | libfreerdp/cache/brush.c | 326 | ||||
-rw-r--r-- | libfreerdp/cache/brush.h | 57 | ||||
-rw-r--r-- | libfreerdp/cache/cache.c | 152 | ||||
-rw-r--r-- | libfreerdp/cache/cache.h | 73 | ||||
-rw-r--r-- | libfreerdp/cache/glyph.c | 892 | ||||
-rw-r--r-- | libfreerdp/cache/glyph.h | 78 | ||||
-rw-r--r-- | libfreerdp/cache/nine_grid.c | 169 | ||||
-rw-r--r-- | libfreerdp/cache/nine_grid.h | 50 | ||||
-rw-r--r-- | libfreerdp/cache/offscreen.c | 243 | ||||
-rw-r--r-- | libfreerdp/cache/offscreen.h | 50 | ||||
-rw-r--r-- | libfreerdp/cache/palette.c | 142 | ||||
-rw-r--r-- | libfreerdp/cache/palette.h | 65 | ||||
-rw-r--r-- | libfreerdp/cache/persistent.c | 374 | ||||
-rw-r--r-- | libfreerdp/cache/pointer.c | 593 | ||||
-rw-r--r-- | libfreerdp/cache/pointer.h | 95 |
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 */ |