diff options
Diffstat (limited to 'libfreerdp/cache/pointer.c')
-rw-r--r-- | libfreerdp/cache/pointer.c | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/libfreerdp/cache/pointer.c b/libfreerdp/cache/pointer.c new file mode 100644 index 0000000..cf9fb4c --- /dev/null +++ b/libfreerdp/cache/pointer.c @@ -0,0 +1,593 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Glyph Cache + * + * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <stdio.h> + +#include <winpr/crt.h> +#include <winpr/assert.h> +#include <winpr/stream.h> + +#include <freerdp/log.h> + +#include "pointer.h" +#include "cache.h" + +#define TAG FREERDP_TAG("cache.pointer") + +static BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer, + BOOL colorCache); +static rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index); + +static void pointer_clear(rdpPointer* pointer) +{ + if (pointer) + { + pointer->lengthAndMask = 0; + free(pointer->andMaskData); + pointer->andMaskData = NULL; + + pointer->lengthXorMask = 0; + free(pointer->xorMaskData); + pointer->xorMaskData = NULL; + } +} + +static void pointer_free(rdpContext* context, rdpPointer* pointer) +{ + if (pointer) + { + IFCALL(pointer->Free, context, pointer); + pointer_clear(pointer); + } + free(pointer); +} + +static BOOL update_pointer_position(rdpContext* context, + const POINTER_POSITION_UPDATE* pointer_position) +{ + if (!context || !context->graphics || !context->graphics->Pointer_Prototype || + !pointer_position) + return FALSE; + + const BOOL GrabMouse = freerdp_settings_get_bool(context->settings, FreeRDP_GrabMouse); + if (!GrabMouse) + return TRUE; + + const rdpPointer* pointer = context->graphics->Pointer_Prototype; + WINPR_ASSERT(pointer); + + return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos, + pointer_position->yPos); +} + +static BOOL update_pointer_system(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system) +{ + rdpPointer* pointer = NULL; + + if (!context || !context->graphics || !context->graphics->Pointer_Prototype || !pointer_system) + return FALSE; + + pointer = context->graphics->Pointer_Prototype; + + switch (pointer_system->type) + { + case SYSPTR_NULL: + return IFCALLRESULT(TRUE, pointer->SetNull, context); + + case SYSPTR_DEFAULT: + return IFCALLRESULT(TRUE, pointer->SetDefault, context); + + default: + WLog_ERR(TAG, "Unknown system pointer type (0x%08" PRIX32 ")", pointer_system->type); + } + return TRUE; +} + +static BOOL upate_pointer_copy_andxor(rdpPointer* pointer, const BYTE* andMaskData, + size_t lengthAndMask, const BYTE* xorMaskData, + size_t lengthXorMask) +{ + WINPR_ASSERT(pointer); + + pointer_clear(pointer); + if (lengthAndMask && andMaskData) + { + pointer->lengthAndMask = lengthAndMask; + pointer->andMaskData = (BYTE*)malloc(lengthAndMask); + if (!pointer->andMaskData) + return FALSE; + + CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask); + } + + if (lengthXorMask && xorMaskData) + { + pointer->lengthXorMask = lengthXorMask; + pointer->xorMaskData = (BYTE*)malloc(lengthXorMask); + if (!pointer->xorMaskData) + return FALSE; + + CopyMemory(pointer->xorMaskData, xorMaskData, lengthXorMask); + } + + return TRUE; +} + +static BOOL update_pointer_color(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color) +{ + rdpPointer* pointer = NULL; + rdpCache* cache = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(pointer_color); + + cache = context->cache; + WINPR_ASSERT(cache); + + pointer = Pointer_Alloc(context); + + if (pointer == NULL) + return FALSE; + pointer->xorBpp = 24; + pointer->xPos = pointer_color->hotSpotX; + pointer->yPos = pointer_color->hotSpotY; + pointer->width = pointer_color->width; + pointer->height = pointer_color->height; + + if (!upate_pointer_copy_andxor(pointer, pointer_color->andMaskData, + pointer_color->lengthAndMask, pointer_color->xorMaskData, + pointer_color->lengthXorMask)) + goto out_fail; + + if (!IFCALLRESULT(TRUE, pointer->New, context, pointer)) + goto out_fail; + + if (!pointer_cache_put(cache->pointer, pointer_color->cacheIndex, pointer, TRUE)) + goto out_fail; + + return IFCALLRESULT(TRUE, pointer->Set, context, pointer); + +out_fail: + pointer_free(context, pointer); + return FALSE; +} + +static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large) +{ + rdpPointer* pointer = NULL; + rdpCache* cache = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(pointer_large); + + cache = context->cache; + WINPR_ASSERT(cache); + + pointer = Pointer_Alloc(context); + if (pointer == NULL) + return FALSE; + pointer->xorBpp = pointer_large->xorBpp; + pointer->xPos = pointer_large->hotSpotX; + pointer->yPos = pointer_large->hotSpotY; + pointer->width = pointer_large->width; + pointer->height = pointer_large->height; + + if (!upate_pointer_copy_andxor(pointer, pointer_large->andMaskData, + pointer_large->lengthAndMask, pointer_large->xorMaskData, + pointer_large->lengthXorMask)) + goto out_fail; + + if (!IFCALLRESULT(TRUE, pointer->New, context, pointer)) + goto out_fail; + + if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer, FALSE)) + goto out_fail; + + return IFCALLRESULT(TRUE, pointer->Set, context, pointer); + +out_fail: + pointer_free(context, pointer); + return FALSE; +} + +static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new) +{ + if (!context || !pointer_new) + return FALSE; + + rdpCache* cache = context->cache; + rdpPointer* pointer = Pointer_Alloc(context); + + if (!pointer) + return FALSE; + + pointer->xorBpp = pointer_new->xorBpp; + pointer->xPos = pointer_new->colorPtrAttr.hotSpotX; + pointer->yPos = pointer_new->colorPtrAttr.hotSpotY; + pointer->width = pointer_new->colorPtrAttr.width; + pointer->height = pointer_new->colorPtrAttr.height; + if (!upate_pointer_copy_andxor( + pointer, pointer_new->colorPtrAttr.andMaskData, pointer_new->colorPtrAttr.lengthAndMask, + pointer_new->colorPtrAttr.xorMaskData, pointer_new->colorPtrAttr.lengthXorMask)) + goto out_fail; + + if (!IFCALLRESULT(TRUE, pointer->New, context, pointer)) + goto out_fail; + + if (!pointer_cache_put(cache->pointer, pointer_new->colorPtrAttr.cacheIndex, pointer, FALSE)) + goto out_fail; + + return IFCALLRESULT(TRUE, pointer->Set, context, pointer); + +out_fail: + pointer_free(context, pointer); + return FALSE; +} + +static BOOL update_pointer_cached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached) +{ + rdpPointer* pointer = NULL; + rdpCache* cache = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(pointer_cached); + + cache = context->cache; + WINPR_ASSERT(cache); + + pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex); + + if (pointer != NULL) + return IFCALLRESULT(TRUE, pointer->Set, context, pointer); + + return FALSE; +} + +rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index) +{ + rdpPointer* pointer = NULL; + + WINPR_ASSERT(pointer_cache); + + if (index >= pointer_cache->cacheSize) + { + WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index, + pointer_cache->cacheSize); + return NULL; + } + + WINPR_ASSERT(pointer_cache->entries); + pointer = pointer_cache->entries[index]; + return pointer; +} + +BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer, + BOOL colorCache) +{ + rdpPointer* prevPointer = NULL; + const FreeRDP_Settings_Keys_UInt32 id = + colorCache ? FreeRDP_ColorPointerCacheSize : FreeRDP_PointerCacheSize; + + WINPR_ASSERT(pointer_cache); + WINPR_ASSERT(pointer_cache->context); + + const UINT32 size = freerdp_settings_get_uint32(pointer_cache->context->settings, id); + if (index >= pointer_cache->cacheSize) + { + WLog_ERR(TAG, + "invalid pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32 "]", + index, pointer_cache->cacheSize, + colorCache ? "color-pointer-cache" : "pointer-cache", size); + return FALSE; + } + if (index >= size) + { + WLog_WARN(TAG, + "suspicious pointer index:%" PRIu32 " [allocated %" PRIu32 ", %s size %" PRIu32 + "]", + index, pointer_cache->cacheSize, + colorCache ? "color-pointer-cache" : "pointer-cache", size); + } + + WINPR_ASSERT(pointer_cache->entries); + prevPointer = pointer_cache->entries[index]; + pointer_free(pointer_cache->context, prevPointer); + pointer_cache->entries[index] = pointer; + return TRUE; +} + +void pointer_cache_register_callbacks(rdpUpdate* update) +{ + rdpPointerUpdate* pointer = NULL; + + WINPR_ASSERT(update); + WINPR_ASSERT(update->context); + + pointer = update->pointer; + WINPR_ASSERT(pointer); + + if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding)) + { + pointer->PointerPosition = update_pointer_position; + pointer->PointerSystem = update_pointer_system; + pointer->PointerColor = update_pointer_color; + pointer->PointerLarge = update_pointer_large; + pointer->PointerNew = update_pointer_new; + pointer->PointerCached = update_pointer_cached; + } +} + +rdpPointerCache* pointer_cache_new(rdpContext* context) +{ + rdpPointerCache* pointer_cache = NULL; + rdpSettings* settings = NULL; + + WINPR_ASSERT(context); + + settings = context->settings; + WINPR_ASSERT(settings); + + pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache)); + + if (!pointer_cache) + return NULL; + + pointer_cache->context = context; + + /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size + * matches */ + const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize); + const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize); + pointer_cache->cacheSize = MAX(size, colorSize) + 1; + + pointer_cache->entries = (rdpPointer**)calloc(pointer_cache->cacheSize, sizeof(rdpPointer*)); + + if (!pointer_cache->entries) + { + free(pointer_cache); + return NULL; + } + + return pointer_cache; +} + +void pointer_cache_free(rdpPointerCache* pointer_cache) +{ + if (pointer_cache != NULL) + { + if (pointer_cache->entries) + { + for (UINT32 i = 0; i < pointer_cache->cacheSize; i++) + { + rdpPointer* pointer = pointer_cache->entries[i]; + pointer_free(pointer_cache->context, pointer); + } + } + + free(pointer_cache->entries); + free(pointer_cache); + } +} + +POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context, + const POINTER_COLOR_UPDATE* src) +{ + POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE)); + + if (!dst || !src) + goto fail; + + *dst = *src; + + if (src->lengthAndMask > 0) + { + dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE)); + + if (!dst->andMaskData) + goto fail; + + memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask); + } + + if (src->lengthXorMask > 0) + { + dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE)); + + if (!dst->xorMaskData) + goto fail; + + memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask); + } + + return dst; +fail: + free_pointer_color_update(context, dst); + return NULL; +} + +void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer) +{ + WINPR_UNUSED(context); + + if (!pointer) + return; + + free(pointer->xorMaskData); + free(pointer->andMaskData); + free(pointer); +} + +POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context, + const POINTER_LARGE_UPDATE* src) +{ + POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE)); + + if (!dst || !src) + goto fail; + + *dst = *src; + + if (src->lengthAndMask > 0) + { + dst->andMaskData = calloc(src->lengthAndMask, sizeof(BYTE)); + + if (!dst->andMaskData) + goto fail; + + memcpy(dst->andMaskData, src->andMaskData, src->lengthAndMask); + } + + if (src->lengthXorMask > 0) + { + dst->xorMaskData = calloc(src->lengthXorMask, sizeof(BYTE)); + + if (!dst->xorMaskData) + goto fail; + + memcpy(dst->xorMaskData, src->xorMaskData, src->lengthXorMask); + } + + return dst; +fail: + free_pointer_large_update(context, dst); + return NULL; +} + +void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer) +{ + WINPR_UNUSED(context); + if (!pointer) + return; + + free(pointer->xorMaskData); + free(pointer->andMaskData); + free(pointer); +} + +POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* src) +{ + POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE)); + + if (!dst || !src) + goto fail; + + *dst = *src; + + if (src->colorPtrAttr.lengthAndMask > 0) + { + dst->colorPtrAttr.andMaskData = calloc(src->colorPtrAttr.lengthAndMask, sizeof(BYTE)); + + if (!dst->colorPtrAttr.andMaskData) + goto fail; + + memcpy(dst->colorPtrAttr.andMaskData, src->colorPtrAttr.andMaskData, + src->colorPtrAttr.lengthAndMask); + } + + if (src->colorPtrAttr.lengthXorMask > 0) + { + dst->colorPtrAttr.xorMaskData = calloc(src->colorPtrAttr.lengthXorMask, sizeof(BYTE)); + + if (!dst->colorPtrAttr.xorMaskData) + goto fail; + + memcpy(dst->colorPtrAttr.xorMaskData, src->colorPtrAttr.xorMaskData, + src->colorPtrAttr.lengthXorMask); + } + + return dst; +fail: + free_pointer_new_update(context, dst); + return NULL; +} + +void free_pointer_new_update(rdpContext* context, POINTER_NEW_UPDATE* pointer) +{ + if (!pointer) + return; + + free(pointer->colorPtrAttr.xorMaskData); + free(pointer->colorPtrAttr.andMaskData); + free(pointer); +} + +POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context, + const POINTER_CACHED_UPDATE* pointer) +{ + POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE)); + + if (!dst) + goto fail; + + *dst = *pointer; + return dst; +fail: + free_pointer_cached_update(context, dst); + return NULL; +} + +void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer) +{ + WINPR_UNUSED(context); + free(pointer); +} + +void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer) +{ + WINPR_UNUSED(context); + free(pointer); +} + +POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context, + const POINTER_POSITION_UPDATE* pointer) +{ + POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE)); + + if (!dst || !pointer) + goto fail; + + *dst = *pointer; + return dst; +fail: + free_pointer_position_update(context, dst); + return NULL; +} + +void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer) +{ + WINPR_UNUSED(context); + free(pointer); +} + +POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context, + const POINTER_SYSTEM_UPDATE* pointer) +{ + POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE)); + + if (!dst || !pointer) + goto fail; + + *dst = *pointer; + return dst; +fail: + free_pointer_system_update(context, dst); + return NULL; +} |