Added RDP 10.7 large pointer support

* Implements [MS-RDPBCGR] version 51 large pointer support.
* Logs unknown large pointer capability flags as warning.

Signed-off-by: Armin Novak <armin.novak@thincast.com>
This commit is contained in:
Armin Novak
2019-12-19 09:35:53 +01:00
parent 57dc6f2239
commit 182d0ce548
15 changed files with 360 additions and 7 deletions

View File

@@ -36,7 +36,6 @@
#include <freerdp/client/channels.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/utils/passphrase.h>
#include <freerdp/client/cmdline.h>
@@ -3136,7 +3135,8 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
if (settings->RemoteFxCodec || settings->NSCodec || settings->SupportGraphicsPipeline)
{
settings->FastPathOutput = TRUE;
settings->LargePointerFlag = TRUE;
settings->LargePointerFlag =
0x0002; /* (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384); */
settings->FrameMarkerCommandEnabled = TRUE;
settings->ColorDepth = 32;
}

View File

@@ -212,12 +212,14 @@
#define PointerUpdate_PointerColor 3
#define PointerUpdate_PointerNew 4
#define PointerUpdate_PointerCached 5
#define PointerUpdate_PointerLarge 6
#define FREERDP_POINTER_UPDATE_ POINTER_POSITION MakeMessageId(PointerUpdate, PointerPosition)
#define FREERDP_POINTER_UPDATE_POINTER_SYSTEM MakeMessageId(PointerUpdate, PointerSystem)
#define FREERDP_POINTER_UPDATE_POINTER_COLOR MakeMessageId(PointerUpdate, PointerColor)
#define FREERDP_POINTER_UPDATE_POINTER_NEW MakeMessageId(PointerUpdate, PointerNew)
#define FREERDP_POINTER_UPDATE_POINTER_CACHED MakeMessageId(PointerUpdate, PointerCached)
#define FREERDP_POINTER_UPDATE_POINTER_LARGE MakeMessageId(PointerUpdate, PointerLarge)
/**
* Input Message Queue

View File

@@ -27,6 +27,7 @@
#define PTR_MSG_TYPE_COLOR 0x0006
#define PTR_MSG_TYPE_CACHED 0x0007
#define PTR_MSG_TYPE_POINTER 0x0008
#define PTR_MSG_TYPE_POINTER_LARGE 0x0009
#define SYSPTR_NULL 0x00000000
#define SYSPTR_DEFAULT 0x00007F00
@@ -58,6 +59,21 @@ struct _POINTER_COLOR_UPDATE
};
typedef struct _POINTER_COLOR_UPDATE POINTER_COLOR_UPDATE;
struct _POINTER_LARGE_UPDATE
{
UINT16 xorBpp;
UINT16 cacheIndex;
UINT16 hotSpotX;
UINT16 hotSpotY;
UINT16 width;
UINT16 height;
UINT32 lengthAndMask;
UINT32 lengthXorMask;
BYTE* xorMaskData;
BYTE* andMaskData;
};
typedef struct _POINTER_LARGE_UPDATE POINTER_LARGE_UPDATE;
struct _POINTER_NEW_UPDATE
{
UINT32 xorBpp;
@@ -77,6 +93,7 @@ typedef BOOL (*pPointerSystem)(rdpContext* context, const POINTER_SYSTEM_UPDATE*
typedef BOOL (*pPointerColor)(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color);
typedef BOOL (*pPointerNew)(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new);
typedef BOOL (*pPointerCached)(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached);
typedef BOOL (*pPointerLarge)(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large);
struct rdp_pointer_update
{
@@ -88,7 +105,8 @@ struct rdp_pointer_update
pPointerColor PointerColor; /* 18 */
pPointerNew PointerNew; /* 19 */
pPointerCached PointerCached; /* 20 */
UINT32 paddingB[32 - 21]; /* 21 */
pPointerLarge PointerLarge; /* 21 */
UINT32 paddingB[32 - 22]; /* 22 */
};
typedef struct rdp_pointer_update rdpPointerUpdate;

View File

@@ -147,6 +147,57 @@ out_fail:
return FALSE;
}
static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large)
{
rdpPointer* pointer;
rdpCache* cache = context->cache;
pointer = Pointer_Alloc(context);
if (pointer != NULL)
{
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;
pointer->lengthAndMask = pointer_large->lengthAndMask;
pointer->lengthXorMask = pointer_large->lengthXorMask;
if (pointer->lengthAndMask && pointer_large->andMaskData)
{
pointer->andMaskData = (BYTE*)malloc(pointer->lengthAndMask);
if (!pointer->andMaskData)
goto out_fail;
CopyMemory(pointer->andMaskData, pointer_large->andMaskData, pointer->lengthAndMask);
}
if (pointer->lengthXorMask && pointer_large->xorMaskData)
{
pointer->xorMaskData = (BYTE*)malloc(pointer->lengthXorMask);
if (!pointer->xorMaskData)
goto out_fail;
CopyMemory(pointer->xorMaskData, pointer_large->xorMaskData, pointer->lengthXorMask);
}
if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
goto out_fail;
if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer))
goto out_fail;
return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
}
return FALSE;
out_fail:
pointer_free(context, pointer);
return FALSE;
}
static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
{
rdpPointer* pointer;
@@ -251,6 +302,7 @@ void pointer_cache_register_callbacks(rdpUpdate* update)
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;
}
@@ -341,6 +393,53 @@ void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointe
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));

View File

@@ -28,6 +28,10 @@ FREERDP_LOCAL POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* contex
const POINTER_COLOR_UPDATE* pointer);
FREERDP_LOCAL void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer);
FREERDP_LOCAL POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
const POINTER_LARGE_UPDATE* pointer);
FREERDP_LOCAL void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer);
FREERDP_LOCAL POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context,
const POINTER_NEW_UPDATE* pointer);
FREERDP_LOCAL void free_pointer_new_update(rdpContext* context, POINTER_NEW_UPDATE* pointer);

View File

@@ -2479,7 +2479,16 @@ static BOOL rdp_read_large_pointer_capability_set(wStream* s, UINT16 length, rdp
return FALSE;
Stream_Read_UINT16(s, largePointerSupportFlags); /* largePointerSupportFlags (2 bytes) */
settings->LargePointerFlag = (largePointerSupportFlags & LARGE_POINTER_FLAG_96x96) ? 1 : 0;
settings->LargePointerFlag =
largePointerSupportFlags & (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384);
if ((largePointerSupportFlags & ~(LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384)) != 0)
{
WLog_WARN(
TAG,
"TS_LARGE_POINTER_CAPABILITYSET with unsupported flags %04X (all flags %04X) received",
largePointerSupportFlags & ~(LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384),
largePointerSupportFlags);
}
return TRUE;
}
@@ -2499,7 +2508,8 @@ static BOOL rdp_write_large_pointer_capability_set(wStream* s, const rdpSettings
return FALSE;
header = rdp_capability_set_start(s);
largePointerSupportFlags = (settings->LargePointerFlag) ? LARGE_POINTER_FLAG_96x96 : 0;
largePointerSupportFlags =
settings->LargePointerFlag & (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384);
Stream_Write_UINT16(s, largePointerSupportFlags); /* largePointerSupportFlags (2 bytes) */
rdp_capability_set_finish(s, header, CAPSET_TYPE_LARGE_POINTER);
return TRUE;

View File

@@ -146,6 +146,7 @@
/* Large Pointer Support Flags */
#define LARGE_POINTER_FLAG_96x96 0x00000001
#define LARGE_POINTER_FLAG_384x384 0x00000002
/* Surface Commands Flags */
#define SURFCMDS_SET_SURFACE_BITS 0x00000002

View File

@@ -470,6 +470,17 @@ static int fastpath_recv_update(rdpFastPath* fastpath, BYTE updateCode, wStream*
}
break;
case FASTPATH_UPDATETYPE_LARGE_POINTER:
{
POINTER_LARGE_UPDATE* pointer_large = update_read_pointer_large(update, s);
if (pointer_large)
{
rc = IFCALLRESULT(FALSE, pointer->PointerLarge, context, pointer_large);
free_pointer_large_update(context, pointer_large);
}
}
break;
default:
break;
}

View File

@@ -79,7 +79,8 @@ enum FASTPATH_UPDATETYPE
FASTPATH_UPDATETYPE_PTR_POSITION = 0x8,
FASTPATH_UPDATETYPE_COLOR = 0x9,
FASTPATH_UPDATETYPE_CACHED = 0xA,
FASTPATH_UPDATETYPE_POINTER = 0xB
FASTPATH_UPDATETYPE_POINTER = 0xB,
FASTPATH_UPDATETYPE_LARGE_POINTER = 0xC
};
enum FASTPATH_FRAGMENT

View File

@@ -1440,6 +1440,22 @@ static BOOL update_message_PointerColor(rdpContext* context,
MakeMessageId(PointerUpdate, PointerColor), (void*)wParam, NULL);
}
static BOOL update_message_PointerLarge(rdpContext* context, const POINTER_LARGE_UPDATE* pointer)
{
POINTER_LARGE_UPDATE* wParam;
if (!context || !context->update || !pointer)
return FALSE;
wParam = copy_pointer_large_update(context, pointer);
if (!wParam)
return FALSE;
return MessageQueue_Post(context->update->queue, (void*)context,
MakeMessageId(PointerUpdate, PointerLarge), (void*)wParam, NULL);
}
static BOOL update_message_PointerNew(rdpContext* context, const POINTER_NEW_UPDATE* pointerNew)
{
POINTER_NEW_UPDATE* wParam;
@@ -2640,11 +2656,13 @@ static BOOL update_message_register_interface(rdpUpdateProxy* message, rdpUpdate
message->PointerPosition = pointer->PointerPosition;
message->PointerSystem = pointer->PointerSystem;
message->PointerColor = pointer->PointerColor;
message->PointerLarge = pointer->PointerLarge;
message->PointerNew = pointer->PointerNew;
message->PointerCached = pointer->PointerCached;
pointer->PointerPosition = update_message_PointerPosition;
pointer->PointerSystem = update_message_PointerSystem;
pointer->PointerColor = update_message_PointerColor;
pointer->PointerLarge = update_message_PointerLarge;
pointer->PointerNew = update_message_PointerNew;
pointer->PointerCached = update_message_PointerCached;
return TRUE;

View File

@@ -123,6 +123,7 @@ struct rdp_update_proxy
pPointerColor PointerColor;
pPointerNew PointerNew;
pPointerCached PointerCached;
pPointerLarge PointerLarge;
HANDLE thread;
};

View File

@@ -421,7 +421,7 @@ rdpSettings* freerdp_settings_new(DWORD flags)
gethostname(settings->ClientHostname, 31);
settings->ClientHostname[31] = 0;
settings->ColorPointerFlag = TRUE;
settings->LargePointerFlag = TRUE;
settings->LargePointerFlag = (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384);
settings->PointerCacheSize = 20;
settings->SoundBeepsEnabled = TRUE;
settings->DrawGdiPlusEnabled = FALSE;

View File

@@ -489,6 +489,130 @@ fail:
return NULL;
}
static BOOL _update_read_pointer_large(wStream* s, POINTER_LARGE_UPDATE* pointer)
{
BYTE* newMask;
UINT32 scanlineSize;
if (!pointer)
goto fail;
if (Stream_GetRemainingLength(s) < 14)
goto fail;
Stream_Read_UINT16(s, pointer->xorBpp);
Stream_Read_UINT16(s, pointer->cacheIndex); /* cacheIndex (2 bytes) */
Stream_Read_UINT16(s, pointer->hotSpotX); /* xPos (2 bytes) */
Stream_Read_UINT16(s, pointer->hotSpotY); /* yPos (2 bytes) */
Stream_Read_UINT16(s, pointer->width); /* width (2 bytes) */
Stream_Read_UINT16(s, pointer->height); /* height (2 bytes) */
if ((pointer->width > 384) || (pointer->height > 384))
goto fail;
Stream_Read_UINT16(s, pointer->lengthAndMask); /* lengthAndMask (2 bytes) */
Stream_Read_UINT16(s, pointer->lengthXorMask); /* lengthXorMask (2 bytes) */
if (pointer->hotSpotX >= pointer->width)
pointer->hotSpotX = 0;
if (pointer->hotSpotY >= pointer->height)
pointer->hotSpotY = 0;
if (pointer->lengthXorMask > 0)
{
/**
* Spec states that:
*
* xorMaskData (variable): A variable-length array of bytes. Contains the 24-bpp, bottom-up
* XOR mask scan-line data. The XOR mask is padded to a 2-byte boundary for each encoded
* scan-line. For example, if a 3x3 pixel cursor is being sent, then each scan-line will
* consume 10 bytes (3 pixels per scan-line multiplied by 3 bytes per pixel, rounded up to
* the next even number of bytes).
*
* In fact instead of 24-bpp, the bpp parameter is given by the containing packet.
*/
if (Stream_GetRemainingLength(s) < pointer->lengthXorMask)
goto fail;
scanlineSize = (7 + pointer->xorBpp * pointer->width) / 8;
scanlineSize = ((scanlineSize + 1) / 2) * 2;
if (scanlineSize * pointer->height != pointer->lengthXorMask)
{
WLog_ERR(TAG,
"invalid lengthXorMask: width=%" PRIu32 " height=%" PRIu32 ", %" PRIu32
" instead of %" PRIu32 "",
pointer->width, pointer->height, pointer->lengthXorMask,
scanlineSize * pointer->height);
goto fail;
}
newMask = realloc(pointer->xorMaskData, pointer->lengthXorMask);
if (!newMask)
goto fail;
pointer->xorMaskData = newMask;
Stream_Read(s, pointer->xorMaskData, pointer->lengthXorMask);
}
if (pointer->lengthAndMask > 0)
{
/**
* andMaskData (variable): A variable-length array of bytes. Contains the 1-bpp, bottom-up
* AND mask scan-line data. The AND mask is padded to a 2-byte boundary for each encoded
* scan-line. For example, if a 7x7 pixel cursor is being sent, then each scan-line will
* consume 2 bytes (7 pixels per scan-line multiplied by 1 bpp, rounded up to the next even
* number of bytes).
*/
if (Stream_GetRemainingLength(s) < pointer->lengthAndMask)
goto fail;
scanlineSize = ((7 + pointer->width) / 8);
scanlineSize = ((1 + scanlineSize) / 2) * 2;
if (scanlineSize * pointer->height != pointer->lengthAndMask)
{
WLog_ERR(TAG, "invalid lengthAndMask: %" PRIu32 " instead of %" PRIu32 "",
pointer->lengthAndMask, scanlineSize * pointer->height);
goto fail;
}
newMask = realloc(pointer->andMaskData, pointer->lengthAndMask);
if (!newMask)
goto fail;
pointer->andMaskData = newMask;
Stream_Read(s, pointer->andMaskData, pointer->lengthAndMask);
}
if (Stream_GetRemainingLength(s) > 0)
Stream_Seek_UINT8(s); /* pad (1 byte) */
return TRUE;
fail:
return FALSE;
}
POINTER_LARGE_UPDATE* update_read_pointer_large(rdpUpdate* update, wStream* s)
{
POINTER_LARGE_UPDATE* pointer = calloc(1, sizeof(POINTER_LARGE_UPDATE));
if (!pointer)
goto fail;
if (!_update_read_pointer_large(s, pointer))
goto fail;
return pointer;
fail:
free_pointer_large_update(update->context, pointer);
return NULL;
}
POINTER_NEW_UPDATE* update_read_pointer_new(rdpUpdate* update, wStream* s)
{
POINTER_NEW_UPDATE* pointer_new = calloc(1, sizeof(POINTER_NEW_UPDATE));
@@ -585,6 +709,18 @@ BOOL update_recv_pointer(rdpUpdate* update, wStream* s)
}
break;
case PTR_MSG_TYPE_POINTER_LARGE:
{
POINTER_LARGE_UPDATE* pointer_large = update_read_pointer_large(update, s);
if (pointer_large)
{
rc = IFCALLRESULT(FALSE, pointer->PointerLarge, context, pointer_large);
free_pointer_large_update(context, pointer_large);
}
}
break;
case PTR_MSG_TYPE_POINTER:
{
POINTER_NEW_UPDATE* pointer_new = update_read_pointer_new(update, s);
@@ -1888,6 +2024,44 @@ out_fail:
return ret;
}
static BOOL update_write_pointer_large(wStream* s, const POINTER_LARGE_UPDATE* pointer)
{
if (!Stream_EnsureRemainingCapacity(s, 32 + pointer->lengthAndMask + pointer->lengthXorMask))
return FALSE;
Stream_Write_UINT16(s, pointer->xorBpp);
Stream_Write_UINT16(s, pointer->cacheIndex);
Stream_Write_UINT16(s, pointer->hotSpotX);
Stream_Write_UINT16(s, pointer->hotSpotY);
Stream_Write_UINT16(s, pointer->width);
Stream_Write_UINT16(s, pointer->height);
Stream_Write_UINT32(s, pointer->lengthAndMask);
Stream_Write_UINT32(s, pointer->lengthXorMask);
Stream_Write(s, pointer->xorMaskData, pointer->lengthXorMask);
Stream_Write(s, pointer->andMaskData, pointer->lengthAndMask);
Stream_Write_UINT8(s, 0); /* pad (1 byte) */
return TRUE;
}
static BOOL update_send_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer)
{
wStream* s;
rdpRdp* rdp = context->rdp;
BOOL ret = FALSE;
s = fastpath_update_pdu_init(rdp->fastpath);
if (!s)
return FALSE;
if (!update_write_pointer_large(s, pointer))
goto out_fail;
ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_LARGE_POINTER, s, FALSE);
out_fail:
Stream_Release(s);
return ret;
}
static BOOL update_send_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
{
wStream* s;
@@ -2635,6 +2809,7 @@ void update_register_server_callbacks(rdpUpdate* update)
update->pointer->PointerSystem = update_send_pointer_system;
update->pointer->PointerPosition = update_send_pointer_position;
update->pointer->PointerColor = update_send_pointer_color;
update->pointer->PointerLarge = update_send_pointer_large;
update->pointer->PointerNew = update_send_pointer_new;
update->pointer->PointerCached = update_send_pointer_cached;
update->window->WindowCreate = update_send_window_create;

View File

@@ -55,6 +55,8 @@ FREERDP_LOCAL POINTER_SYSTEM_UPDATE* update_read_pointer_system(rdpUpdate* updat
FREERDP_LOCAL POINTER_POSITION_UPDATE* update_read_pointer_position(rdpUpdate* update, wStream* s);
FREERDP_LOCAL POINTER_COLOR_UPDATE* update_read_pointer_color(rdpUpdate* update, wStream* s,
BYTE xorBpp);
FREERDP_LOCAL POINTER_LARGE_UPDATE* update_read_pointer_large(rdpUpdate* update, wStream* s);
FREERDP_LOCAL POINTER_NEW_UPDATE* update_read_pointer_new(rdpUpdate* update, wStream* s);
FREERDP_LOCAL POINTER_CACHED_UPDATE* update_read_pointer_cached(rdpUpdate* update, wStream* s);

View File

@@ -152,6 +152,16 @@ static BOOL pf_client_send_pointer_color(rdpContext* context,
return ps->update->pointer->PointerColor(ps, pointer_color);
}
static BOOL pf_client_send_pointer_large(rdpContext* context,
const POINTER_LARGE_UPDATE* pointer_large)
{
pClientContext* pc = (pClientContext*)context;
proxyData* pdata = pc->pdata;
rdpContext* ps = (rdpContext*)pdata->ps;
WLog_DBG(TAG, __FUNCTION__);
return ps->update->pointer->PointerLarge(ps, pointer_large);
}
static BOOL pf_client_send_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
{
pClientContext* pc = (pClientContext*)context;
@@ -324,6 +334,7 @@ void pf_client_register_update_callbacks(rdpUpdate* update)
update->pointer->PointerSystem = pf_client_send_pointer_system;
update->pointer->PointerPosition = pf_client_send_pointer_position;
update->pointer->PointerColor = pf_client_send_pointer_color;
update->pointer->PointerLarge = pf_client_send_pointer_large;
update->pointer->PointerNew = pf_client_send_pointer_new;
update->pointer->PointerCached = pf_client_send_pointer_cached;
}