xfreerdp: initial multitouch functionality

This commit is contained in:
Marc-André Moreau
2013-05-14 17:45:52 -04:00
parent c23da2f80b
commit e6aec6c936
5 changed files with 210 additions and 41 deletions

View File

@@ -36,6 +36,26 @@
#include "rdpei_main.h"
/**
* Touch Input
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd562197/
*
* Windows Touch Input
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd317321/
*
* Input: Touch injection sample
* http://code.msdn.microsoft.com/windowsdesktop/Touch-Injection-Sample-444d9bf7
*
* Pointer Input Message Reference
* http://msdn.microsoft.com/en-us/library/hh454916/
*
* POINTER_INFO Structure
* http://msdn.microsoft.com/en-us/library/hh454907/
*
* POINTER_TOUCH_INFO Structure
* http://msdn.microsoft.com/en-us/library/hh454910/
*/
#define MAX_CONTACTS 512
struct _RDPEI_CHANNEL_CALLBACK
@@ -66,11 +86,12 @@ struct _RDPEI_PLUGIN
RDPEI_LISTENER_CALLBACK* listener_callback;
int version;
int touchIdOffset;
UINT16 maxTouchContacts;
UINT64 currentFrameTime;
UINT64 previousFrameTime;
RDPINPUT_TOUCH_FRAME frame;
RDPINPUT_CONTACT_DATA contacts[MAX_CONTACTS];
RDPINPUT_CONTACT_POINT* contactPoints;
};
typedef struct _RDPEI_PLUGIN RDPEI_PLUGIN;
@@ -108,21 +129,19 @@ int rdpei_send_cs_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback)
wStream* s;
UINT32 flags;
UINT32 pduLength;
UINT16 maxTouchContacts;
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) callback->plugin;
flags = 0;
flags |= READY_FLAGS_SHOW_TOUCH_VISUALS;
//flags |= READY_FLAGS_DISABLE_TIMESTAMP_INJECTION;
maxTouchContacts = 10;
pduLength = RDPINPUT_HEADER_LENGTH + 10;
s = Stream_New(NULL, pduLength);
Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
Stream_Write_UINT32(s, flags); /* flags (4 bytes) */
Stream_Write_UINT32(s, RDPINPUT_PROTOCOL_V1); /* protocolVersion (4 bytes) */
Stream_Write_UINT16(s, maxTouchContacts); /* maxTouchContacts (2 bytes) */
Stream_Write_UINT16(s, rdpei->maxTouchContacts); /* maxTouchContacts (2 bytes) */
Stream_SealLength(s);
@@ -132,6 +151,22 @@ int rdpei_send_cs_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback)
return status;
}
void rdpei_print_contact_flags(UINT32 contactFlags)
{
if (contactFlags & CONTACT_FLAG_DOWN)
printf(" CONTACT_FLAG_DOWN");
if (contactFlags & CONTACT_FLAG_UPDATE)
printf(" CONTACT_FLAG_UPDATE");
if (contactFlags & CONTACT_FLAG_UP)
printf(" CONTACT_FLAG_UP");
if (contactFlags & CONTACT_FLAG_INRANGE)
printf(" CONTACT_FLAG_INRANGE");
if (contactFlags & CONTACT_FLAG_INCONTACT)
printf(" CONTACT_FLAG_INCONTACT");
if (contactFlags & CONTACT_FLAG_CANCELED)
printf(" CONTACT_FLAG_CANCELED");
}
int rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
{
int index;
@@ -153,7 +188,9 @@ int rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
printf("contact[%d].fieldsPresent: %d\n", index, contact->fieldsPresent);
printf("contact[%d].x: %d\n", index, contact->x);
printf("contact[%d].y: %d\n", index, contact->y);
printf("contact[%d].contactFlags: 0x%04X\n", index, contact->contactFlags);
printf("contact[%d].contactFlags: 0x%04X", index, contact->contactFlags);
rdpei_print_contact_flags(contact->contactFlags);
printf("\n");
Stream_Write_UINT8(s, contact->contactId); /* contactId (1 byte) */
@@ -408,27 +445,10 @@ int rdpei_end_frame(RdpeiClientContext* context)
int rdpei_add_contact(RdpeiClientContext* context, RDPINPUT_CONTACT_DATA* contact)
{
RDPINPUT_CONTACT_DATA* previousContact;
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) context->handle;
if (rdpei->frame.contactCount < MAX_CONTACTS)
{
if (rdpei->frame.contactCount > 0)
{
previousContact = &(rdpei->contacts[rdpei->frame.contactCount - 1]);
if ((previousContact->x == contact->x) || (previousContact->y == contact->y) ||
(previousContact->contactId == contact->contactId))
{
return 1;
}
}
if (rdpei->touchIdOffset < 0)
rdpei->touchIdOffset = contact->contactId;
contact->contactId -= rdpei->touchIdOffset;
CopyMemory(&(rdpei->contacts[rdpei->frame.contactCount]), contact, sizeof(RDPINPUT_CONTACT_DATA));
rdpei->frame.contactCount++;
}
@@ -436,6 +456,104 @@ int rdpei_add_contact(RdpeiClientContext* context, RDPINPUT_CONTACT_DATA* contac
return 1;
}
int rdpei_contact_begin(RdpeiClientContext* context, int externalId, int x, int y)
{
int i, j;
int contactId = -1;
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) context->handle;
/* Create a new contact point in an empty slot */
for (i = 0; i < rdpei->maxTouchContacts; i++)
{
if (!rdpei->contactPoints[i].flags)
{
rdpei->contactPoints[i].flags = 1;
rdpei->contactPoints[i].contactId = i;
rdpei->contactPoints[i].touchDownX = x;
rdpei->contactPoints[i].touchDownX = y;
for (j = 0; j < MAX_EXTERNAL_IDS; j++)
{
if (!rdpei->contactPoints[i].externalIds[j])
{
rdpei->contactPoints[i].externalIds[j] = externalId;
rdpei->contactPoints[i].externalIdCount++;
break;
}
}
contactId = rdpei->contactPoints[i].contactId;
break;
}
}
return contactId;
}
int rdpei_contact_update(RdpeiClientContext* context, int externalId)
{
int i, j;
int contactId = -1;
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) context->handle;
for (i = 0; i < rdpei->maxTouchContacts; i++)
{
if (!rdpei->contactPoints[i].flags)
continue;
for (j = 0; j < MAX_EXTERNAL_IDS; j++)
{
if (rdpei->contactPoints[i].externalIds[j] == externalId)
{
contactId = rdpei->contactPoints[i].contactId;
break;
}
}
if (contactId != -1)
break;
}
return contactId;
}
int rdpei_contact_end(RdpeiClientContext* context, int externalId)
{
int i, j;
int contactId = -1;
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) context->handle;
for (i = 0; i < rdpei->maxTouchContacts; i++)
{
if (!rdpei->contactPoints[i].flags)
continue;
for (j = 0; j < MAX_EXTERNAL_IDS; j++)
{
if (rdpei->contactPoints[i].externalIds[j] == externalId)
{
contactId = rdpei->contactPoints[i].contactId;
rdpei->contactPoints[i].externalIds[j] = 0;
rdpei->contactPoints[i].externalIdCount--;
if (rdpei->contactPoints[i].externalIdCount < 1)
{
rdpei->contactPoints[i].flags = 0;
rdpei->contactPoints[i].contactId = 0;
}
break;
}
}
if (contactId != -1)
break;
}
return contactId;
}
#ifdef STATIC_CHANNELS
#define DVCPluginEntry rdpei_DVCPluginEntry
#endif
@@ -450,6 +568,8 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
if (rdpei == NULL)
{
size_t size;
rdpei = (RDPEI_PLUGIN*) malloc(sizeof(RDPEI_PLUGIN));
ZeroMemory(rdpei, sizeof(RDPEI_PLUGIN));
@@ -459,11 +579,15 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
rdpei->iface.Terminated = rdpei_plugin_terminated;
rdpei->version = 1;
rdpei->touchIdOffset = -1;
rdpei->currentFrameTime = 0;
rdpei->previousFrameTime = 0;
rdpei->frame.contacts = (RDPINPUT_CONTACT_DATA*) rdpei->contacts;
rdpei->maxTouchContacts = 10;
size = rdpei->maxTouchContacts * sizeof(RDPINPUT_CONTACT_POINT);
rdpei->contactPoints = (RDPINPUT_CONTACT_POINT*) malloc(size);
ZeroMemory(rdpei->contactPoints, size);
context = (RdpeiClientContext*) malloc(sizeof(RdpeiClientContext));
context->handle = (void*) rdpei;
@@ -472,6 +596,10 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
context->EndFrame = rdpei_end_frame;
context->AddContact = rdpei_add_contact;
context->ContactBegin = rdpei_contact_begin;
context->ContactUpdate = rdpei_contact_update;
context->ContactEnd = rdpei_contact_end;
rdpei->iface.pInterface = (void*) context;
error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpei", (IWTSPlugin*) rdpei);

View File

@@ -51,6 +51,19 @@
#define EVENTID_RESUME_TOUCH 0x0005
#define EVENTID_DISMISS_HOVERING_CONTACT 0x0006
#define MAX_EXTERNAL_IDS 32
struct _RDPINPUT_CONTACT_POINT
{
UINT32 flags;
UINT32 contactId;
int touchDownX;
int touchDownY;
int externalIdCount;
int externalIds[MAX_EXTERNAL_IDS];
};
typedef struct _RDPINPUT_CONTACT_POINT RDPINPUT_CONTACT_POINT;
#ifdef WITH_DEBUG_DVC
#define DEBUG_DVC(fmt, ...) DEBUG_CLASS(DVC, fmt, ## __VA_ARGS__)
#else

View File

@@ -136,6 +136,9 @@ static BOOL xf_event_MotionNotify(xfInfo* xfi, XEvent* event, BOOL app)
int x, y;
Window childWindow;
if (xfi->settings->MultiTouchInput)
return TRUE;
input = xfi->instance->input;
x = event->xmotion.x;
y = event->xmotion.y;
@@ -186,6 +189,9 @@ static BOOL xf_event_ButtonPress(xfInfo* xfi, XEvent* event, BOOL app)
BOOL extended;
rdpInput* input;
if (xfi->settings->MultiTouchInput)
return TRUE;
input = xfi->instance->input;
x = 0;
@@ -295,6 +301,9 @@ static BOOL xf_event_ButtonRelease(xfInfo* xfi, XEvent* event, BOOL app)
BOOL extended;
rdpInput* input;
if (xfi->settings->MultiTouchInput)
return TRUE;
input = xfi->instance->input;
x = 0;

View File

@@ -57,15 +57,17 @@ int scale_cnt;
int xf_input_init(xfInfo* xfi, Window window)
{
int i, j;
int nmasks;
int ndevices;
int major = 2;
int minor = 2;
Status xstatus;
XIEventMask evmask;
XIDeviceInfo* info;
XIEventMask evmasks[8];
int opcode, event, error;
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
BYTE masks[8][XIMaskLen(XI_LASTEVENT)];
nmasks = 0;
active_contacts = 0;
ZeroMemory(contacts, sizeof(touchContact) * MAX_CONTACTS);
@@ -99,21 +101,29 @@ int xf_input_init(xfInfo* xfi, Window window)
if (class->type != XITouchClass)
continue;
printf("%s %s touch device, supporting %d touches.\n",
dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent", t->num_touches);
if (t->mode != XIDirectTouch)
continue;
if (strcmp(dev->name, "Virtual core pointer") == 0)
continue;
printf("%s %s touch device (id: %d, mode: %d), supporting %d touches.\n",
dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent",
dev->deviceid, t->mode, t->num_touches);
evmasks[nmasks].mask = masks[nmasks];
evmasks[nmasks].mask_len = sizeof(masks[0]);
ZeroMemory(masks[nmasks], sizeof(masks[0]));
evmasks[nmasks].deviceid = dev->deviceid;
XISetMask(masks[nmasks], XI_TouchBegin);
XISetMask(masks[nmasks], XI_TouchUpdate);
XISetMask(masks[nmasks], XI_TouchEnd);
nmasks++;
}
}
evmask.mask = mask;
evmask.mask_len = sizeof(mask);
ZeroMemory(mask, sizeof(mask));
evmask.deviceid = XIAllDevices;
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
xstatus = XISelectEvents(xfi->display, window, &evmask, 1);
xstatus = XISelectEvents(xfi->display, window, evmasks, nmasks);
return -1;
}
@@ -331,7 +341,6 @@ int xf_input_touch_remote(xfInfo* xfi, XIDeviceEvent* event, DWORD flags)
//if ((x < 0) || (y < 0))
// return 0;
contact.contactId = touchId;
contact.fieldsPresent = 0;
contact.x = x;
contact.y = y;
@@ -339,20 +348,22 @@ int xf_input_touch_remote(xfInfo* xfi, XIDeviceEvent* event, DWORD flags)
if (flags & CONTACT_FLAG_DOWN)
{
contact.contactId = rdpei->ContactBegin(rdpei, touchId, x, y);
contact.contactFlags |= CONTACT_FLAG_INRANGE;
contact.contactFlags |= CONTACT_FLAG_INCONTACT;
}
else if (flags & CONTACT_FLAG_UPDATE)
{
contact.contactId = rdpei->ContactUpdate(rdpei, touchId);
contact.contactFlags |= CONTACT_FLAG_INRANGE;
contact.contactFlags |= CONTACT_FLAG_INCONTACT;
}
else if (flags & CONTACT_FLAG_UP)
{
//contact.contactFlags |= CONTACT_FLAG_INRANGE;
contact.contactId = rdpei->ContactEnd(rdpei, touchId);
}
//rdpei->BeginFrame(rdpei);
rdpei->BeginFrame(rdpei);
rdpei->AddContact(rdpei, &contact);
rdpei->EndFrame(rdpei);

View File

@@ -68,6 +68,10 @@ typedef int (*pcRdpeiBeginFrame)(RdpeiClientContext* context);
typedef int (*pcRdpeiEndFrame)(RdpeiClientContext* context);
typedef int (*pcRdpeiAddContact)(RdpeiClientContext* context, RDPINPUT_CONTACT_DATA* contact);
typedef int (*pcRdpeiContactBegin)(RdpeiClientContext* context, int externalId, int x, int y);
typedef int (*pcRdpeiContactUpdate)(RdpeiClientContext* context, int externalId);
typedef int (*pcRdpeiContactEnd)(RdpeiClientContext* context, int externalId);
struct _rdpei_client_context
{
void* handle;
@@ -77,6 +81,10 @@ struct _rdpei_client_context
pcRdpeiBeginFrame BeginFrame;
pcRdpeiEndFrame EndFrame;
pcRdpeiAddContact AddContact;
pcRdpeiContactBegin ContactBegin;
pcRdpeiContactUpdate ContactUpdate;
pcRdpeiContactEnd ContactEnd;
};
#endif /* FREERDP_CHANNEL_CLIENT_RDPEI_H */