* 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>
#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>
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;
}
#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
#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
};
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;
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
{
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;
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;
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;
}
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));
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);
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;
}
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;
/* 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
}
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;
}
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
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;
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;
pPointerColor PointerColor;
pPointerNew PointerNew;
pPointerCached PointerCached;
+ pPointerLarge PointerLarge;
HANDLE thread;
};
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;
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));
}
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);
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;
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;
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);
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;
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;
}