From e6aec6c9362498a6a42d020964ed0c8ffc10a547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 14 May 2013 17:45:52 -0400 Subject: [PATCH] xfreerdp: initial multitouch functionality --- channels/rdpei/client/rdpei_main.c | 176 +++++++++++++++++++++++++---- channels/rdpei/client/rdpei_main.h | 13 +++ client/X11/xf_event.c | 9 ++ client/X11/xf_input.c | 45 +++++--- include/freerdp/client/rdpei.h | 8 ++ 5 files changed, 210 insertions(+), 41 deletions(-) diff --git a/channels/rdpei/client/rdpei_main.c b/channels/rdpei/client/rdpei_main.c index 65baaf430..562951bed 100644 --- a/channels/rdpei/client/rdpei_main.c +++ b/channels/rdpei/client/rdpei_main.c @@ -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); diff --git a/channels/rdpei/client/rdpei_main.h b/channels/rdpei/client/rdpei_main.h index 5781ee654..8e532bd00 100644 --- a/channels/rdpei/client/rdpei_main.h +++ b/channels/rdpei/client/rdpei_main.h @@ -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 diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index d6226ca67..5af5c232e 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -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; diff --git a/client/X11/xf_input.c b/client/X11/xf_input.c index a8a442cb6..d34fee12d 100644 --- a/client/X11/xf_input.c +++ b/client/X11/xf_input.c @@ -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); diff --git a/include/freerdp/client/rdpei.h b/include/freerdp/client/rdpei.h index 0a41ac423..37ed03ead 100644 --- a/include/freerdp/client/rdpei.h +++ b/include/freerdp/client/rdpei.h @@ -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 */