Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -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
)

View File

@@ -0,0 +1,622 @@
/**
* 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 <winpr/cast.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* bitmapCache, UINT32 id, UINT32 index,
rdpBitmap* bitmap);
static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
{
rdpBitmap* bitmap = nullptr;
rdpCache* cache = nullptr;
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 == nullptr)
return TRUE;
memblt->bitmap = bitmap;
return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
}
static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
{
rdpBitmap* bitmap = nullptr;
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;
const BYTE style = WINPR_ASSERTING_INT_CAST(UINT8, 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 = nullptr;
rdpBitmap* prevBitmap = nullptr;
rdpCache* cache = context->cache;
bitmap = Bitmap_Alloc(context);
if (!bitmap)
return FALSE;
if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapWidth),
WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapHeight)))
goto fail;
if (!bitmap->Decompress(context, bitmap, cacheBitmap->bitmapDataStream,
cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight,
cacheBitmap->bitmapBpp, cacheBitmap->bitmapLength,
cacheBitmap->compressed, RDP_CODEC_ID_NONE))
goto fail;
if (!bitmap->New(context, bitmap))
goto fail;
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);
fail:
Bitmap_Free(context, bitmap);
return FALSE;
}
static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
{
rdpBitmap* prevBitmap = nullptr;
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;
if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapWidth),
WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapHeight)))
goto fail;
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 = nullptr;
rdpBitmap* prevBitmap = nullptr;
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);
if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->width),
WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->height)))
goto fail;
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 = nullptr;
if (id >= bitmapCache->maxCells)
{
WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
return nullptr;
}
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 nullptr;
}
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 = nullptr;
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 = WINPR_C_ARRAY_INIT;
rdpBitmap* bitmap = cell->entries[j];
if (!bitmap || !bitmap->key64)
continue;
cacheEntry.key64 = bitmap->key64;
cacheEntry.width = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->width);
cacheEntry.height = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->height);
const UINT64 size = 4ULL * bitmap->width * bitmap->height;
if (size > UINT32_MAX)
continue;
cacheEntry.size = (UINT32)size;
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 = nullptr;
rdpBitmapCache* bitmapCache = nullptr;
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
bitmapCache = (rdpBitmapCache*)calloc(1, sizeof(rdpBitmapCache));
if (!bitmapCache)
return nullptr;
const UINT32 BitmapCacheV2NumCells =
freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
bitmapCache->context = context;
/* overallocate by 1. older RDP servers do send a off by 1 cache index. */
bitmapCache->cells =
(BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells + 1ull, 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 nullptr;
}
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((void*)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 nullptr;
}
void free_bitmap_update(WINPR_ATTR_UNUSED rdpContext* context,
WINPR_ATTR_UNUSED 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 nullptr;
}
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 nullptr;
}
void free_cache_bitmap_order(WINPR_ATTR_UNUSED 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 nullptr;
}
void free_cache_bitmap_v2_order(WINPR_ATTR_UNUSED rdpContext* context,
WINPR_ATTR_UNUSED 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 nullptr;
}
void free_cache_bitmap_v3_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
{
if (order)
free(order->bitmapData.data);
free(order);
}

View File

@@ -0,0 +1,100 @@
/**
* 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
{
WINPR_ATTR_NODISCARD pMemBlt MemBlt; /* 0 */
WINPR_ATTR_NODISCARD pMem3Blt Mem3Blt; /* 1 */
WINPR_ATTR_NODISCARD pCacheBitmap CacheBitmap; /* 2 */
WINPR_ATTR_NODISCARD pCacheBitmapV2 CacheBitmapV2; /* 3 */
WINPR_ATTR_NODISCARD pCacheBitmapV3 CacheBitmapV3; /* 4 */
WINPR_ATTR_NODISCARD 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* bitmapCache);
WINPR_ATTR_MALLOC(bitmap_cache_free, 1)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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 */

View File

@@ -0,0 +1,322 @@
/**
* 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)
{
BOOL ret = TRUE;
WINPR_ASSERT(context);
WINPR_ASSERT(patblt);
const rdpCache* cache = context->cache;
WINPR_ASSERT(cache);
rdpBrush* brush = &patblt->brush;
WINPR_ASSERT(brush->style <= UINT8_MAX);
const BYTE style = (BYTE)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 = nullptr;
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)
{
BOOL ret = TRUE;
WINPR_ASSERT(context);
WINPR_ASSERT(polygon_cb);
rdpCache* cache = context->cache;
WINPR_ASSERT(cache);
rdpBrush* brush = &polygon_cb->brush;
WINPR_ASSERT(brush->style <= UINT8_MAX);
const BYTE style = (UINT8)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 = nullptr;
rdpCache* cache = nullptr;
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 = nullptr;
if (!brushCache)
return nullptr;
if (!bpp)
return nullptr;
if (*bpp == 1)
{
if (index >= brushCache->maxMonoEntries)
{
WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", *bpp, index);
return nullptr;
}
*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 nullptr;
}
*bpp = brushCache->entries[index].bpp;
entry = brushCache->entries[index].entry;
}
if (entry == nullptr)
{
WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) at index: 0x%08" PRIX32 "", *bpp, index);
return nullptr;
}
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 = nullptr;
WINPR_ASSERT(context);
brushCache = (rdpBrushCache*)calloc(1, sizeof(rdpBrushCache));
if (!brushCache)
return nullptr;
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 nullptr;
}
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 = nullptr;
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 nullptr;
}

View File

@@ -0,0 +1,60 @@
/**
* 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
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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 */

View File

@@ -0,0 +1,153 @@
/**
* 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 = nullptr;
WINPR_ASSERT(context);
cache = (rdpCache*)calloc(1, sizeof(rdpCache));
if (!cache)
return nullptr;
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 nullptr;
}
void cache_free(rdpCache* cache)
{
if (cache != nullptr)
{
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 nullptr;
}
void free_cache_color_table_order(WINPR_ATTR_UNUSED 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 nullptr;
}
void free_surface_bits_command(WINPR_ATTR_UNUSED rdpContext* context, SURFACE_BITS_COMMAND* order)
{
if (order)
free(order->bmp.bitmapData);
free(order);
}

View File

@@ -0,0 +1,76 @@
/**
* 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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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 */

View File

@@ -0,0 +1,886 @@
/**
* 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/cast.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* glyphCache, UINT32 id, UINT32 index);
static BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index, rdpGlyph* glyph);
static const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size);
static BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
const void* fragment);
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, "glyph index out of bound %" PRIu32 " [max %" PRIuz "]", index,
length);
}
if (flAccel & SO_VERTICAL)
*y += WINPR_ASSERTING_INT_CAST(int32_t, offset);
if (flAccel & SO_HORIZONTAL)
*x += WINPR_ASSERTING_INT_CAST(int32_t, offset);
}
return index;
}
static BOOL update_process_glyph(rdpContext* context, const BYTE* data, UINT32 cacheIndex, INT32* x,
const INT32* y, UINT32 cacheId, UINT32 flAccel, BOOL fOpRedundant,
const RDP_RECT* bound)
{
INT32 sx = 0;
INT32 sy = 0;
if (!context || !data || !x || !y || !context->graphics || !context->cache ||
!context->cache->glyph)
return FALSE;
rdpGlyphCache* glyph_cache = context->cache->glyph;
rdpGlyph* glyph = glyph_cache_get(glyph_cache, cacheId, cacheIndex);
if (!glyph)
return FALSE;
INT32 dx = glyph->x + *x;
INT32 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 = WINPR_ASSERTING_INT_CAST(int32_t, glyph->cx) - sx;
INT32 dh = WINPR_ASSERTING_INT_CAST(int32_t, 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 += WINPR_ASSERTING_INT_CAST(int32_t, 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 = nullptr;
RDP_RECT bound = WINPR_C_ARRAY_INIT;
BOOL rc = FALSE;
if (!context || !data || !context->graphics || !context->cache || !context->cache->glyph)
return FALSE;
rdpGraphics* graphics = context->graphics;
WINPR_ASSERT(graphics);
WINPR_ASSERT(context->cache);
rdpGlyphCache* glyph_cache = context->cache->glyph;
WINPR_ASSERT(glyph_cache);
{
rdpGlyph* glyph = graphics->Glyph_Prototype;
if (!glyph)
goto fail;
/* 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;
{
const UINT32 w = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
if (opX + opWidth > (INT64)w)
{
/**
* 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 = WINPR_ASSERTING_INT_CAST(int, w) - opX;
}
if (bkX + bkWidth > (INT64)w)
{
/**
* 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 = WINPR_ASSERTING_INT_CAST(int, w) - bkX;
}
}
bound.x = WINPR_ASSERTING_INT_CAST(INT16, bkX);
bound.y = WINPR_ASSERTING_INT_CAST(INT16, bkY);
bound.width = WINPR_ASSERTING_INT_CAST(INT16, bkWidth);
bound.height = WINPR_ASSERTING_INT_CAST(INT16, bkHeight);
if (!glyph->BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor, fOpRedundant))
goto fail;
if (!IFCALLRESULT(TRUE, glyph->SetBounds, context, bkX, bkY, bkWidth, bkHeight))
goto fail;
while (index < length)
{
const UINT32 op = data[index++];
switch (op)
{
case GLYPH_FRAGMENT_USE:
if (index + 1 > length)
goto fail;
id = data[index++];
fragments = (const BYTE*)glyph_cache_fragment_get(glyph_cache, id, &size);
if (fragments == nullptr)
goto fail;
for (UINT32 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))
goto fail;
}
break;
case GLYPH_FRAGMENT_ADD:
if (index + 2 > length)
goto fail;
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))
goto fail;
break;
}
}
if (!glyph->EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor))
goto fail;
}
rc = TRUE;
fail:
return rc;
}
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,
WINPR_ASSERTING_INT_CAST(int32_t, glyphIndex->fOpRedundant));
}
static BOOL update_gdi_fast_index(rdpContext* context, const FAST_INDEX_ORDER* fastIndex)
{
INT32 opWidth = 0;
INT32 opHeight = 0;
INT32 bkWidth = 0;
INT32 bkHeight = 0;
BOOL rc = FALSE;
if (!context || !fastIndex || !context->cache)
return FALSE;
INT32 opLeft = fastIndex->opLeft;
INT32 opTop = fastIndex->opTop;
INT32 opRight = fastIndex->opRight;
INT32 opBottom = fastIndex->opBottom;
INT32 x = fastIndex->x;
INT32 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;
if (!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))
goto fail;
rc = TRUE;
fail:
return rc;
}
static BOOL update_gdi_fast_glyph(rdpContext* context, const FAST_GLYPH_ORDER* fastGlyph)
{
INT32 x = 0;
INT32 y = 0;
BYTE text_data[4] = WINPR_C_ARRAY_INIT;
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 = nullptr;
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 = nullptr;
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)
{
if (!context || !cacheGlyph || !context->cache)
return FALSE;
rdpCache* cache = context->cache;
for (size_t i = 0; i < cacheGlyph->cGlyphs; i++)
{
const GLYPH_DATA* glyph_data = &cacheGlyph->glyphData[i];
rdpGlyph* glyph = Glyph_Alloc(context, glyph_data->x, glyph_data->y, glyph_data->cx,
glyph_data->cy, glyph_data->cb, glyph_data->aj);
if (!glyph)
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)
{
if (!context || !cacheGlyphV2 || !context->cache)
return FALSE;
rdpCache* cache = context->cache;
for (size_t i = 0; i < cacheGlyphV2->cGlyphs; i++)
{
const GLYPH_DATA_V2* glyphData = &cacheGlyphV2->glyphData[i];
rdpGlyph* 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)
{
WINPR_ASSERT(glyphCache);
WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCacheGet: id: %" PRIu32 " index: %" PRIu32 "", id,
index);
if (id >= ARRAYSIZE(glyphCache->glyphCache))
{
WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
return nullptr;
}
GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
if (index > cache->number)
{
WLog_ERR(TAG, "index %" PRIu32 " out of range for cache id: %" PRIu32 "", index, id);
return nullptr;
}
rdpGlyph* glyph = cache->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)
{
WINPR_ASSERT(glyphCache);
if (id >= ARRAYSIZE(glyphCache->glyphCache))
{
WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
return FALSE;
}
GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
if (index >= cache->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);
rdpGlyph* prevGlyph = cache->entries[index];
if (prevGlyph)
{
WINPR_ASSERT(prevGlyph->Free);
prevGlyph->Free(glyphCache->context, prevGlyph);
}
cache->entries[index] = glyph;
return TRUE;
}
const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size)
{
void* fragment = nullptr;
WINPR_ASSERT(glyphCache);
WINPR_ASSERT(glyphCache->fragCache.entries);
if (index > 255)
{
WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
return nullptr;
}
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)
{
WINPR_ASSERT(glyphCache);
WINPR_ASSERT(glyphCache->fragCache.entries);
if (index > 255)
{
WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
return FALSE;
}
if (size == 0)
return FALSE;
void* copy = malloc(size);
if (!copy)
return FALSE;
WLog_Print(glyphCache->log, WLOG_DEBUG,
"GlyphCacheFragmentPut: index: %" PRIu32 " size: %" PRIu32 "", index, size);
CopyMemory(copy, fragment, size);
void* 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 = nullptr;
rdpSettings* settings = nullptr;
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
glyphCache = (rdpGlyphCache*)calloc(1, sizeof(rdpGlyphCache));
if (!glyphCache)
return nullptr;
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 nullptr;
}
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] = nullptr;
}
}
free((void*)entries);
cache[i].entries = nullptr;
}
for (size_t i = 0; i < ARRAYSIZE(glyphCache->fragCache.entries); i++)
{
free(glyphCache->fragCache.entries[i].fragment);
glyphCache->fragCache.entries[i].fragment = nullptr;
}
free(glyphCache);
}
}
CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context, const CACHE_GLYPH_ORDER* glyph)
{
CACHE_GLYPH_ORDER* dst = nullptr;
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 nullptr;
}
void free_cache_glyph_order(WINPR_ATTR_UNUSED 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 = nullptr;
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 nullptr;
}
void free_cache_glyph_v2_order(WINPR_ATTR_UNUSED 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);
}

View File

@@ -0,0 +1,85 @@
/**
* 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)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpGlyphCache* glyph_cache_new(rdpContext* context);
FREERDP_LOCAL void free_cache_glyph_order(rdpContext* context, CACHE_GLYPH_ORDER* glyph);
WINPR_ATTR_MALLOC(free_cache_glyph_order, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context,
const CACHE_GLYPH_ORDER* glyph);
FREERDP_LOCAL void free_cache_glyph_v2_order(rdpContext* context, CACHE_GLYPH_V2_ORDER* glyph);
WINPR_ATTR_MALLOC(free_cache_glyph_v2_order, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL CACHE_GLYPH_V2_ORDER*
copy_cache_glyph_v2_order(rdpContext* context, const CACHE_GLYPH_V2_ORDER* glyph);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CACHE_GLYPH_H */

View File

@@ -0,0 +1,130 @@
/**
* 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/update.h>
#include <freerdp/freerdp.h>
#include <winpr/stream.h>
#include "nine_grid.h"
#include "cache.h"
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 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;
}
rdpNineGridCache* nine_grid_cache_new(rdpContext* context)
{
rdpNineGridCache* nine_grid = nullptr;
rdpSettings* settings = nullptr;
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
nine_grid = (rdpNineGridCache*)calloc(1, sizeof(rdpNineGridCache));
if (!nine_grid)
return nullptr;
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 nullptr;
}
void nine_grid_cache_free(rdpNineGridCache* nine_grid)
{
if (nine_grid != nullptr)
{
if (nine_grid->entries != nullptr)
{
for (size_t i = 0; i < nine_grid->maxEntries; i++)
free(nine_grid->entries[i].entry);
free(nine_grid->entries);
}
free(nine_grid);
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpNineGridCache* nine_grid_cache_new(rdpContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_NINE_GRID_CACHE_H */

View File

@@ -0,0 +1,255 @@
/**
* 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/cast.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* offscreenCache, 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)
{
if (!context || !createOffscreenBitmap || !context->cache)
return FALSE;
rdpCache* cache = context->cache;
rdpBitmap* bitmap = Bitmap_Alloc(context);
if (!bitmap)
return FALSE;
if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, createOffscreenBitmap->cx),
WINPR_ASSERTING_INT_CAST(UINT16, createOffscreenBitmap->cy)))
{
Bitmap_Free(context, bitmap);
return FALSE;
}
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)
{
if (!bitmap->SetSurface(context, bitmap, FALSE))
return FALSE;
}
for (UINT32 i = 0; i < createOffscreenBitmap->deleteList.cIndices; i++)
{
const UINT16 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)
{
if (!context || !context->cache || !switchSurface || !context->graphics)
return FALSE;
rdpCache* cache = context->cache;
rdpBitmap* bitmap = context->graphics->Bitmap_Prototype;
if (!bitmap)
return FALSE;
if (switchSurface->bitmapId == SCREEN_BITMAP_SURFACE)
{
if (!bitmap->SetSurface(context, nullptr, TRUE))
return FALSE;
}
else
{
rdpBitmap* bmp = nullptr;
bmp = offscreen_cache_get(cache->offscreen, switchSurface->bitmapId);
if (bmp == nullptr)
return FALSE;
if (!bitmap->SetSurface(context, bmp, FALSE))
return FALSE;
}
cache->offscreen->currentSurface = switchSurface->bitmapId;
return TRUE;
}
rdpBitmap* offscreen_cache_get(rdpOffscreenCache* offscreenCache, UINT32 index)
{
rdpBitmap* bitmap = nullptr;
WINPR_ASSERT(offscreenCache);
if (index >= offscreenCache->maxEntries)
{
WLog_ERR(TAG, "invalid offscreen bitmap index: 0x%08" PRIX32 "", index);
return nullptr;
}
bitmap = offscreenCache->entries[index];
if (!bitmap)
{
WLog_ERR(TAG, "invalid offscreen bitmap at index: 0x%08" PRIX32 "", index);
return nullptr;
}
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)
{
WINPR_ASSERT(offscreenCache);
if (index >= offscreenCache->maxEntries)
{
WLog_ERR(TAG, "invalid offscreen bitmap index (delete): 0x%08" PRIX32 "", index);
return;
}
rdpBitmap* prevBitmap = offscreenCache->entries[index];
if (prevBitmap != nullptr)
{
WINPR_ASSERT(offscreenCache->context);
/* Ensure that the bitmap is no longer used in GDI */
if (prevBitmap->SetSurface)
{
if (!prevBitmap->SetSurface(offscreenCache->context, nullptr, FALSE))
WLog_WARN(TAG, "prevBitmap->SetSurface failed");
}
Bitmap_Free(offscreenCache->context, prevBitmap);
}
offscreenCache->entries[index] = nullptr;
}
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 = nullptr;
rdpSettings* settings = nullptr;
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
offscreenCache = (rdpOffscreenCache*)calloc(1, sizeof(rdpOffscreenCache));
if (!offscreenCache)
return nullptr;
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 nullptr;
}
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((void*)offscreenCache->entries);
free(offscreenCache);
}
}

View File

@@ -0,0 +1,52 @@
/**
* 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
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpBitmap* offscreen_cache_get(rdpOffscreenCache* offscreenCache, 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)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpOffscreenCache* offscreen_cache_new(rdpContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_OFFSCREEN_CACHE_H */

View File

@@ -0,0 +1,121 @@
/**
* 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_put(rdpPaletteCache* palette, UINT32 index, void* entry);
static BOOL update_gdi_cache_color_table(rdpContext* context,
const CACHE_COLOR_TABLE_ORDER* cacheColorTable)
{
UINT32* colorTable = nullptr;
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_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)
{
WINPR_ASSERT(update);
WINPR_ASSERT(update->secondary);
update->secondary->CacheColorTable = update_gdi_cache_color_table;
}
rdpPaletteCache* palette_cache_new(rdpContext* context)
{
rdpPaletteCache* paletteCache = nullptr;
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(WINPR_ATTR_UNUSED 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 nullptr;
}

View File

@@ -0,0 +1,67 @@
/**
* 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* paletteCache);
WINPR_ATTR_MALLOC(palette_cache_free, 1)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL PALETTE_UPDATE* copy_palette_update(rdpContext* context,
const PALETTE_UPDATE* pointer);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CACHE_PALETTE_H */

View File

@@ -0,0 +1,376 @@
/**
* 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/cache/persistent.h>
struct rdp_persistent_cache
{
FILE* fp;
BOOL write;
int version;
int count;
char* filename;
BYTE* bmpData;
UINT32 bmpSize;
};
static const char sig_str[] = "RDP8bmp";
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 = WINPR_C_ARRAY_INIT;
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)
{
PERSISTENT_CACHE_ENTRY_V2 entry2 = WINPR_C_ARRAY_INIT;
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(&entry2, sizeof(entry2), 1, persistent->fp) != 1)
return -1;
if (fwrite(entry->data, entry->size, 1, persistent->fp) != 1)
return -1;
if (0x4000 > entry->size)
{
const size_t padding = 0x4000 - entry->size;
if (fwrite(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 = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(persistent);
WINPR_ASSERT(entry);
if (fread(&entry3, sizeof(entry3), 1, persistent->fp) != 1)
return -1;
entry->key64 = entry3.key64;
entry->width = entry3.width;
entry->height = entry3.height;
const UINT64 size = 4ull * entry3.width * entry3.height;
if (size > UINT32_MAX)
return -1;
entry->size = (UINT32)size;
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 = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
if (fread((void*)&entry, sizeof(entry), 1, persistent->fp) != 1)
break;
if (_fseeki64(persistent->fp, (4LL * entry.width * entry.height), 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] = WINPR_C_ARRAY_INIT;
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 (memcmp(sig, sig_str, sizeof(sig_str)) == 0)
persistent->version = 3;
else
persistent->version = 2;
(void)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;
}
(void)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 = WINPR_C_ARRAY_INIT;
memcpy(header.sig, sig_str, MIN(sizeof(header.sig), sizeof(sig_str)));
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)
{
WINPR_ASSERT(version <= INT32_MAX);
persistent->version = (int)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)
{
(void)fclose(persistent->fp);
persistent->fp = nullptr;
}
return 1;
}
rdpPersistentCache* persistent_cache_new(void)
{
rdpPersistentCache* persistent = calloc(1, sizeof(rdpPersistentCache));
if (!persistent)
return nullptr;
persistent->bmpSize = 0x4000;
persistent->bmpData = calloc(1, persistent->bmpSize);
if (!persistent->bmpData)
{
free(persistent);
return nullptr;
}
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);
}

View File

@@ -0,0 +1,597 @@
/**
* 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 = nullptr;
pointer->lengthXorMask = 0;
free(pointer->xorMaskData);
pointer->xorMaskData = nullptr;
}
}
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 = nullptr;
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)
{
if (lengthAndMask > UINT32_MAX)
return FALSE;
pointer->lengthAndMask = (UINT32)lengthAndMask;
pointer->andMaskData = (BYTE*)malloc(lengthAndMask);
if (!pointer->andMaskData)
return FALSE;
CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask);
}
if (lengthXorMask && xorMaskData)
{
if (lengthXorMask > UINT32_MAX)
return FALSE;
pointer->lengthXorMask = (UINT32)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 = nullptr;
rdpCache* cache = nullptr;
WINPR_ASSERT(context);
WINPR_ASSERT(pointer_color);
cache = context->cache;
WINPR_ASSERT(cache);
pointer = Pointer_Alloc(context);
if (pointer == nullptr)
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 = nullptr;
rdpCache* cache = nullptr;
WINPR_ASSERT(context);
WINPR_ASSERT(pointer_large);
cache = context->cache;
WINPR_ASSERT(cache);
pointer = Pointer_Alloc(context);
if (pointer == nullptr)
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 = nullptr;
rdpCache* cache = nullptr;
WINPR_ASSERT(context);
WINPR_ASSERT(pointer_cached);
cache = context->cache;
WINPR_ASSERT(cache);
pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex);
if (pointer != nullptr)
return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
return FALSE;
}
rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index)
{
rdpPointer* pointer = nullptr;
WINPR_ASSERT(pointer_cache);
if (index >= pointer_cache->cacheSize)
{
WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index,
pointer_cache->cacheSize);
return nullptr;
}
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 = nullptr;
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 = nullptr;
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 = nullptr;
rdpSettings* settings = nullptr;
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
if (!pointer_cache)
return nullptr;
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 nullptr;
}
return pointer_cache;
}
void pointer_cache_free(rdpPointerCache* pointer_cache)
{
if (pointer_cache != nullptr)
{
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((void*)pointer_cache->entries);
free(pointer_cache);
}
}
POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
const POINTER_COLOR_UPDATE* pointer)
{
POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
if (!dst || !pointer)
goto fail;
*dst = *pointer;
if (pointer->lengthAndMask > 0)
{
dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
if (!dst->andMaskData)
goto fail;
memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
}
if (pointer->lengthXorMask > 0)
{
dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
if (!dst->xorMaskData)
goto fail;
memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
}
return dst;
fail:
free_pointer_color_update(context, dst);
return nullptr;
}
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* pointer)
{
POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
if (!dst || !pointer)
goto fail;
*dst = *pointer;
if (pointer->lengthAndMask > 0)
{
dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
if (!dst->andMaskData)
goto fail;
memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
}
if (pointer->lengthXorMask > 0)
{
dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
if (!dst->xorMaskData)
goto fail;
memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
}
return dst;
fail:
free_pointer_large_update(context, dst);
return nullptr;
}
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* pointer)
{
POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
if (!dst || !pointer)
goto fail;
*dst = *pointer;
if (pointer->colorPtrAttr.lengthAndMask > 0)
{
dst->colorPtrAttr.andMaskData = calloc(pointer->colorPtrAttr.lengthAndMask, sizeof(BYTE));
if (!dst->colorPtrAttr.andMaskData)
goto fail;
memcpy(dst->colorPtrAttr.andMaskData, pointer->colorPtrAttr.andMaskData,
pointer->colorPtrAttr.lengthAndMask);
}
if (pointer->colorPtrAttr.lengthXorMask > 0)
{
dst->colorPtrAttr.xorMaskData = calloc(pointer->colorPtrAttr.lengthXorMask, sizeof(BYTE));
if (!dst->colorPtrAttr.xorMaskData)
goto fail;
memcpy(dst->colorPtrAttr.xorMaskData, pointer->colorPtrAttr.xorMaskData,
pointer->colorPtrAttr.lengthXorMask);
}
return dst;
fail:
free_pointer_new_update(context, dst);
return nullptr;
}
void free_pointer_new_update(WINPR_ATTR_UNUSED 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 nullptr;
}
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 nullptr;
}
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 nullptr;
}

View File

@@ -0,0 +1,102 @@
/**
* 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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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)
WINPR_ATTR_NODISCARD
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 */