Cleaned up rdpei channel, updated to current spec.
authorArmin Novak <armin.novak@thincast.com>
Wed, 11 Nov 2020 07:54:46 +0000 (08:54 +0100)
committerakallabeth <akallabeth@users.noreply.github.com>
Thu, 25 Feb 2021 08:51:41 +0000 (09:51 +0100)
(cherry picked from commit bd061fc291108bad67ce94dd92e1fdb31e0c0cae)

channels/rdpei/client/rdpei_main.c
channels/rdpei/client/rdpei_main.h
channels/rdpei/rdpei_common.c
channels/rdpei/rdpei_common.h
channels/rdpei/server/rdpei_main.c
include/freerdp/channels/rdpei.h
include/freerdp/client/rdpei.h
include/freerdp/server/rdpei.h

index 0134c60..f20d58a 100644 (file)
@@ -62,7 +62,8 @@
  * http://msdn.microsoft.com/en-us/library/hh454910/
  */
 
-#define MAX_CONTACTS 512
+#define MAX_CONTACTS 64
+#define MAX_PEN_CONTACTS 4
 
 struct _RDPEI_CHANNEL_CALLBACK
 {
@@ -93,16 +94,23 @@ struct _RDPEI_PLUGIN
 
        RdpeiClientContext* context;
 
-       int version;
+       UINT32 version;
+       UINT32 features;
        UINT16 maxTouchContacts;
        UINT64 currentFrameTime;
        UINT64 previousFrameTime;
-       RDPINPUT_TOUCH_FRAME frame;
-       RDPINPUT_CONTACT_DATA contacts[MAX_CONTACTS];
-       RDPINPUT_CONTACT_POINT* contactPoints;
+       RDPINPUT_CONTACT_POINT contactPoints[MAX_CONTACTS];
 
+       UINT64 currentPenFrameTime;
+       UINT64 previousPenFrameTime;
+       UINT16 maxPenContacts;
+       RDPINPUT_PEN_CONTACT_POINT penContactPoints[MAX_PEN_CONTACTS];
+
+       CRITICAL_SECTION lock;
        rdpContext* rdpcontext;
        BOOL initialized;
+       HANDLE thread;
+       HANDLE event;
 };
 typedef struct _RDPEI_PLUGIN RDPEI_PLUGIN;
 
@@ -111,7 +119,7 @@ typedef struct _RDPEI_PLUGIN RDPEI_PLUGIN;
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-static UINT rdpei_send_frame(RdpeiClientContext* context);
+static UINT rdpei_send_frame(RdpeiClientContext* context, RDPINPUT_TOUCH_FRAME* frame);
 
 #ifdef WITH_DEBUG_RDPEI
 static const char* rdpei_eventid_string(UINT16 event)
@@ -130,12 +138,39 @@ static const char* rdpei_eventid_string(UINT16 event)
                        return "EVENTID_RESUME_TOUCH";
                case EVENTID_DISMISS_HOVERING_CONTACT:
                        return "EVENTID_DISMISS_HOVERING_CONTACT";
+               case EVENTID_PEN:
+                       return "EVENTID_PEN";
                default:
                        return "EVENTID_UNKNOWN";
        }
 }
 #endif
 
+static RDPINPUT_CONTACT_POINT* rdpei_contact(RDPEI_PLUGIN* rdpei, INT32 externalId, BOOL active)
+{
+       UINT16 i;
+
+       for (i = 0; i < rdpei->maxTouchContacts; i++)
+       {
+               RDPINPUT_CONTACT_POINT* contactPoint = &rdpei->contactPoints[i];
+
+               if (!contactPoint->active && active)
+                       continue;
+               else if (!contactPoint->active && !active)
+               {
+                       contactPoint->contactId = i;
+                       contactPoint->externalId = externalId;
+                       contactPoint->active = TRUE;
+                       return contactPoint;
+               }
+               else if (contactPoint->externalId == externalId)
+               {
+                       return contactPoint;
+               }
+       }
+       return NULL;
+}
+
 /**
  * Function description
  *
@@ -143,23 +178,29 @@ static const char* rdpei_eventid_string(UINT16 event)
  */
 static UINT rdpei_add_frame(RdpeiClientContext* context)
 {
-       int i;
-       RDPINPUT_CONTACT_DATA* contact;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
-       rdpei->frame.contactCount = 0;
+       UINT16 i;
+       RDPEI_PLUGIN* rdpei;
+       RDPINPUT_TOUCH_FRAME frame = { 0 };
+       RDPINPUT_CONTACT_DATA contacts[MAX_CONTACTS] = { 0 };
+
+       if (!context || !context->handle)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+       frame.contacts = contacts;
 
        for (i = 0; i < rdpei->maxTouchContacts; i++)
        {
-               contact = (RDPINPUT_CONTACT_DATA*)&(rdpei->contactPoints[i].data);
+               RDPINPUT_CONTACT_POINT* contactPoint = &rdpei->contactPoints[i];
+               RDPINPUT_CONTACT_DATA* contact = &contactPoint->data;
 
-               if (rdpei->contactPoints[i].dirty)
+               if (contactPoint->dirty)
                {
-                       CopyMemory(&(rdpei->contacts[rdpei->frame.contactCount]), contact,
-                                  sizeof(RDPINPUT_CONTACT_DATA));
+                       contacts[frame.contactCount] = *contact;
                        rdpei->contactPoints[i].dirty = FALSE;
-                       rdpei->frame.contactCount++;
+                       frame.contactCount++;
                }
-               else if (rdpei->contactPoints[i].active)
+               else if (contactPoint->active)
                {
                        if (contact->contactFlags & CONTACT_FLAG_DOWN)
                        {
@@ -168,12 +209,26 @@ static UINT rdpei_add_frame(RdpeiClientContext* context)
                                contact->contactFlags |= CONTACT_FLAG_INCONTACT;
                        }
 
-                       CopyMemory(&(rdpei->contacts[rdpei->frame.contactCount]), contact,
-                                  sizeof(RDPINPUT_CONTACT_DATA));
-                       rdpei->frame.contactCount++;
+                       contacts[frame.contactCount] = *contact;
+                       frame.contactCount++;
+               }
+               if (contact->contactFlags & CONTACT_FLAG_UP)
+               {
+                       contactPoint->active = FALSE;
+                       contactPoint->externalId = 0;
+                       contactPoint->contactId = 0;
                }
        }
 
+       if (frame.contactCount > 0)
+       {
+               UINT error = rdpei_send_frame(context, &frame);
+               if (error != CHANNEL_RC_OK)
+               {
+                       WLog_ERR(TAG, "rdpei_send_frame failed with error %" PRIu32 "!", error);
+                       return error;
+               }
+       }
        return CHANNEL_RC_OK;
 }
 
@@ -186,6 +241,9 @@ static UINT rdpei_send_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s, UINT16
                            UINT32 pduLength)
 {
        UINT status;
+       if (!callback || !s || !callback->channel || !callback->channel->Write)
+               return ERROR_INTERNAL_ERROR;
+
        Stream_SetPosition(s, 0);
        Stream_Write_UINT16(s, eventId);   /* eventId (2 bytes) */
        Stream_Write_UINT32(s, pduLength); /* pduLength (4 bytes) */
@@ -200,6 +258,249 @@ static UINT rdpei_send_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s, UINT16
        return status;
 }
 
+static UINT rdpei_write_pen_frame(wStream* s, const RDPINPUT_PEN_FRAME* frame)
+{
+       UINT16 x;
+       if (!s || !frame)
+               return ERROR_INTERNAL_ERROR;
+
+       if (!rdpei_write_2byte_unsigned(s, frame->contactCount))
+               return ERROR_OUTOFMEMORY;
+       if (!rdpei_write_8byte_unsigned(s, frame->frameOffset))
+               return ERROR_OUTOFMEMORY;
+       for (x = 0; x < frame->contactCount; x++)
+       {
+               const RDPINPUT_PEN_CONTACT* contact = &frame->contacts[x];
+
+               if (!Stream_EnsureRemainingCapacity(s, 1))
+                       return ERROR_OUTOFMEMORY;
+               Stream_Write_UINT8(s, contact->deviceId);
+               if (!rdpei_write_2byte_unsigned(s, contact->fieldsPresent))
+                       return ERROR_OUTOFMEMORY;
+               if (!rdpei_write_4byte_signed(s, contact->x))
+                       return ERROR_OUTOFMEMORY;
+               if (!rdpei_write_4byte_signed(s, contact->y))
+                       return ERROR_OUTOFMEMORY;
+               if (!rdpei_write_4byte_unsigned(s, contact->contactFlags))
+                       return ERROR_OUTOFMEMORY;
+               if (contact->fieldsPresent & PEN_CONTACT_PENFLAGS_PRESENT)
+               {
+                       if (!rdpei_write_4byte_unsigned(s, contact->penFlags))
+                               return ERROR_OUTOFMEMORY;
+               }
+               if (contact->fieldsPresent & PEN_CONTACT_PRESSURE_PRESENT)
+               {
+                       if (!rdpei_write_4byte_unsigned(s, contact->pressure))
+                               return ERROR_OUTOFMEMORY;
+               }
+               if (contact->fieldsPresent & PEN_CONTACT_ROTATION_PRESENT)
+               {
+                       if (!rdpei_write_2byte_unsigned(s, contact->rotation))
+                               return ERROR_OUTOFMEMORY;
+               }
+               if (contact->fieldsPresent & PEN_CONTACT_TILTX_PRESENT)
+               {
+                       if (!rdpei_write_2byte_signed(s, contact->tiltX))
+                               return ERROR_OUTOFMEMORY;
+               }
+               if (contact->fieldsPresent & PEN_CONTACT_TILTY_PRESENT)
+               {
+                       if (!rdpei_write_2byte_signed(s, contact->tiltY))
+                               return ERROR_OUTOFMEMORY;
+               }
+       }
+       return CHANNEL_RC_OK;
+}
+
+static UINT rdpei_send_pen_event_pdu(RDPEI_CHANNEL_CALLBACK* callback, UINT32 frameOffset,
+                                     const RDPINPUT_PEN_FRAME* frames, UINT16 count)
+{
+       UINT status;
+       wStream* s;
+       UINT16 x;
+
+       if (!frames || (count == 0))
+               return ERROR_INTERNAL_ERROR;
+
+       s = Stream_New(NULL, 64);
+
+       if (!s)
+       {
+               WLog_ERR(TAG, "Stream_New failed!");
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
+       Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
+       /**
+        * the time that has elapsed (in milliseconds) from when the oldest touch frame
+        * was generated to when it was encoded for transmission by the client.
+        */
+       rdpei_write_4byte_unsigned(s, frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
+       rdpei_write_2byte_unsigned(s, count);       /* (frameCount) TWO_BYTE_UNSIGNED_INTEGER */
+
+       for (x = 0; x < count; x++)
+       {
+               if ((status = rdpei_write_pen_frame(s, &frames[x])))
+               {
+                       WLog_ERR(TAG, "rdpei_write_touch_frame failed with error %" PRIu32 "!", status);
+                       Stream_Free(s, TRUE);
+                       return status;
+               }
+       }
+       Stream_SealLength(s);
+
+       status = rdpei_send_pdu(callback, s, EVENTID_PEN, Stream_Length(s));
+       Stream_Free(s, TRUE);
+       return status;
+}
+
+static UINT rdpei_send_pen_frame(RdpeiClientContext* context, RDPINPUT_PEN_FRAME* frame)
+{
+       const UINT64 currentTime = GetTickCount64();
+       RDPEI_PLUGIN* rdpei;
+       RDPEI_CHANNEL_CALLBACK* callback;
+       UINT error;
+
+       if (!context)
+               return ERROR_INTERNAL_ERROR;
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+       if (!rdpei || !rdpei->listener_callback)
+               return ERROR_INTERNAL_ERROR;
+
+       callback = rdpei->listener_callback->channel_callback;
+
+       if (!rdpei->previousPenFrameTime && !rdpei->currentPenFrameTime)
+       {
+               rdpei->currentPenFrameTime = currentTime;
+               frame->frameOffset = 0;
+       }
+       else
+       {
+               rdpei->currentPenFrameTime = currentTime;
+               frame->frameOffset = rdpei->currentPenFrameTime - rdpei->previousPenFrameTime;
+       }
+
+       if ((error = rdpei_send_pen_event_pdu(callback, frame->frameOffset, frame, 1)))
+               return error;
+
+       rdpei->previousPenFrameTime = rdpei->currentPenFrameTime;
+       return error;
+}
+
+static UINT rdpei_add_pen_frame(RdpeiClientContext* context)
+{
+       UINT16 i;
+       RDPEI_PLUGIN* rdpei;
+       RDPINPUT_PEN_FRAME penFrame = { 0 };
+       RDPINPUT_PEN_CONTACT penContacts[MAX_PEN_CONTACTS] = { 0 };
+
+       if (!context || !context->handle)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+
+       penFrame.contacts = penContacts;
+
+       for (i = 0; i < rdpei->maxPenContacts; i++)
+       {
+               RDPINPUT_PEN_CONTACT_POINT* contact = &(rdpei->penContactPoints[i]);
+
+               if (contact->dirty)
+               {
+                       penContacts[penFrame.contactCount++] = contact->data;
+                       contact->dirty = FALSE;
+               }
+               else if (contact->active)
+               {
+                       if (contact->data.contactFlags & CONTACT_FLAG_DOWN)
+                       {
+                               contact->data.contactFlags = CONTACT_FLAG_UPDATE;
+                               contact->data.contactFlags |= CONTACT_FLAG_INRANGE;
+                               contact->data.contactFlags |= CONTACT_FLAG_INCONTACT;
+                       }
+
+                       penContacts[penFrame.contactCount++] = contact->data;
+               }
+               if (contact->data.contactFlags & CONTACT_FLAG_UP)
+               {
+                       contact->externalId = 0;
+                       contact->active = FALSE;
+               }
+       }
+
+       if (penFrame.contactCount > 0)
+               return rdpei_send_pen_frame(context, &penFrame);
+       return CHANNEL_RC_OK;
+}
+
+static UINT rdpei_update(RdpeiClientContext* context)
+{
+       UINT error = rdpei_add_frame(context);
+       if (error != CHANNEL_RC_OK)
+       {
+               WLog_ERR(TAG, "rdpei_add_frame failed with error %" PRIu32 "!", error);
+               return error;
+       }
+
+       return rdpei_add_pen_frame(context);
+}
+
+static DWORD WINAPI rdpei_periodic_update(LPVOID arg)
+{
+       DWORD status;
+       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)arg;
+       UINT error = CHANNEL_RC_OK;
+       RdpeiClientContext* context;
+
+       if (!rdpei)
+       {
+               error = ERROR_INVALID_PARAMETER;
+               goto out;
+       }
+
+       context = (RdpeiClientContext*)rdpei->iface.pInterface;
+
+       if (!context)
+       {
+               error = ERROR_INVALID_PARAMETER;
+               goto out;
+       }
+
+       while (rdpei->initialized)
+       {
+               status = WaitForSingleObject(rdpei->event, 20);
+
+               if (status == WAIT_FAILED)
+               {
+                       error = GetLastError();
+                       WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
+                       break;
+               }
+
+               EnterCriticalSection(&rdpei->lock);
+
+               error = rdpei_update(context);
+               if (error != CHANNEL_RC_OK)
+               {
+                       WLog_ERR(TAG, "rdpei_add_frame failed with error %" PRIu32 "!", error);
+                       break;
+               }
+
+               if (status == WAIT_OBJECT_0)
+                       ResetEvent(rdpei->event);
+
+               LeaveCriticalSection(&rdpei->lock);
+       }
+
+out:
+
+       if (error && rdpei && rdpei->rdpcontext)
+               setChannelError(rdpei->rdpcontext, error, "rdpei_schedule_thread reported an error");
+
+       ExitThread(error);
+       return error;
+}
+
 /**
  * Function description
  *
@@ -211,10 +512,17 @@ static UINT rdpei_send_cs_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback)
        wStream* s;
        UINT32 flags;
        UINT32 pduLength;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
+       RDPEI_PLUGIN* rdpei;
+
+       if (!callback || !callback->plugin)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)callback->plugin;
        flags = 0;
-       flags |= READY_FLAGS_SHOW_TOUCH_VISUALS;
-       // flags |= READY_FLAGS_DISABLE_TIMESTAMP_INJECTION;
+       flags |= CS_READY_FLAGS_SHOW_TOUCH_VISUALS;
+       if (rdpei->version > RDPINPUT_PROTOCOL_V10)
+               flags |= CS_READY_FLAGS_DISABLE_TIMESTAMP_INJECTION;
+       flags |= CS_READY_FLAGS_ENABLE_MULTIPEN_INJECTION;
        pduLength = RDPINPUT_HEADER_LENGTH + 10;
        s = Stream_New(NULL, pduLength);
 
@@ -226,7 +534,7 @@ static UINT rdpei_send_cs_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback)
 
        Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
        Stream_Write_UINT32(s, flags);                   /* flags (4 bytes) */
-       Stream_Write_UINT32(s, RDPINPUT_PROTOCOL_V10);   /* protocolVersion (4 bytes) */
+       Stream_Write_UINT32(s, rdpei->version);          /* protocolVersion (4 bytes) */
        Stream_Write_UINT16(s, rdpei->maxTouchContacts); /* maxTouchContacts (2 bytes) */
        Stream_SealLength(s);
        status = rdpei_send_pdu(callback, s, EVENTID_CS_READY, pduLength);
@@ -265,6 +573,8 @@ static UINT rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
        UINT32 index;
        int rectSize = 2;
        RDPINPUT_CONTACT_DATA* contact;
+       if (!s || !frame)
+               return ERROR_INTERNAL_ERROR;
 #ifdef WITH_DEBUG_RDPEI
        WLog_DBG(TAG, "contactCount: %" PRIu32 "", frame->contactCount);
        WLog_DBG(TAG, "frameOffset: 0x%016" PRIX64 "", frame->frameOffset);
@@ -349,6 +659,8 @@ static UINT rdpei_send_touch_event_pdu(RDPEI_CHANNEL_CALLBACK* callback,
        UINT status;
        wStream* s;
        UINT32 pduLength;
+       if (!frame)
+               return ERROR_INTERNAL_ERROR;
        pduLength = 64 + (frame->contactCount * 64);
        s = Stream_New(NULL, pduLength);
 
@@ -388,8 +700,32 @@ static UINT rdpei_send_touch_event_pdu(RDPEI_CHANNEL_CALLBACK* callback,
  */
 static UINT rdpei_recv_sc_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
 {
+       UINT32 features = 0;
+       UINT32 size;
        UINT32 protocolVersion;
+       RDPEI_PLUGIN* rdpei;
+
+       if (!callback || !callback->plugin)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)callback->plugin;
+
+       size = Stream_GetRemainingLength(s);
+       if (size < 4)
+               return ERROR_INVALID_DATA;
        Stream_Read_UINT32(s, protocolVersion); /* protocolVersion (4 bytes) */
+
+       if (protocolVersion >= RDPINPUT_PROTOCOL_V300)
+       {
+               if (size < 8)
+                       return ERROR_INVALID_DATA;
+       }
+       if (size >= 9)
+               Stream_Read_UINT32(s, features);
+
+       if (rdpei->version > protocolVersion)
+               rdpei->version = protocolVersion;
+       rdpei->features = features;
 #if 0
 
        if (protocolVersion != RDPINPUT_PROTOCOL_V10)
@@ -409,8 +745,17 @@ static UINT rdpei_recv_sc_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s
  */
 static UINT rdpei_recv_suspend_touch_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
 {
-       RdpeiClientContext* rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
        UINT error = CHANNEL_RC_OK;
+       RdpeiClientContext* rdpei;
+
+       WINPR_UNUSED(s);
+
+       if (!callback || !callback->plugin)
+               return ERROR_INTERNAL_ERROR;
+       rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
+       if (!rdpei)
+               return ERROR_INTERNAL_ERROR;
+
        IFCALLRET(rdpei->SuspendTouch, error, rdpei);
 
        if (error)
@@ -426,8 +771,14 @@ static UINT rdpei_recv_suspend_touch_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStre
  */
 static UINT rdpei_recv_resume_touch_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
 {
-       RdpeiClientContext* rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
+       RdpeiClientContext* rdpei;
        UINT error = CHANNEL_RC_OK;
+       if (!s || !callback || !callback->plugin)
+               return ERROR_INTERNAL_ERROR;
+       rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
+       if (!rdpei)
+               return ERROR_INTERNAL_ERROR;
+
        IFCALLRET(rdpei->ResumeTouch, error, rdpei);
 
        if (error)
@@ -446,6 +797,8 @@ static UINT rdpei_recv_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
        UINT16 eventId;
        UINT32 pduLength;
        UINT error;
+       if (!s)
+               return ERROR_INTERNAL_ERROR;
        if (Stream_GetRemainingLength(s) < 6)
                return ERROR_INVALID_DATA;
 
@@ -532,8 +885,13 @@ static UINT rdpei_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
 {
        RDPEI_CHANNEL_CALLBACK* callback;
        RDPEI_LISTENER_CALLBACK* listener_callback = (RDPEI_LISTENER_CALLBACK*)pListenerCallback;
+       if (!listener_callback)
+               return ERROR_INTERNAL_ERROR;
        callback = (RDPEI_CHANNEL_CALLBACK*)calloc(1, sizeof(RDPEI_CHANNEL_CALLBACK));
 
+       WINPR_UNUSED(Data);
+       WINPR_UNUSED(pbAccept);
+
        if (!callback)
        {
                WLog_ERR(TAG, "calloc failed!");
@@ -555,6 +913,43 @@ static UINT rdpei_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
  *
  * @return 0 on success, otherwise a Win32 error code
  */
+static UINT rdpei_plugin_terminated(IWTSPlugin* pPlugin)
+{
+       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)pPlugin;
+
+       if (!pPlugin)
+               return ERROR_INVALID_PARAMETER;
+
+       if (rdpei && rdpei->listener_callback)
+       {
+               IWTSVirtualChannelManager* mgr = rdpei->listener_callback->channel_mgr;
+               if (mgr)
+                       IFCALL(mgr->DestroyListener, mgr, rdpei->listener);
+
+               rdpei->initialized = FALSE;
+               if (rdpei->event)
+                       SetEvent(rdpei->event);
+
+               if (rdpei->thread)
+               {
+                       WaitForSingleObject(rdpei->thread, INFINITE);
+                       CloseHandle(rdpei->thread);
+               }
+               if (rdpei->event)
+                       CloseHandle(rdpei->event);
+       }
+       DeleteCriticalSection(&rdpei->lock);
+       free(rdpei->listener_callback);
+       free(rdpei->context);
+       free(rdpei);
+       return CHANNEL_RC_OK;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
 static UINT rdpei_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
 {
        UINT error;
@@ -586,54 +981,48 @@ static UINT rdpei_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
 
        rdpei->listener->pInterface = rdpei->iface.pInterface;
 
+       InitializeCriticalSection(&rdpei->lock);
+       rdpei->event = CreateEventA(NULL, TRUE, FALSE, NULL);
+       if (!rdpei->event)
+               goto error_out;
+       rdpei->thread = CreateThread(NULL, 0, rdpei_periodic_update, rdpei, 0, NULL);
+       if (!rdpei->thread)
+               goto error_out;
        rdpei->initialized = TRUE;
        return error;
 error_out:
-       free(rdpei->listener_callback);
+       rdpei_plugin_terminated(pPlugin);
        return error;
 }
 
 /**
- * Function description
- *
- * @return 0 on success, otherwise a Win32 error code
- */
-static UINT rdpei_plugin_terminated(IWTSPlugin* pPlugin)
-{
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)pPlugin;
-
-       if (!pPlugin)
-               return ERROR_INVALID_PARAMETER;
-
-       if (rdpei && rdpei->listener_callback)
-       {
-               IWTSVirtualChannelManager* mgr = rdpei->listener_callback->channel_mgr;
-               if (mgr)
-                       IFCALL(mgr->DestroyListener, mgr, rdpei->listener);
-       }
-       free(rdpei->listener_callback);
-       free(rdpei->contactPoints);
-       free(rdpei->context);
-       free(rdpei);
-       return CHANNEL_RC_OK;
-}
-
-/**
  * Channel Client Interface
  */
 
-static int rdpei_get_version(RdpeiClientContext* context)
+static UINT32 rdpei_get_version(RdpeiClientContext* context)
 {
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
+       RDPEI_PLUGIN* rdpei;
+       if (!context || !context->handle)
+               return -1;
+       rdpei = (RDPEI_PLUGIN*)context->handle;
        return rdpei->version;
 }
 
+static UINT32 rdpei_get_features(RdpeiClientContext* context)
+{
+       RDPEI_PLUGIN* rdpei;
+       if (!context || !context->handle)
+               return -1;
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+       return rdpei->features;
+}
+
 /**
  * Function description
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-UINT rdpei_send_frame(RdpeiClientContext* context)
+UINT rdpei_send_frame(RdpeiClientContext* context, RDPINPUT_TOUCH_FRAME* frame)
 {
        UINT64 currentTime;
        RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
@@ -644,22 +1033,21 @@ UINT rdpei_send_frame(RdpeiClientContext* context)
        if (!rdpei->previousFrameTime && !rdpei->currentFrameTime)
        {
                rdpei->currentFrameTime = currentTime;
-               rdpei->frame.frameOffset = 0;
+               frame->frameOffset = 0;
        }
        else
        {
                rdpei->currentFrameTime = currentTime;
-               rdpei->frame.frameOffset = rdpei->currentFrameTime - rdpei->previousFrameTime;
+               frame->frameOffset = rdpei->currentFrameTime - rdpei->previousFrameTime;
        }
 
-       if ((error = rdpei_send_touch_event_pdu(callback, &rdpei->frame)))
+       if ((error = rdpei_send_touch_event_pdu(callback, frame)))
        {
                WLog_ERR(TAG, "rdpei_send_touch_event_pdu failed with error %" PRIu32 "!", error);
                return error;
        }
 
        rdpei->previousFrameTime = rdpei->currentFrameTime;
-       rdpei->frame.contactCount = 0;
        return error;
 }
 
@@ -670,127 +1058,195 @@ UINT rdpei_send_frame(RdpeiClientContext* context)
  */
 static UINT rdpei_add_contact(RdpeiClientContext* context, const RDPINPUT_CONTACT_DATA* contact)
 {
-       UINT error;
        RDPINPUT_CONTACT_POINT* contactPoint;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
+       RDPEI_PLUGIN* rdpei;
+       if (!context || !contact || !context->handle)
+               return ERROR_INTERNAL_ERROR;
 
-       contactPoint = (RDPINPUT_CONTACT_POINT*)&rdpei->contactPoints[contact->contactId];
-       CopyMemory(&(contactPoint->data), contact, sizeof(RDPINPUT_CONTACT_DATA));
-       contactPoint->dirty = TRUE;
+       rdpei = (RDPEI_PLUGIN*)context->handle;
 
-       error = rdpei_add_frame(context);
-       if (error != CHANNEL_RC_OK)
-       {
-               WLog_ERR(TAG, "rdpei_add_frame failed with error %" PRIu32 "!", error);
-               return error;
-       }
+       EnterCriticalSection(&rdpei->lock);
+       contactPoint = &rdpei->contactPoints[contact->contactId];
+       contactPoint->data = *contact;
+       contactPoint->dirty = TRUE;
+       SetEvent(rdpei->event);
+       LeaveCriticalSection(&rdpei->lock);
 
-       if (rdpei->frame.contactCount > 0)
-       {
-               error = rdpei_send_frame(context);
-               if (error != CHANNEL_RC_OK)
-               {
-                       WLog_ERR(TAG, "rdpei_send_frame failed with error %" PRIu32 "!", error);
-                       return error;
-               }
-       }
        return CHANNEL_RC_OK;
 }
 
-/**
- * Function description
- *
- * @return 0 on success, otherwise a Win32 error code
- */
-static UINT rdpei_touch_begin(RdpeiClientContext* context, int externalId, int x, int y,
-                              int* contactId)
+static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, UINT32 contactFlags,
+                                INT32 x, INT32 y, INT32* contactId)
 {
-       unsigned int i;
        INT64 contactIdlocal = -1;
-       RDPINPUT_CONTACT_DATA contact;
-       RDPINPUT_CONTACT_POINT* contactPoint = NULL;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
+       RDPINPUT_CONTACT_POINT* contactPoint;
+       RDPEI_PLUGIN* rdpei;
+       BOOL begin;
        UINT error = CHANNEL_RC_OK;
 
-       /* Create a new contact point in an empty slot */
-
-       for (i = 0; i < rdpei->maxTouchContacts; i++)
-       {
-               contactPoint = (RDPINPUT_CONTACT_POINT*)&rdpei->contactPoints[i];
+       if (!context || !contactId || !context->handle)
+               return ERROR_INTERNAL_ERROR;
 
-               if (!contactPoint->active)
-               {
-                       contactPoint->contactId = i;
-                       contactIdlocal = contactPoint->contactId;
-                       contactPoint->externalId = externalId;
-                       contactPoint->active = TRUE;
-                       contactPoint->state = RDPINPUT_CONTACT_STATE_ENGAGED;
-                       break;
-               }
-       }
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+       /* Create a new contact point in an empty slot */
+       EnterCriticalSection(&rdpei->lock);
+       begin = contactFlags & CONTACT_FLAG_DOWN;
+       contactPoint = rdpei_contact(rdpei, externalId, !begin);
+       if (contactPoint)
+               contactIdlocal = contactPoint->contactId;
+       LeaveCriticalSection(&rdpei->lock);
 
        if (contactIdlocal >= 0)
        {
-               ZeroMemory(&contact, sizeof(RDPINPUT_CONTACT_DATA));
-               contactPoint->lastX = x;
-               contactPoint->lastY = y;
+               RDPINPUT_CONTACT_DATA contact = { 0 };
                contact.x = x;
                contact.y = y;
-               contact.contactId = (UINT32)contactIdlocal;
-               contact.contactFlags |= CONTACT_FLAG_DOWN;
-               contact.contactFlags |= CONTACT_FLAG_INRANGE;
-               contact.contactFlags |= CONTACT_FLAG_INCONTACT;
+               contact.contactId = contactIdlocal;
+               contact.contactFlags = contactFlags;
                error = context->AddContact(context, &contact);
        }
 
-       *contactId = contactIdlocal;
+       if (contactId)
+               *contactId = contactIdlocal;
        return error;
 }
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpei_touch_begin(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                              INT32* contactId)
+{
+       return rdpei_touch_process(context, externalId,
+                                  CONTACT_FLAG_DOWN | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT, x,
+                                  y, contactId);
+}
 
 /**
  * Function description
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-static UINT rdpei_touch_update(RdpeiClientContext* context, int externalId, int x, int y,
-                               int* contactId)
+static UINT rdpei_touch_update(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                               INT32* contactId)
 {
-       unsigned int i;
-       int contactIdlocal = -1;
-       RDPINPUT_CONTACT_DATA contact;
-       RDPINPUT_CONTACT_POINT* contactPoint = NULL;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
-       UINT error = CHANNEL_RC_OK;
+       return rdpei_touch_process(context, externalId,
+                                  CONTACT_FLAG_UPDATE | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT,
+                                  x, y, contactId);
+}
 
-       for (i = 0; i < rdpei->maxTouchContacts; i++)
-       {
-               contactPoint = (RDPINPUT_CONTACT_POINT*)&rdpei->contactPoints[i];
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpei_touch_end(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                            INT32* contactId)
+{
+       UINT error = rdpei_touch_process(
+           context, externalId, CONTACT_FLAG_UPDATE | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT, x,
+           y, contactId);
+       if (error != CHANNEL_RC_OK)
+               return error;
+       return rdpei_touch_process(context, externalId, CONTACT_FLAG_UP, x, y, contactId);
+}
 
-               if (!contactPoint->active)
-                       continue;
+static RDPINPUT_PEN_CONTACT_POINT* rdpei_pen_contact(RDPEI_PLUGIN* rdpei, INT32 externalId,
+                                                     BOOL active)
+{
+       UINT32 x;
+       if (!rdpei)
+               return NULL;
 
-               if (contactPoint->externalId == externalId)
+       for (x = 0; x < rdpei->maxPenContacts; x++)
+       {
+               RDPINPUT_PEN_CONTACT_POINT* contact = &rdpei->penContactPoints[x];
+               if (active)
                {
-                       contactIdlocal = contactPoint->contactId;
-                       break;
+                       if (contact->active)
+                       {
+                               if (contact->externalId == externalId)
+                                       return contact;
+                       }
+               }
+               else
+               {
+                       if (!contact->active)
+                       {
+                               contact->externalId = externalId;
+                               contact->active = TRUE;
+                               return contact;
+                       }
                }
        }
+       return NULL;
+}
 
-       if (contactIdlocal >= 0)
+static UINT rdpei_add_pen(RdpeiClientContext* context, INT32 externalId,
+                          const RDPINPUT_PEN_CONTACT* contact)
+{
+       RDPEI_PLUGIN* rdpei;
+       RDPINPUT_PEN_CONTACT_POINT* contactPoint;
+
+       if (!context || !contact || !context->handle)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+
+       EnterCriticalSection(&rdpei->lock);
+       contactPoint = rdpei_pen_contact(rdpei, externalId, TRUE);
+       if (contactPoint)
        {
-               ZeroMemory(&contact, sizeof(RDPINPUT_CONTACT_DATA));
-               contactPoint->lastX = x;
-               contactPoint->lastY = y;
+               contactPoint->data = *contact;
+               contactPoint->dirty = TRUE;
+               SetEvent(rdpei->event);
+       }
+       LeaveCriticalSection(&rdpei->lock);
+
+       return CHANNEL_RC_OK;
+}
+
+static UINT rdpei_pen_process(RdpeiClientContext* context, INT32 externalId, UINT32 contactFlags,
+                              UINT32 fieldFlags, INT32 x, INT32 y, va_list ap)
+{
+       RDPINPUT_PEN_CONTACT_POINT* contactPoint;
+       RDPEI_PLUGIN* rdpei;
+       BOOL begin;
+       UINT error = CHANNEL_RC_OK;
+
+       if (!context || !context->handle)
+               return ERROR_INTERNAL_ERROR;
+
+       rdpei = (RDPEI_PLUGIN*)context->handle;
+       begin = contactFlags & CONTACT_FLAG_DOWN;
+
+       EnterCriticalSection(&rdpei->lock);
+       contactPoint = rdpei_pen_contact(rdpei, externalId, !begin);
+       LeaveCriticalSection(&rdpei->lock);
+       if (contactPoint != NULL)
+       {
+               RDPINPUT_PEN_CONTACT contact = { 0 };
+
                contact.x = x;
                contact.y = y;
-               contact.contactId = (UINT32)contactIdlocal;
-               contact.contactFlags |= CONTACT_FLAG_UPDATE;
-               contact.contactFlags |= CONTACT_FLAG_INRANGE;
-               contact.contactFlags |= CONTACT_FLAG_INCONTACT;
-               error = context->AddContact(context, &contact);
+               contact.fieldsPresent = fieldFlags;
+
+               contact.contactFlags = contactFlags;
+               if (fieldFlags & PEN_CONTACT_PENFLAGS_PRESENT)
+                       contact.penFlags = va_arg(ap, UINT32);
+               if (fieldFlags & PEN_CONTACT_PRESSURE_PRESENT)
+                       contact.pressure = va_arg(ap, UINT32);
+               if (fieldFlags & PEN_CONTACT_ROTATION_PRESENT)
+                       contact.rotation = va_arg(ap, UINT32);
+               if (fieldFlags & PEN_CONTACT_TILTX_PRESENT)
+                       contact.tiltX = va_arg(ap, INT32);
+               if (fieldFlags & PEN_CONTACT_TILTY_PRESENT)
+                       contact.tiltY = va_arg(ap, INT32);
+
+               error = context->AddPen(context, externalId, &contact);
        }
 
-       *contactId = contactIdlocal;
        return error;
 }
 
@@ -799,64 +1255,59 @@ static UINT rdpei_touch_update(RdpeiClientContext* context, int externalId, int
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-static UINT rdpei_touch_end(RdpeiClientContext* context, int externalId, int x, int y,
-                            int* contactId)
-{
-       unsigned int i;
-       int contactIdlocal = -1;
-       int tempvalue;
-       RDPINPUT_CONTACT_DATA contact;
-       RDPINPUT_CONTACT_POINT* contactPoint = NULL;
-       RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
+static UINT rdpei_pen_begin(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
+                            INT32 x, INT32 y, ...)
+{
        UINT error;
+       va_list ap;
 
-       for (i = 0; i < rdpei->maxTouchContacts; i++)
-       {
-               contactPoint = (RDPINPUT_CONTACT_POINT*)&rdpei->contactPoints[i];
-
-               if (!contactPoint->active)
-                       continue;
+       va_start(ap, y);
+       error = rdpei_pen_process(context, externalId,
+                                 CONTACT_FLAG_DOWN | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT,
+                                 fieldFlags, x, y, ap);
+       va_end(ap);
 
-               if (contactPoint->externalId == externalId)
-               {
-                       contactIdlocal = contactPoint->contactId;
-                       break;
-               }
-       }
-
-       if (contactIdlocal >= 0)
-       {
-               ZeroMemory(&contact, sizeof(RDPINPUT_CONTACT_DATA));
-
-               if ((contactPoint->lastX != x) && (contactPoint->lastY != y))
-               {
-                       if ((error = context->TouchUpdate(context, externalId, x, y, &tempvalue)))
-                       {
-                               WLog_ERR(TAG, "context->TouchUpdate failed with error %" PRIu32 "!", error);
-                               return error;
-                       }
-               }
-
-               contact.x = x;
-               contact.y = y;
-               contact.contactId = (UINT32)contactIdlocal;
-               contact.contactFlags |= CONTACT_FLAG_UP;
+       return error;
+}
 
-               if ((error = context->AddContact(context, &contact)))
-               {
-                       WLog_ERR(TAG, "context->AddContact failed with error %" PRIu32 "!", error);
-                       return error;
-               }
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpei_pen_update(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
+                             INT32 x, INT32 y, ...)
+{
+       UINT error;
+       va_list ap;
 
-               contactPoint->externalId = 0;
-               contactPoint->active = FALSE;
-               contactPoint->flags = 0;
-               contactPoint->contactId = 0;
-               contactPoint->state = RDPINPUT_CONTACT_STATE_OUT_OF_RANGE;
-       }
+       va_start(ap, y);
+       error = rdpei_pen_process(context, externalId,
+                                 CONTACT_FLAG_UPDATE | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT,
+                                 fieldFlags, x, y, ap);
+       va_end(ap);
+       return error;
+}
 
-       *contactId = contactIdlocal;
-       return CHANNEL_RC_OK;
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpei_pen_end(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags, INT32 x,
+                          INT32 y, ...)
+{
+       UINT error;
+       va_list ap;
+
+       va_start(ap, y);
+       error = rdpei_pen_process(context, externalId,
+                                 CONTACT_FLAG_UPDATE | CONTACT_FLAG_INRANGE | CONTACT_FLAG_INCONTACT,
+                                 fieldFlags, x, y, ap);
+       if (error == CHANNEL_RC_OK)
+               error = rdpei_pen_process(context, externalId, CONTACT_FLAG_UP, fieldFlags, x, y, ap);
+       va_end(ap);
+       return error;
 }
 
 #ifdef BUILTIN_CHANNELS
@@ -879,7 +1330,6 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
 
        if (!rdpei)
        {
-               size_t size;
                rdpei = (RDPEI_PLUGIN*)calloc(1, sizeof(RDPEI_PLUGIN));
 
                if (!rdpei)
@@ -892,24 +1342,15 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
                rdpei->iface.Connected = NULL;
                rdpei->iface.Disconnected = NULL;
                rdpei->iface.Terminated = rdpei_plugin_terminated;
-               rdpei->version = 1;
+               rdpei->version = RDPINPUT_PROTOCOL_V300;
                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*)calloc(1, size);
+               rdpei->maxTouchContacts = MAX_CONTACTS;
+               rdpei->maxPenContacts = MAX_PEN_CONTACTS;
                rdpei->rdpcontext =
                    ((freerdp*)((rdpSettings*)pEntryPoints->GetRdpSettings(pEntryPoints))->instance)
                        ->context;
 
-               if (!rdpei->contactPoints)
-               {
-                       WLog_ERR(TAG, "calloc failed!");
-                       error = CHANNEL_RC_NO_MEMORY;
-                       goto error_out;
-               }
-
                context = (RdpeiClientContext*)calloc(1, sizeof(RdpeiClientContext));
 
                if (!context)
@@ -921,10 +1362,15 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
 
                context->handle = (void*)rdpei;
                context->GetVersion = rdpei_get_version;
+               context->GetFeatures = rdpei_get_features;
                context->AddContact = rdpei_add_contact;
                context->TouchBegin = rdpei_touch_begin;
                context->TouchUpdate = rdpei_touch_update;
                context->TouchEnd = rdpei_touch_end;
+               context->AddPen = rdpei_add_pen;
+               context->PenBegin = rdpei_pen_begin;
+               context->PenUpdate = rdpei_pen_update;
+               context->PenEnd = rdpei_pen_end;
                rdpei->iface.pInterface = (void*)context;
 
                if ((error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpei", (IWTSPlugin*)rdpei)))
index ed557b2..01ecd5f 100644 (file)
 
 #define TAG CHANNELS_TAG("rdpei.client")
 
-#define RDPINPUT_CONTACT_STATE_INITIAL 0x0000
-#define RDPINPUT_CONTACT_STATE_ENGAGED 0x0001
-#define RDPINPUT_CONTACT_STATE_HOVERING 0x0002
-#define RDPINPUT_CONTACT_STATE_OUT_OF_RANGE 0x0003
-
 /**
  * Touch Contact State Transitions
  *
 
 struct _RDPINPUT_CONTACT_POINT
 {
-       int lastX;
-       int lastY;
        BOOL dirty;
        BOOL active;
-       UINT32 state;
-       UINT32 flags;
        UINT32 contactId;
-       int externalId;
+       INT32 externalId;
        RDPINPUT_CONTACT_DATA data;
 };
 typedef struct _RDPINPUT_CONTACT_POINT RDPINPUT_CONTACT_POINT;
 
+struct _RDPINPUT_PEN_CONTACT_POINT
+{
+       BOOL dirty;
+       BOOL active;
+       INT32 externalId;
+       RDPINPUT_PEN_CONTACT data;
+};
+typedef struct _RDPINPUT_PEN_CONTACT_POINT RDPINPUT_PEN_CONTACT_POINT;
+
 #ifdef WITH_DEBUG_DVC
 #define DEBUG_DVC(...) WLog_DBG(TAG, __VA_ARGS__)
 #else
index 7628b32..1078a6d 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "rdpei_common.h"
 
-BOOL rdpei_read_2byte_unsigned(wStream* s, UINT32* value)
+BOOL rdpei_read_2byte_unsigned(wStream* s, UINT16* value)
 {
        BYTE byte;
 
@@ -53,10 +53,13 @@ BOOL rdpei_read_2byte_unsigned(wStream* s, UINT32* value)
        return TRUE;
 }
 
-BOOL rdpei_write_2byte_unsigned(wStream* s, UINT32 value)
+BOOL rdpei_write_2byte_unsigned(wStream* s, UINT16 value)
 {
        BYTE byte;
 
+       if (!Stream_EnsureRemainingCapacity(s, 2))
+               return FALSE;
+
        if (value > 0x7FFF)
                return FALSE;
 
@@ -76,7 +79,7 @@ BOOL rdpei_write_2byte_unsigned(wStream* s, UINT32 value)
        return TRUE;
 }
 
-BOOL rdpei_read_2byte_signed(wStream* s, INT32* value)
+BOOL rdpei_read_2byte_signed(wStream* s, INT16* value)
 {
        BYTE byte;
        BOOL negative;
@@ -105,11 +108,14 @@ BOOL rdpei_read_2byte_signed(wStream* s, INT32* value)
        return TRUE;
 }
 
-BOOL rdpei_write_2byte_signed(wStream* s, INT32 value)
+BOOL rdpei_write_2byte_signed(wStream* s, INT16 value)
 {
        BYTE byte;
        BOOL negative = FALSE;
 
+       if (!Stream_EnsureRemainingCapacity(s, 2))
+               return FALSE;
+
        if (value < 0)
        {
                negative = TRUE;
@@ -199,6 +205,9 @@ BOOL rdpei_write_4byte_unsigned(wStream* s, UINT32 value)
 {
        BYTE byte;
 
+       if (!Stream_EnsureRemainingCapacity(s, 4))
+               return FALSE;
+
        if (value <= 0x3FUL)
        {
                Stream_Write_UINT8(s, value);
@@ -300,6 +309,9 @@ BOOL rdpei_write_4byte_signed(wStream* s, INT32 value)
        BYTE byte;
        BOOL negative = FALSE;
 
+       if (!Stream_EnsureRemainingCapacity(s, 4))
+               return FALSE;
+
        if (value < 0)
        {
                negative = TRUE;
@@ -478,6 +490,9 @@ BOOL rdpei_write_8byte_unsigned(wStream* s, UINT64 value)
 {
        BYTE byte;
 
+       if (!Stream_EnsureRemainingCapacity(s, 8))
+               return FALSE;
+
        if (value <= 0x1FULL)
        {
                byte = value & 0x1F;
@@ -591,7 +606,7 @@ BOOL rdpei_write_8byte_unsigned(wStream* s, UINT64 value)
 
 void touch_event_reset(RDPINPUT_TOUCH_EVENT* event)
 {
-       int i;
+       UINT16 i;
 
        for (i = 0; i < event->frameCount; i++)
                touch_frame_reset(&event->frames[i]);
@@ -607,3 +622,22 @@ void touch_frame_reset(RDPINPUT_TOUCH_FRAME* frame)
        frame->contacts = NULL;
        frame->contactCount = 0;
 }
+
+void pen_event_reset(RDPINPUT_PEN_EVENT* event)
+{
+       UINT16 i;
+
+       for (i = 0; i < event->frameCount; i++)
+               pen_frame_reset(&event->frames[i]);
+
+       free(event->frames);
+       event->frames = NULL;
+       event->frameCount = 0;
+}
+
+void pen_frame_reset(RDPINPUT_PEN_FRAME* frame)
+{
+       free(frame->contacts);
+       frame->contacts = NULL;
+       frame->contactCount = 0;
+}
index 610767a..3a5362f 100644 (file)
@@ -33,13 +33,14 @@ enum
        EVENTID_TOUCH = 0x0003,
        EVENTID_SUSPEND_TOUCH = 0x0004,
        EVENTID_RESUME_TOUCH = 0x0005,
-       EVENTID_DISMISS_HOVERING_CONTACT = 0x0006
+       EVENTID_DISMISS_HOVERING_CONTACT = 0x0006,
+       EVENTID_PEN = 0x0008
 };
 
-BOOL rdpei_read_2byte_unsigned(wStream* s, UINT32* value);
-BOOL rdpei_write_2byte_unsigned(wStream* s, UINT32 value);
-BOOL rdpei_read_2byte_signed(wStream* s, INT32* value);
-BOOL rdpei_write_2byte_signed(wStream* s, INT32 value);
+BOOL rdpei_read_2byte_unsigned(wStream* s, UINT16* value);
+BOOL rdpei_write_2byte_unsigned(wStream* s, UINT16 value);
+BOOL rdpei_read_2byte_signed(wStream* s, INT16* value);
+BOOL rdpei_write_2byte_signed(wStream* s, INT16 value);
 BOOL rdpei_read_4byte_unsigned(wStream* s, UINT32* value);
 BOOL rdpei_write_4byte_unsigned(wStream* s, UINT32 value);
 BOOL rdpei_read_4byte_signed(wStream* s, INT32* value);
@@ -50,4 +51,7 @@ BOOL rdpei_write_8byte_unsigned(wStream* s, UINT64 value);
 void touch_event_reset(RDPINPUT_TOUCH_EVENT* event);
 void touch_frame_reset(RDPINPUT_TOUCH_FRAME* frame);
 
+void pen_event_reset(RDPINPUT_PEN_EVENT* event);
+void pen_frame_reset(RDPINPUT_PEN_FRAME* frame);
+
 #endif /* FREERDP_CHANNEL_RDPEI_COMMON_H */
index 81b93ce..9e3cf57 100644 (file)
@@ -55,6 +55,7 @@ struct _rdpei_server_private
        UINT16 currentMsgType;
 
        RDPINPUT_TOUCH_EVENT touchEvent;
+       RDPINPUT_PEN_EVENT penEvent;
 
        enum RdpEiState automataState;
 };
@@ -69,26 +70,22 @@ RdpeiServerContext* rdpei_server_context_new(HANDLE vcm)
 
        ret->priv = priv = calloc(1, sizeof(*ret->priv));
        if (!priv)
-               goto out_free;
+               goto fail;
 
        priv->inputStream = Stream_New(NULL, 256);
        if (!priv->inputStream)
-               goto out_free_priv;
+               goto fail;
 
        priv->outputStream = Stream_New(NULL, 200);
        if (!priv->inputStream)
-               goto out_free_input_stream;
+               goto fail;
 
        ret->vcm = vcm;
        rdpei_server_context_reset(ret);
        return ret;
 
-out_free_input_stream:
-       Stream_Free(priv->inputStream, TRUE);
-out_free_priv:
-       free(ret->priv);
-out_free:
-       free(ret);
+fail:
+       rdpei_server_context_free(ret);
        return NULL;
 }
 
@@ -145,10 +142,17 @@ void rdpei_server_context_reset(RdpeiServerContext* context)
 
 void rdpei_server_context_free(RdpeiServerContext* context)
 {
-       RdpeiServerPrivate* priv = context->priv;
-       if (priv->channelHandle != INVALID_HANDLE_VALUE)
-               WTSVirtualChannelClose(priv->channelHandle);
-       Stream_Free(priv->inputStream, TRUE);
+       RdpeiServerPrivate* priv;
+
+       if (!context)
+               return;
+       priv = context->priv;
+       if (priv)
+       {
+               if (priv->channelHandle != INVALID_HANDLE_VALUE)
+                       WTSVirtualChannelClose(priv->channelHandle);
+               Stream_Free(priv->inputStream, TRUE);
+       }
        free(priv);
        free(context);
 }
@@ -180,6 +184,8 @@ static UINT read_cs_ready_message(RdpeiServerContext* context, wStream* s)
        {
                case RDPINPUT_PROTOCOL_V10:
                case RDPINPUT_PROTOCOL_V101:
+               case RDPINPUT_PROTOCOL_V200:
+               case RDPINPUT_PROTOCOL_V300:
                        break;
                default:
                        WLog_ERR(TAG, "unhandled RPDEI protocol version 0x%" PRIx32 "", context->clientVersion);
@@ -201,6 +207,7 @@ static UINT read_cs_ready_message(RdpeiServerContext* context, wStream* s)
 static UINT read_touch_contact_data(RdpeiServerContext* context, wStream* s,
                                     RDPINPUT_CONTACT_DATA* contactData)
 {
+       WINPR_UNUSED(context);
        if (Stream_GetRemainingLength(s) < 1)
        {
                WLog_ERR(TAG, "Not enough data!");
@@ -246,6 +253,55 @@ static UINT read_touch_contact_data(RdpeiServerContext* context, wStream* s,
        return CHANNEL_RC_OK;
 }
 
+static UINT read_pen_contact(RdpeiServerContext* context, wStream* s,
+                             RDPINPUT_PEN_CONTACT* contactData)
+{
+       WINPR_UNUSED(context);
+       if (Stream_GetRemainingLength(s) < 1)
+       {
+               WLog_ERR(TAG, "Not enough data!");
+               return ERROR_INVALID_DATA;
+       }
+
+       Stream_Read_UINT8(s, contactData->deviceId);
+       if (!rdpei_read_2byte_unsigned(s, &contactData->fieldsPresent) ||
+           !rdpei_read_4byte_signed(s, &contactData->x) ||
+           !rdpei_read_4byte_signed(s, &contactData->y) ||
+           !rdpei_read_4byte_unsigned(s, &contactData->contactFlags))
+       {
+               WLog_ERR(TAG, "rdpei_read_ failed!");
+               return ERROR_INTERNAL_ERROR;
+       }
+
+       if (contactData->fieldsPresent & PEN_CONTACT_PENFLAGS_PRESENT)
+       {
+               if (!rdpei_read_4byte_unsigned(s, &contactData->penFlags))
+                       return ERROR_INVALID_DATA;
+       }
+       if (contactData->fieldsPresent & PEN_CONTACT_PRESSURE_PRESENT)
+       {
+               if (!rdpei_read_4byte_unsigned(s, &contactData->pressure))
+                       return ERROR_INVALID_DATA;
+       }
+       if (contactData->fieldsPresent & PEN_CONTACT_ROTATION_PRESENT)
+       {
+               if (!rdpei_read_2byte_unsigned(s, &contactData->rotation))
+                       return ERROR_INVALID_DATA;
+       }
+       if (contactData->fieldsPresent & PEN_CONTACT_TILTX_PRESENT)
+       {
+               if (!rdpei_read_2byte_signed(s, &contactData->tiltX))
+                       return ERROR_INVALID_DATA;
+       }
+       if (contactData->fieldsPresent & PEN_CONTACT_TILTY_PRESENT)
+       {
+               if (!rdpei_read_2byte_signed(s, &contactData->tiltY))
+                       return ERROR_INVALID_DATA;
+       }
+
+       return CHANNEL_RC_OK;
+}
+
 /**
  * Function description
  *
@@ -284,6 +340,39 @@ static UINT read_touch_frame(RdpeiServerContext* context, wStream* s, RDPINPUT_T
        return CHANNEL_RC_OK;
 }
 
+static UINT read_pen_frame(RdpeiServerContext* context, wStream* s, RDPINPUT_PEN_FRAME* frame)
+{
+       UINT32 i;
+       RDPINPUT_PEN_CONTACT* contact;
+       UINT error;
+
+       if (!rdpei_read_2byte_unsigned(s, &frame->contactCount) ||
+           !rdpei_read_8byte_unsigned(s, &frame->frameOffset))
+       {
+               WLog_ERR(TAG, "rdpei_read_ failed!");
+               return ERROR_INTERNAL_ERROR;
+       }
+
+       frame->contacts = contact = calloc(frame->contactCount, sizeof(RDPINPUT_CONTACT_DATA));
+       if (!frame->contacts)
+       {
+               WLog_ERR(TAG, "calloc failed!");
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
+       for (i = 0; i < frame->contactCount; i++, contact++)
+       {
+               if ((error = read_pen_contact(context, s, contact)))
+               {
+                       WLog_ERR(TAG, "read_touch_contact_data failed with error %" PRIu32 "!", error);
+                       frame->contactCount = i;
+                       pen_frame_reset(frame);
+                       return error;
+               }
+       }
+       return CHANNEL_RC_OK;
+}
+
 /**
  * Function description
  *
@@ -291,7 +380,7 @@ static UINT read_touch_frame(RdpeiServerContext* context, wStream* s, RDPINPUT_T
  */
 static UINT read_touch_event(RdpeiServerContext* context, wStream* s)
 {
-       UINT32 frameCount;
+       UINT16 frameCount;
        UINT32 i;
        RDPINPUT_TOUCH_EVENT* event = &context->priv->touchEvent;
        RDPINPUT_TOUCH_FRAME* frame;
@@ -331,6 +420,48 @@ out_cleanup:
        return error;
 }
 
+static UINT read_pen_event(RdpeiServerContext* context, wStream* s)
+{
+       UINT16 frameCount;
+       UINT32 i;
+       RDPINPUT_PEN_EVENT* event = &context->priv->penEvent;
+       RDPINPUT_PEN_FRAME* frame;
+       UINT error = CHANNEL_RC_OK;
+
+       if (!rdpei_read_4byte_unsigned(s, &event->encodeTime) ||
+           !rdpei_read_2byte_unsigned(s, &frameCount))
+       {
+               WLog_ERR(TAG, "rdpei_read_ failed!");
+               return ERROR_INTERNAL_ERROR;
+       }
+
+       event->frameCount = frameCount;
+       event->frames = frame = calloc(event->frameCount, sizeof(RDPINPUT_PEN_FRAME));
+       if (!event->frames)
+       {
+               WLog_ERR(TAG, "calloc failed!");
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
+       for (i = 0; i < frameCount; i++, frame++)
+       {
+               if ((error = read_pen_frame(context, s, frame)))
+               {
+                       WLog_ERR(TAG, "read_pen_frame failed with error %" PRIu32 "!", error);
+                       event->frameCount = i;
+                       goto out_cleanup;
+               }
+       }
+
+       IFCALLRET(context->onPenEvent, error, context, event);
+       if (error)
+               WLog_ERR(TAG, "context->onPenEvent failed with error %" PRIu32 "", error);
+
+out_cleanup:
+       pen_event_reset(event);
+       return error;
+}
+
 /**
  * Function description
  *
@@ -445,6 +576,13 @@ UINT rdpei_server_handle_messages(RdpeiServerContext* context)
                                return error;
                        }
                        break;
+               case EVENTID_PEN:
+                       if ((error = read_pen_event(context, s)))
+                       {
+                               WLog_ERR(TAG, "read_pen_event failed with error %" PRIu32 "", error);
+                               return error;
+                       }
+                       break;
                default:
                        WLog_ERR(TAG, "unexpected message type 0x%" PRIx16 "", priv->currentMsgType);
        }
@@ -460,7 +598,7 @@ UINT rdpei_server_handle_messages(RdpeiServerContext* context)
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version)
+UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version, UINT32 features)
 {
        ULONG written;
        RdpeiServerPrivate* priv = context->priv;
@@ -482,6 +620,8 @@ UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version)
        Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
        Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4);
        Stream_Write_UINT32(priv->outputStream, version);
+       if (version >= RDPINPUT_PROTOCOL_V300)
+               Stream_Write_UINT32(priv->outputStream, features);
 
        if (!WTSVirtualChannelWrite(priv->channelHandle, (PCHAR)Stream_Buffer(priv->outputStream),
                                    Stream_GetPosition(priv->outputStream), &written))
index de09ffc..edb6d6b 100644 (file)
 enum
 {
        RDPINPUT_PROTOCOL_V10 = 0x00010000,
-       RDPINPUT_PROTOCOL_V101 = 0x00010001
+       RDPINPUT_PROTOCOL_V101 = 0x00010001,
+       RDPINPUT_PROTOCOL_V200 = 0x00020000,
+       RDPINPUT_PROTOCOL_V300 = 0x00030000
 };
 
 /* Client Ready Flags */
-#define READY_FLAGS_SHOW_TOUCH_VISUALS 0x00000001
-#define READY_FLAGS_DISABLE_TIMESTAMP_INJECTION 0x00000002
+#define CS_READY_FLAGS_SHOW_TOUCH_VISUALS 0x00000001
+#define CS_READY_FLAGS_DISABLE_TIMESTAMP_INJECTION 0x00000002
+#define CS_READY_FLAGS_ENABLE_MULTIPEN_INJECTION 0x00000004
 
 #define CONTACT_DATA_CONTACTRECT_PRESENT 0x0001
 #define CONTACT_DATA_ORIENTATION_PRESENT 0x0002
 #define CONTACT_DATA_PRESSURE_PRESENT 0x0004
 
-#define CONTACT_FLAG_DOWN 0x0001
-#define CONTACT_FLAG_UPDATE 0x0002
-#define CONTACT_FLAG_UP 0x0004
-#define CONTACT_FLAG_INRANGE 0x0008
-#define CONTACT_FLAG_INCONTACT 0x0010
-#define CONTACT_FLAG_CANCELED 0x0020
+typedef enum
+{
+       PEN_CONTACT_PENFLAGS_PRESENT = 0x0001,
+       PEN_CONTACT_PRESSURE_PRESENT = 0x0002,
+       PEN_CONTACT_ROTATION_PRESENT = 0x0004,
+       PEN_CONTACT_TILTX_PRESENT = 0x0008,
+       PEN_CONTACT_TILTY_PRESENT = 0x0010
+} RDPINPUT_PEN_FIELDS_PRESENT;
+
+typedef enum
+{
+       CONTACT_FLAG_DOWN = 0x0001,
+       CONTACT_FLAG_UPDATE = 0x0002,
+       CONTACT_FLAG_UP = 0x0004,
+       CONTACT_FLAG_INRANGE = 0x0008,
+       CONTACT_FLAG_INCONTACT = 0x0010,
+       CONTACT_FLAG_CANCELED = 0x0020
+} RDPINPUT_CONTACT_FLAGS;
+
+typedef enum
+{
+       PEN_FLAG_BARREL_PRESSED = 0x0001,
+       PEN_FLAG_ERASER_PRESSED = 0x0002,
+       PEN_FLAG_INVERTED = 0x0004
+} RDPINPUT_PEN_FLAGS;
 
 /** @brief a contact point */
 struct _RDPINPUT_CONTACT_DATA
 {
        UINT32 contactId;
-       UINT32 fieldsPresent;
+       UINT16 fieldsPresent;
        INT32 x;
        INT32 y;
        UINT32 contactFlags;
-       INT32 contactRectLeft;
-       INT32 contactRectTop;
-       INT32 contactRectRight;
-       INT32 contactRectBottom;
+       INT16 contactRectLeft;
+       INT16 contactRectTop;
+       INT16 contactRectRight;
+       INT16 contactRectBottom;
        UINT32 orientation;
        UINT32 pressure;
 };
@@ -70,7 +92,7 @@ typedef struct _RDPINPUT_CONTACT_DATA RDPINPUT_CONTACT_DATA;
 /** @brief a frame containing contact points */
 struct _RDPINPUT_TOUCH_FRAME
 {
-       UINT32 contactCount;
+       UINT16 contactCount;
        UINT64 frameOffset;
        RDPINPUT_CONTACT_DATA* contacts;
 };
@@ -85,4 +107,35 @@ struct _RDPINPUT_TOUCH_EVENT
 };
 typedef struct _RDPINPUT_TOUCH_EVENT RDPINPUT_TOUCH_EVENT;
 
+struct _RDPINPUT_PEN_CONTACT
+{
+       UINT8 deviceId;
+       UINT16 fieldsPresent;
+       INT32 x;
+       INT32 y;
+       UINT32 contactFlags;
+       UINT32 penFlags;
+       UINT32 pressure;
+       UINT16 rotation;
+       INT16 tiltX;
+       INT16 tiltY;
+};
+typedef struct _RDPINPUT_PEN_CONTACT RDPINPUT_PEN_CONTACT;
+
+struct _RDPINPUT_PEN_FRAME
+{
+       UINT16 contactCount;
+       UINT64 frameOffset;
+       RDPINPUT_PEN_CONTACT* contacts;
+};
+typedef struct _RDPINPUT_PEN_FRAME RDPINPUT_PEN_FRAME;
+
+/** @brief a touch event with some frames*/
+struct _RDPINPUT_PEN_EVENT
+{
+       UINT32 encodeTime;
+       UINT16 frameCount;
+       RDPINPUT_PEN_FRAME* frames;
+};
+typedef struct _RDPINPUT_PEN_EVENT RDPINPUT_PEN_EVENT;
 #endif /* FREERDP_CHANNEL_RDPEI_H */
index 1be4ebd..7c350cf 100644 (file)
 
 typedef struct _rdpei_client_context RdpeiClientContext;
 
-typedef int (*pcRdpeiGetVersion)(RdpeiClientContext* context);
+typedef UINT32 (*pcRdpeiGetVersion)(RdpeiClientContext* context);
+typedef UINT32 (*pcRdpeiGetFeatures)(RdpeiClientContext* context);
 typedef UINT (*pcRdpeiAddContact)(RdpeiClientContext* context,
                                   const RDPINPUT_CONTACT_DATA* contact);
 
-typedef UINT (*pcRdpeiTouchBegin)(RdpeiClientContext* context, int externalId, int x, int y,
-                                  int* contactId);
-typedef UINT (*pcRdpeiTouchUpdate)(RdpeiClientContext* context, int externalId, int x, int y,
-                                   int* contactId);
-typedef UINT (*pcRdpeiTouchEnd)(RdpeiClientContext* context, int externalId, int x, int y,
-                                int* contactId);
+typedef UINT (*pcRdpeiTouchBegin)(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                                  INT32* contactId);
+typedef UINT (*pcRdpeiTouchUpdate)(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                                   INT32* contactId);
+typedef UINT (*pcRdpeiTouchEnd)(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
+                                INT32* contactId);
+
+typedef UINT (*pcRdpeiAddPen)(RdpeiClientContext* context, INT32 externalId,
+                              const RDPINPUT_PEN_CONTACT* contact);
+
+typedef UINT (*pcRdpeiPen)(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
+                           INT32 x, INT32 y, ...);
 
 typedef UINT (*pcRdpeiSuspendTouch)(RdpeiClientContext* context);
 typedef UINT (*pcRdpeiResumeTouch)(RdpeiClientContext* context);
@@ -50,6 +57,7 @@ struct _rdpei_client_context
        void* custom;
 
        pcRdpeiGetVersion GetVersion;
+       pcRdpeiGetFeatures GetFeatures;
 
        pcRdpeiAddContact AddContact;
 
@@ -57,6 +65,12 @@ struct _rdpei_client_context
        pcRdpeiTouchUpdate TouchUpdate;
        pcRdpeiTouchEnd TouchEnd;
 
+       pcRdpeiAddPen AddPen;
+
+       pcRdpeiPen PenBegin;
+       pcRdpeiPen PenUpdate;
+       pcRdpeiPen PenEnd;
+
        pcRdpeiSuspendTouch SuspendTouch;
        pcRdpeiResumeTouch ResumeTouch;
 };
index 9cd31a4..765809d 100644 (file)
@@ -41,7 +41,8 @@ struct _rdpei_server_context
 
        /** callbacks that can be set by the user */
        UINT (*onClientReady)(RdpeiServerContext* context);
-       UINT (*onTouchEvent)(RdpeiServerContext* context, RDPINPUT_TOUCH_EVENT* touchEvent);
+       UINT (*onTouchEvent)(RdpeiServerContext* context, const RDPINPUT_TOUCH_EVENT* touchEvent);
+       UINT (*onPenEvent)(RdpeiServerContext* context, const RDPINPUT_PEN_EVENT* penEvent);
        UINT (*onTouchReleased)(RdpeiServerContext* context, BYTE contactId);
 
        void* user_data; /* user data, useful for callbacks */
@@ -59,7 +60,8 @@ extern "C"
        FREERDP_API UINT rdpei_server_init(RdpeiServerContext* context);
        FREERDP_API UINT rdpei_server_handle_messages(RdpeiServerContext* context);
 
-       FREERDP_API UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version);
+       FREERDP_API UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version,
+                                                   UINT32 features);
        FREERDP_API UINT rdpei_server_suspend(RdpeiServerContext* context);
        FREERDP_API UINT rdpei_server_resume(RdpeiServerContext* context);