22#include <freerdp/config.h> 
   30#include <winpr/cast.h> 
   31#include <winpr/synch.h> 
   32#include <winpr/thread.h> 
   33#include <winpr/stream.h> 
   34#include <winpr/sysinfo.h> 
   35#include <winpr/cmdline.h> 
   36#include <winpr/collections.h> 
   38#include <freerdp/addin.h> 
   39#include <freerdp/freerdp.h> 
   40#include <freerdp/client/channels.h> 
   42#include "rdpei_common.h" 
   44#include "rdpei_main.h" 
   46#define RDPEI_TAG CHANNELS_TAG("rdpei.client") 
   68#define MAX_CONTACTS 64 
   69#define MAX_PEN_CONTACTS 4 
   75  RdpeiClientContext* context;
 
   79  UINT16 maxTouchContacts;
 
   80  UINT64 currentFrameTime;
 
   81  UINT64 previousFrameTime;
 
   84  UINT64 currentPenFrameTime;
 
   85  UINT64 previousPenFrameTime;
 
   86  UINT16 maxPenContacts;
 
   90  rdpContext* rdpcontext;
 
   95  UINT64 lastPollEventTime;
 
  107#ifdef WITH_DEBUG_RDPEI 
  108static const char* rdpei_eventid_string(UINT16 event)
 
  112    case EVENTID_SC_READY:
 
  113      return "EVENTID_SC_READY";
 
  114    case EVENTID_CS_READY:
 
  115      return "EVENTID_CS_READY";
 
  117      return "EVENTID_TOUCH";
 
  118    case EVENTID_SUSPEND_TOUCH:
 
  119      return "EVENTID_SUSPEND_TOUCH";
 
  120    case EVENTID_RESUME_TOUCH:
 
  121      return "EVENTID_RESUME_TOUCH";
 
  122    case EVENTID_DISMISS_HOVERING_CONTACT:
 
  123      return "EVENTID_DISMISS_HOVERING_CONTACT";
 
  125      return "EVENTID_PEN";
 
  127      return "EVENTID_UNKNOWN";
 
  134  for (UINT16 i = 0; i < rdpei->maxTouchContacts; i++)
 
  138    if (!contactPoint->active && active)
 
  140    else if (!contactPoint->active && !active)
 
  142      contactPoint->contactId = i;
 
  143      contactPoint->externalId = externalId;
 
  144      contactPoint->active = TRUE;
 
  147    else if (contactPoint->externalId == externalId)
 
  160static UINT rdpei_add_frame(RdpeiClientContext* context)
 
  162  RDPEI_PLUGIN* rdpei = NULL;
 
  166  if (!context || !context->handle)
 
  167    return ERROR_INTERNAL_ERROR;
 
  169  rdpei = (RDPEI_PLUGIN*)context->handle;
 
  170  frame.contacts = contacts;
 
  172  for (UINT16 i = 0; i < rdpei->maxTouchContacts; i++)
 
  177    if (contactPoint->dirty)
 
  179      contacts[frame.contactCount] = *contact;
 
  180      rdpei->contactPoints[i].dirty = FALSE;
 
  181      frame.contactCount++;
 
  183    else if (contactPoint->active)
 
  185      if (contact->contactFlags & RDPINPUT_CONTACT_FLAG_DOWN)
 
  187        contact->contactFlags = RDPINPUT_CONTACT_FLAG_UPDATE;
 
  188        contact->contactFlags |= RDPINPUT_CONTACT_FLAG_INRANGE;
 
  189        contact->contactFlags |= RDPINPUT_CONTACT_FLAG_INCONTACT;
 
  192      contacts[frame.contactCount] = *contact;
 
  193      frame.contactCount++;
 
  195    if (contact->contactFlags & RDPINPUT_CONTACT_FLAG_UP)
 
  197      contactPoint->active = FALSE;
 
  198      contactPoint->externalId = 0;
 
  199      contactPoint->contactId = 0;
 
  203  if (frame.contactCount > 0)
 
  205    UINT error = rdpei_send_frame(context, &frame);
 
  206    if (error != CHANNEL_RC_OK)
 
  208      WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  209                 "rdpei_send_frame failed with error %" PRIu32 
"!", error);
 
  213  return CHANNEL_RC_OK;
 
  226  if (!callback || !s || !callback->channel || !callback->channel->Write)
 
  227    return ERROR_INTERNAL_ERROR;
 
  229  if (pduLength > UINT32_MAX)
 
  230    return ERROR_INVALID_PARAMETER;
 
  232  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  234    return ERROR_INTERNAL_ERROR;
 
  236  Stream_SetPosition(s, 0);
 
  237  Stream_Write_UINT16(s, eventId);   
 
  238  Stream_Write_UINT32(s, (UINT32)pduLength); 
 
  239  Stream_SetPosition(s, Stream_Length(s));
 
  240  status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
 
  242#ifdef WITH_DEBUG_RDPEI 
  243  WLog_Print(rdpei->base.log, WLOG_DEBUG,
 
  244             "rdpei_send_pdu: eventId: %" PRIu16 
" (%s) length: %" PRIu32 
" status: %" PRIu32 
"",
 
  245             eventId, rdpei_eventid_string(eventId), pduLength, status);
 
  253    return ERROR_INTERNAL_ERROR;
 
  255  if (!rdpei_write_2byte_unsigned(s, frame->contactCount))
 
  256    return ERROR_OUTOFMEMORY;
 
  257  if (!rdpei_write_8byte_unsigned(s, frame->frameOffset))
 
  258    return ERROR_OUTOFMEMORY;
 
  259  for (UINT16 x = 0; x < frame->contactCount; x++)
 
  263    if (!Stream_EnsureRemainingCapacity(s, 1))
 
  264      return ERROR_OUTOFMEMORY;
 
  265    Stream_Write_UINT8(s, contact->deviceId);
 
  266    if (!rdpei_write_2byte_unsigned(s, contact->fieldsPresent))
 
  267      return ERROR_OUTOFMEMORY;
 
  268    if (!rdpei_write_4byte_signed(s, contact->x))
 
  269      return ERROR_OUTOFMEMORY;
 
  270    if (!rdpei_write_4byte_signed(s, contact->y))
 
  271      return ERROR_OUTOFMEMORY;
 
  272    if (!rdpei_write_4byte_unsigned(s, contact->contactFlags))
 
  273      return ERROR_OUTOFMEMORY;
 
  274    if (contact->fieldsPresent & RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT)
 
  276      if (!rdpei_write_4byte_unsigned(s, contact->penFlags))
 
  277        return ERROR_OUTOFMEMORY;
 
  279    if (contact->fieldsPresent & RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT)
 
  281      if (!rdpei_write_4byte_unsigned(s, contact->pressure))
 
  282        return ERROR_OUTOFMEMORY;
 
  284    if (contact->fieldsPresent & RDPINPUT_PEN_CONTACT_ROTATION_PRESENT)
 
  286      if (!rdpei_write_2byte_unsigned(s, contact->rotation))
 
  287        return ERROR_OUTOFMEMORY;
 
  289    if (contact->fieldsPresent & RDPINPUT_PEN_CONTACT_TILTX_PRESENT)
 
  291      if (!rdpei_write_2byte_signed(s, contact->tiltX))
 
  292        return ERROR_OUTOFMEMORY;
 
  294    if (contact->fieldsPresent & RDPINPUT_PEN_CONTACT_TILTY_PRESENT)
 
  296      if (!rdpei_write_2byte_signed(s, contact->tiltY))
 
  297        return ERROR_OUTOFMEMORY;
 
  300  return CHANNEL_RC_OK;
 
  309  WINPR_ASSERT(callback);
 
  311  if (frameOffset > UINT32_MAX)
 
  312    return ERROR_INVALID_PARAMETER;
 
  313  if (count > UINT16_MAX)
 
  314    return ERROR_INVALID_PARAMETER;
 
  316  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  318    return ERROR_INTERNAL_ERROR;
 
  320  if (!frames || (count == 0))
 
  321    return ERROR_INTERNAL_ERROR;
 
  323  s = Stream_New(NULL, 64);
 
  327    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"Stream_New failed!");
 
  328    return CHANNEL_RC_NO_MEMORY;
 
  331  Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
 
  336  rdpei_write_4byte_unsigned(s,
 
  337                             (UINT32)frameOffset); 
 
  338  rdpei_write_2byte_unsigned(s, (UINT16)count);    
 
  340  for (
size_t x = 0; x < count; x++)
 
  342    if ((status = rdpei_write_pen_frame(s, &frames[x])))
 
  344      WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  345                 "rdpei_write_pen_frame failed with error %" PRIu32 
"!", status);
 
  346      Stream_Free(s, TRUE);
 
  350  Stream_SealLength(s);
 
  352  status = rdpei_send_pdu(callback, s, EVENTID_PEN, Stream_Length(s));
 
  353  Stream_Free(s, TRUE);
 
  357static UINT rdpei_send_pen_frame(RdpeiClientContext* context, 
RDPINPUT_PEN_FRAME* frame)
 
  359  const UINT64 currentTime = GetTickCount64();
 
  360  RDPEI_PLUGIN* rdpei = NULL;
 
  365    return ERROR_INTERNAL_ERROR;
 
  366  rdpei = (RDPEI_PLUGIN*)context->handle;
 
  367  if (!rdpei || !rdpei->base.listener_callback)
 
  368    return ERROR_INTERNAL_ERROR;
 
  369  if (!rdpei || !rdpei->rdpcontext)
 
  370    return ERROR_INTERNAL_ERROR;
 
  372    return CHANNEL_RC_OK;
 
  374  callback = rdpei->base.listener_callback->channel_callback;
 
  377    return CHANNEL_RC_OK;
 
  379  if (!rdpei->previousPenFrameTime && !rdpei->currentPenFrameTime)
 
  381    rdpei->currentPenFrameTime = currentTime;
 
  382    frame->frameOffset = 0;
 
  386    rdpei->currentPenFrameTime = currentTime;
 
  387    frame->frameOffset = rdpei->currentPenFrameTime - rdpei->previousPenFrameTime;
 
  390  const size_t off = WINPR_ASSERTING_INT_CAST(
size_t, frame->frameOffset);
 
  391  error = rdpei_send_pen_event_pdu(callback, off, frame, 1);
 
  395  rdpei->previousPenFrameTime = rdpei->currentPenFrameTime;
 
  399static UINT rdpei_add_pen_frame(RdpeiClientContext* context)
 
  401  RDPEI_PLUGIN* rdpei = NULL;
 
  405  if (!context || !context->handle)
 
  406    return ERROR_INTERNAL_ERROR;
 
  408  rdpei = (RDPEI_PLUGIN*)context->handle;
 
  410  penFrame.contacts = penContacts;
 
  412  for (UINT16 i = 0; i < rdpei->maxPenContacts; i++)
 
  418      penContacts[penFrame.contactCount++] = contact->data;
 
  419      contact->dirty = FALSE;
 
  421    else if (contact->active)
 
  423      if (contact->data.contactFlags & RDPINPUT_CONTACT_FLAG_DOWN)
 
  425        contact->data.contactFlags = RDPINPUT_CONTACT_FLAG_UPDATE;
 
  426        contact->data.contactFlags |= RDPINPUT_CONTACT_FLAG_INRANGE;
 
  427        contact->data.contactFlags |= RDPINPUT_CONTACT_FLAG_INCONTACT;
 
  430      penContacts[penFrame.contactCount++] = contact->data;
 
  432    if (contact->data.contactFlags & RDPINPUT_CONTACT_FLAG_CANCELED)
 
  434      contact->externalId = 0;
 
  435      contact->active = FALSE;
 
  439  if (penFrame.contactCount > 0)
 
  440    return rdpei_send_pen_frame(context, &penFrame);
 
  441  return CHANNEL_RC_OK;
 
  444static UINT rdpei_update(wLog* log, RdpeiClientContext* context)
 
  446  UINT error = rdpei_add_frame(context);
 
  447  if (error != CHANNEL_RC_OK)
 
  449    WLog_Print(log, WLOG_ERROR, 
"rdpei_add_frame failed with error %" PRIu32 
"!", error);
 
  453  return rdpei_add_pen_frame(context);
 
  456static BOOL rdpei_poll_run_unlocked(rdpContext* context, 
void* userdata)
 
  458  RDPEI_PLUGIN* rdpei = userdata;
 
  460  WINPR_ASSERT(context);
 
  462  const UINT64 now = GetTickCount64();
 
  465  if ((now < rdpei->lastPollEventTime) || (now - rdpei->lastPollEventTime < 20ULL))
 
  468  rdpei->lastPollEventTime = now;
 
  470  const UINT error = rdpei_update(rdpei->base.log, rdpei->context);
 
  472  (void)ResetEvent(rdpei->event);
 
  474  if (error != CHANNEL_RC_OK)
 
  476    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"rdpei_add_frame failed with error %" PRIu32 
"!",
 
  478    setChannelError(context, error, 
"rdpei_add_frame reported an error");
 
  485static BOOL rdpei_poll_run(rdpContext* context, 
void* userdata)
 
  487  RDPEI_PLUGIN* rdpei = userdata;
 
  490  EnterCriticalSection(&rdpei->lock);
 
  491  BOOL rc = rdpei_poll_run_unlocked(context, userdata);
 
  492  LeaveCriticalSection(&rdpei->lock);
 
  496static DWORD WINAPI rdpei_periodic_update(LPVOID arg)
 
  499  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)arg;
 
  500  UINT error = CHANNEL_RC_OK;
 
  501  RdpeiClientContext* context = NULL;
 
  505    error = ERROR_INVALID_PARAMETER;
 
  509  context = rdpei->context;
 
  513    error = ERROR_INVALID_PARAMETER;
 
  517  while (rdpei->running)
 
  519    status = WaitForSingleObject(rdpei->event, 20);
 
  521    if (status == WAIT_FAILED)
 
  523      error = GetLastError();
 
  524      WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  525                 "WaitForMultipleObjects failed with error %" PRIu32 
"!", error);
 
  529    if (!rdpei_poll_run(rdpei->rdpcontext, rdpei))
 
  530      error = ERROR_INTERNAL_ERROR;
 
  535  if (error && rdpei && rdpei->rdpcontext)
 
  536    setChannelError(rdpei->rdpcontext, error, 
"rdpei_schedule_thread reported an error");
 
  539    rdpei->running = FALSE;
 
  555  UINT32 pduLength = 0;
 
  556  RDPEI_PLUGIN* rdpei = NULL;
 
  558  if (!callback || !callback->plugin)
 
  559    return ERROR_INTERNAL_ERROR;
 
  560  rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  562  flags |= CS_READY_FLAGS_SHOW_TOUCH_VISUALS & rdpei->context->clientFeaturesMask;
 
  563  if (rdpei->version > RDPINPUT_PROTOCOL_V10)
 
  564    flags |= CS_READY_FLAGS_DISABLE_TIMESTAMP_INJECTION & rdpei->context->clientFeaturesMask;
 
  565  if (rdpei->features & SC_READY_MULTIPEN_INJECTION_SUPPORTED)
 
  566    flags |= CS_READY_FLAGS_ENABLE_MULTIPEN_INJECTION & rdpei->context->clientFeaturesMask;
 
  568  pduLength = RDPINPUT_HEADER_LENGTH + 10;
 
  569  s = Stream_New(NULL, pduLength);
 
  573    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"Stream_New failed!");
 
  574    return CHANNEL_RC_NO_MEMORY;
 
  577  Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
 
  578  Stream_Write_UINT32(s, flags);                   
 
  579  Stream_Write_UINT32(s, rdpei->version);          
 
  580  Stream_Write_UINT16(s, rdpei->maxTouchContacts); 
 
  581  Stream_SealLength(s);
 
  582  status = rdpei_send_pdu(callback, s, EVENTID_CS_READY, pduLength);
 
  583  Stream_Free(s, TRUE);
 
  587#if defined(WITH_DEBUG_RDPEI) 
  588static void rdpei_print_contact_flags(wLog* log, UINT32 contactFlags)
 
  590  if (contactFlags & RDPINPUT_CONTACT_FLAG_DOWN)
 
  591    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_DOWN");
 
  593  if (contactFlags & RDPINPUT_CONTACT_FLAG_UPDATE)
 
  594    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_UPDATE");
 
  596  if (contactFlags & RDPINPUT_CONTACT_FLAG_UP)
 
  597    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_UP");
 
  599  if (contactFlags & RDPINPUT_CONTACT_FLAG_INRANGE)
 
  600    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_INRANGE");
 
  602  if (contactFlags & RDPINPUT_CONTACT_FLAG_INCONTACT)
 
  603    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_INCONTACT");
 
  605  if (contactFlags & RDPINPUT_CONTACT_FLAG_CANCELED)
 
  606    WLog_Print(log, WLOG_DEBUG, 
" RDPINPUT_CONTACT_FLAG_CANCELED");
 
  610static INT16 bounded(INT32 val)
 
  629    return ERROR_INTERNAL_ERROR;
 
  630#ifdef WITH_DEBUG_RDPEI 
  631  WLog_Print(log, WLOG_DEBUG, 
"contactCount: %" PRIu32 
"", frame->contactCount);
 
  632  WLog_Print(log, WLOG_DEBUG, 
"frameOffset: 0x%016" PRIX64 
"", frame->frameOffset);
 
  634  rdpei_write_2byte_unsigned(s,
 
  635                             frame->contactCount); 
 
  640  rdpei_write_8byte_unsigned(s, frame->frameOffset *
 
  643  if (!Stream_EnsureRemainingCapacity(s, (
size_t)frame->contactCount * 64))
 
  645    WLog_Print(log, WLOG_ERROR, 
"Stream_EnsureRemainingCapacity failed!");
 
  646    return CHANNEL_RC_NO_MEMORY;
 
  649  for (UINT32 index = 0; index < frame->contactCount; index++)
 
  651    contact = &frame->contacts[index];
 
  652    contact->fieldsPresent |= CONTACT_DATA_CONTACTRECT_PRESENT;
 
  653    contact->contactRectLeft = bounded(contact->x - rectSize);
 
  654    contact->contactRectTop = bounded(contact->y - rectSize);
 
  655    contact->contactRectRight = bounded(contact->x + rectSize);
 
  656    contact->contactRectBottom = bounded(contact->y + rectSize);
 
  657#ifdef WITH_DEBUG_RDPEI 
  658    WLog_Print(log, WLOG_DEBUG, 
"contact[%" PRIu32 
"].contactId: %" PRIu32 
"", index,
 
  660    WLog_Print(log, WLOG_DEBUG, 
"contact[%" PRIu32 
"].fieldsPresent: %" PRIu32 
"", index,
 
  661               contact->fieldsPresent);
 
  662    WLog_Print(log, WLOG_DEBUG, 
"contact[%" PRIu32 
"].x: %" PRId32 
"", index, contact->x);
 
  663    WLog_Print(log, WLOG_DEBUG, 
"contact[%" PRIu32 
"].y: %" PRId32 
"", index, contact->y);
 
  664    WLog_Print(log, WLOG_DEBUG, 
"contact[%" PRIu32 
"].contactFlags: 0x%08" PRIX32 
"", index,
 
  665               contact->contactFlags);
 
  666    rdpei_print_contact_flags(log, contact->contactFlags);
 
  669        s, WINPR_ASSERTING_INT_CAST(uint8_t, contact->contactId)); 
 
  671    rdpei_write_2byte_unsigned(s, contact->fieldsPresent);
 
  672    rdpei_write_4byte_signed(s, contact->x); 
 
  673    rdpei_write_4byte_signed(s, contact->y); 
 
  675    rdpei_write_4byte_unsigned(s, contact->contactFlags);
 
  677    if (contact->fieldsPresent & CONTACT_DATA_CONTACTRECT_PRESENT)
 
  680      rdpei_write_2byte_signed(s, contact->contactRectLeft);
 
  682      rdpei_write_2byte_signed(s, contact->contactRectTop);
 
  684      rdpei_write_2byte_signed(s, contact->contactRectRight);
 
  686      rdpei_write_2byte_signed(s, contact->contactRectBottom);
 
  689    if (contact->fieldsPresent & CONTACT_DATA_ORIENTATION_PRESENT)
 
  692      rdpei_write_4byte_unsigned(s, contact->orientation);
 
  695    if (contact->fieldsPresent & CONTACT_DATA_PRESSURE_PRESENT)
 
  698      rdpei_write_4byte_unsigned(s, contact->pressure);
 
  702  return CHANNEL_RC_OK;
 
  715  WINPR_ASSERT(callback);
 
  717  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  718  if (!rdpei || !rdpei->rdpcontext)
 
  719    return ERROR_INTERNAL_ERROR;
 
  721    return CHANNEL_RC_OK;
 
  724    return ERROR_INTERNAL_ERROR;
 
  726  size_t pduLength = 64ULL + (64ULL * frame->contactCount);
 
  727  wStream* s = Stream_New(NULL, pduLength);
 
  731    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"Stream_New failed!");
 
  732    return CHANNEL_RC_NO_MEMORY;
 
  735  Stream_Seek(s, RDPINPUT_HEADER_LENGTH);
 
  740  rdpei_write_4byte_unsigned(
 
  741      s, (UINT32)frame->frameOffset); 
 
  742  rdpei_write_2byte_unsigned(s, 1);   
 
  744  status = rdpei_write_touch_frame(rdpei->base.log, s, frame);
 
  747    WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  748               "rdpei_write_touch_frame failed with error %" PRIu32 
"!", status);
 
  749    Stream_Free(s, TRUE);
 
  753  Stream_SealLength(s);
 
  754  status = rdpei_send_pdu(callback, s, EVENTID_TOUCH, Stream_Length(s));
 
  755  Stream_Free(s, TRUE);
 
  767  UINT32 protocolVersion = 0;
 
  769  if (!callback || !callback->plugin)
 
  770    return ERROR_INTERNAL_ERROR;
 
  772  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  774  if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 4))
 
  775    return ERROR_INVALID_DATA;
 
  776  Stream_Read_UINT32(s, protocolVersion); 
 
  778  if (protocolVersion >= RDPINPUT_PROTOCOL_V300)
 
  780    if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 4))
 
  781      return ERROR_INVALID_DATA;
 
  784  if (Stream_GetRemainingLength(s) >= 4)
 
  785    Stream_Read_UINT32(s, features);
 
  787  if (rdpei->version > protocolVersion)
 
  788    rdpei->version = protocolVersion;
 
  789  rdpei->features = features;
 
  791  if (protocolVersion > RDPINPUT_PROTOCOL_V300)
 
  793    WLog_Print(rdpei->base.log, WLOG_WARN,
 
  794               "Unknown [MS-RDPEI] protocolVersion: 0x%08" PRIX32 
"", protocolVersion);
 
  797  return CHANNEL_RC_OK;
 
  807  UINT error = CHANNEL_RC_OK;
 
  811  if (!callback || !callback->plugin)
 
  812    return ERROR_INTERNAL_ERROR;
 
  814  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  815  RdpeiClientContext* context = rdpei->context;
 
  817    return ERROR_INTERNAL_ERROR;
 
  819  IFCALLRET(context->SuspendTouch, error, context);
 
  822    WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  823               "rdpei->SuspendTouch failed with error %" PRIu32 
"!", error);
 
  835  UINT error = CHANNEL_RC_OK;
 
  837    return ERROR_INTERNAL_ERROR;
 
  839  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  841    return ERROR_INTERNAL_ERROR;
 
  843  RdpeiClientContext* context = (RdpeiClientContext*)callback->plugin->pInterface;
 
  845    return ERROR_INTERNAL_ERROR;
 
  847  IFCALLRET(context->ResumeTouch, error, context);
 
  850    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"rdpei->ResumeTouch failed with error %" PRIu32 
"!",
 
  864  UINT32 pduLength = 0;
 
  868    return ERROR_INTERNAL_ERROR;
 
  870  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  872    return ERROR_INTERNAL_ERROR;
 
  874  if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 6))
 
  875    return ERROR_INVALID_DATA;
 
  877  Stream_Read_UINT16(s, eventId);   
 
  878  Stream_Read_UINT32(s, pduLength); 
 
  879#ifdef WITH_DEBUG_RDPEI 
  880  WLog_Print(rdpei->base.log, WLOG_DEBUG,
 
  881             "rdpei_recv_pdu: eventId: %" PRIu16 
" (%s) length: %" PRIu32 
"", eventId,
 
  882             rdpei_eventid_string(eventId), pduLength);
 
  885  if ((pduLength < 6) || !Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, pduLength - 6))
 
  886    return ERROR_INVALID_DATA;
 
  890    case EVENTID_SC_READY:
 
  891      if ((error = rdpei_recv_sc_ready_pdu(callback, s)))
 
  893        WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  894                   "rdpei_recv_sc_ready_pdu failed with error %" PRIu32 
"!", error);
 
  898      if ((error = rdpei_send_cs_ready_pdu(callback)))
 
  900        WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  901                   "rdpei_send_cs_ready_pdu failed with error %" PRIu32 
"!", error);
 
  907    case EVENTID_SUSPEND_TOUCH:
 
  908      if ((error = rdpei_recv_suspend_touch_pdu(callback, s)))
 
  910        WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  911                   "rdpei_recv_suspend_touch_pdu failed with error %" PRIu32 
"!", error);
 
  917    case EVENTID_RESUME_TOUCH:
 
  918      if ((error = rdpei_recv_resume_touch_pdu(callback, s)))
 
  920        WLog_Print(rdpei->base.log, WLOG_ERROR,
 
  921                   "rdpei_recv_resume_touch_pdu failed with error %" PRIu32 
"!", error);
 
  931  return CHANNEL_RC_OK;
 
  939static UINT rdpei_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, 
wStream* data)
 
  942  return rdpei_recv_pdu(callback, data);
 
  950static UINT rdpei_on_close(IWTSVirtualChannelCallback* pChannelCallback)
 
  955    RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
 
  956    if (rdpei && rdpei->base.listener_callback)
 
  958      if (rdpei->base.listener_callback->channel_callback == callback)
 
  959        rdpei->base.listener_callback->channel_callback = NULL;
 
  963  return CHANNEL_RC_OK;
 
  970static UINT32 rdpei_get_version(RdpeiClientContext* context)
 
  972  RDPEI_PLUGIN* rdpei = NULL;
 
  973  if (!context || !context->handle)
 
  975  rdpei = (RDPEI_PLUGIN*)context->handle;
 
  976  return rdpei->version;
 
  979static UINT32 rdpei_get_features(RdpeiClientContext* context)
 
  981  RDPEI_PLUGIN* rdpei = NULL;
 
  982  if (!context || !context->handle)
 
  984  rdpei = (RDPEI_PLUGIN*)context->handle;
 
  985  return rdpei->features;
 
  995  UINT64 currentTime = GetTickCount64();
 
  996  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
 
 1000  callback = rdpei->base.listener_callback->channel_callback;
 
 1004    return CHANNEL_RC_OK;
 
 1006  if (!rdpei->previousFrameTime && !rdpei->currentFrameTime)
 
 1008    rdpei->currentFrameTime = currentTime;
 
 1009    frame->frameOffset = 0;
 
 1013    rdpei->currentFrameTime = currentTime;
 
 1014    frame->frameOffset = rdpei->currentFrameTime - rdpei->previousFrameTime;
 
 1017  if ((error = rdpei_send_touch_event_pdu(callback, frame)))
 
 1019    WLog_Print(rdpei->base.log, WLOG_ERROR,
 
 1020               "rdpei_send_touch_event_pdu failed with error %" PRIu32 
"!", error);
 
 1024  rdpei->previousFrameTime = rdpei->currentFrameTime;
 
 1036  RDPEI_PLUGIN* rdpei = NULL;
 
 1037  if (!context || !contact || !context->handle)
 
 1038    return ERROR_INTERNAL_ERROR;
 
 1040  rdpei = (RDPEI_PLUGIN*)context->handle;
 
 1042  EnterCriticalSection(&rdpei->lock);
 
 1043  contactPoint = &rdpei->contactPoints[contact->contactId];
 
 1045  if (contactPoint->dirty && contactPoint->data.contactFlags != contact->contactFlags)
 
 1046    rdpei_add_frame(context);
 
 1048  contactPoint->data = *contact;
 
 1049  contactPoint->dirty = TRUE;
 
 1050  (void)SetEvent(rdpei->event);
 
 1051  LeaveCriticalSection(&rdpei->lock);
 
 1053  return CHANNEL_RC_OK;
 
 1056static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, UINT32 contactFlags,
 
 1057                                INT32 x, INT32 y, INT32* contactId, UINT32 fieldFlags, va_list ap)
 
 1059  INT64 contactIdlocal = -1;
 
 1061  UINT error = CHANNEL_RC_OK;
 
 1063  if (!context || !contactId || !context->handle)
 
 1064    return ERROR_INTERNAL_ERROR;
 
 1066  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
 
 1068  EnterCriticalSection(&rdpei->lock);
 
 1069  const BOOL begin = (contactFlags & RDPINPUT_CONTACT_FLAG_DOWN) != 0;
 
 1070  contactPoint = rdpei_contact(rdpei, externalId, !begin);
 
 1072    contactIdlocal = contactPoint->contactId;
 
 1073  LeaveCriticalSection(&rdpei->lock);
 
 1075  if (contactIdlocal > UINT32_MAX)
 
 1076    return ERROR_INVALID_PARAMETER;
 
 1078  if (contactIdlocal >= 0)
 
 1083    contact.contactId = (UINT32)contactIdlocal;
 
 1084    contact.contactFlags = contactFlags;
 
 1085    contact.fieldsPresent = WINPR_ASSERTING_INT_CAST(UINT16, fieldFlags);
 
 1087    if (fieldFlags & CONTACT_DATA_CONTACTRECT_PRESENT)
 
 1089      INT32 val = va_arg(ap, INT32);
 
 1090      contact.contactRectLeft = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1092      val = va_arg(ap, INT32);
 
 1093      contact.contactRectTop = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1095      val = va_arg(ap, INT32);
 
 1096      contact.contactRectRight = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1098      val = va_arg(ap, INT32);
 
 1099      contact.contactRectBottom = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1101    if (fieldFlags & CONTACT_DATA_ORIENTATION_PRESENT)
 
 1103      UINT32 p = va_arg(ap, UINT32);
 
 1106        WLog_Print(rdpei->base.log, WLOG_WARN,
 
 1107                   "TouchContact %" PRId64 
": Invalid orientation value %" PRIu32
 
 1108                   "degree, clamping to 359 degree",
 
 1112      contact.orientation = p;
 
 1114    if (fieldFlags & CONTACT_DATA_PRESSURE_PRESENT)
 
 1116      UINT32 p = va_arg(ap, UINT32);
 
 1119        WLog_Print(rdpei->base.log, WLOG_WARN,
 
 1120                   "TouchContact %" PRId64 
": Invalid pressure value %" PRIu32
 
 1121                   ", clamping to 1024",
 
 1125      contact.pressure = p;
 
 1128    error = context->AddContact(context, &contact);
 
 1132    *contactId = (INT32)contactIdlocal;
 
 1141static UINT rdpei_touch_begin(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
 
 1146  rc = rdpei_touch_process(context, externalId,
 
 1147                           RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
 
 1148                               RDPINPUT_CONTACT_FLAG_INCONTACT,
 
 1149                           x, y, contactId, 0, ap);
 
 1158static UINT rdpei_touch_update(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
 
 1163  rc = rdpei_touch_process(context, externalId,
 
 1164                           RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
 
 1165                               RDPINPUT_CONTACT_FLAG_INCONTACT,
 
 1166                           x, y, contactId, 0, ap);
 
 1175static UINT rdpei_touch_end(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
 
 1180  error = rdpei_touch_process(context, externalId,
 
 1181                              RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
 
 1182                                  RDPINPUT_CONTACT_FLAG_INCONTACT,
 
 1183                              x, y, contactId, 0, ap);
 
 1184  if (error != CHANNEL_RC_OK)
 
 1187      rdpei_touch_process(context, externalId, RDPINPUT_CONTACT_FLAG_UP, x, y, contactId, 0, ap);
 
 1196static UINT rdpei_touch_cancel(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
 
 1201  rc = rdpei_touch_process(context, externalId,
 
 1202                           RDPINPUT_CONTACT_FLAG_UP | RDPINPUT_CONTACT_FLAG_CANCELED, x, y,
 
 1207static UINT rdpei_touch_raw_event(RdpeiClientContext* context, INT32 externalId, INT32 x, INT32 y,
 
 1208                                  INT32* contactId, UINT32 flags, UINT32 fieldFlags, ...)
 
 1212  va_start(ap, fieldFlags);
 
 1213  rc = rdpei_touch_process(context, externalId, flags, x, y, contactId, fieldFlags, ap);
 
 1218static UINT rdpei_touch_raw_event_va(RdpeiClientContext* context, INT32 externalId, INT32 x,
 
 1219                                     INT32 y, INT32* contactId, UINT32 flags, UINT32 fieldFlags,
 
 1222  return rdpei_touch_process(context, externalId, flags, x, y, contactId, fieldFlags, args);
 
 1231  for (UINT32 x = 0; x < rdpei->maxPenContacts; x++)
 
 1236      if (contact->active)
 
 1238        if (contact->externalId == externalId)
 
 1244      if (!contact->active)
 
 1246        contact->externalId = externalId;
 
 1247        contact->active = TRUE;
 
 1255static UINT rdpei_add_pen(RdpeiClientContext* context, INT32 externalId,
 
 1258  RDPEI_PLUGIN* rdpei = NULL;
 
 1261  if (!context || !contact || !context->handle)
 
 1262    return ERROR_INTERNAL_ERROR;
 
 1264  rdpei = (RDPEI_PLUGIN*)context->handle;
 
 1266  EnterCriticalSection(&rdpei->lock);
 
 1267  contactPoint = rdpei_pen_contact(rdpei, externalId, TRUE);
 
 1270    contactPoint->data = *contact;
 
 1271    contactPoint->dirty = TRUE;
 
 1272    (void)SetEvent(rdpei->event);
 
 1274  LeaveCriticalSection(&rdpei->lock);
 
 1276  return CHANNEL_RC_OK;
 
 1279static UINT rdpei_pen_process(RdpeiClientContext* context, INT32 externalId, UINT32 contactFlags,
 
 1280                              UINT32 fieldFlags, INT32 x, INT32 y, va_list ap)
 
 1283  RDPEI_PLUGIN* rdpei = NULL;
 
 1284  UINT error = CHANNEL_RC_OK;
 
 1286  if (!context || !context->handle)
 
 1287    return ERROR_INTERNAL_ERROR;
 
 1289  rdpei = (RDPEI_PLUGIN*)context->handle;
 
 1291  EnterCriticalSection(&rdpei->lock);
 
 1293  contactPoint = rdpei_pen_contact(rdpei, externalId, TRUE);
 
 1296    const UINT32 mask = RDPINPUT_CONTACT_FLAG_INRANGE;
 
 1297    if ((contactFlags & mask) == mask)
 
 1299      contactPoint = rdpei_pen_contact(rdpei, externalId, FALSE);
 
 1302  LeaveCriticalSection(&rdpei->lock);
 
 1303  if (contactPoint != NULL)
 
 1309    contact.fieldsPresent = WINPR_ASSERTING_INT_CAST(UINT16, fieldFlags);
 
 1311    contact.contactFlags = contactFlags;
 
 1312    if (fieldFlags & RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT)
 
 1314      const UINT32 val = va_arg(ap, UINT32);
 
 1315      contact.penFlags = WINPR_ASSERTING_INT_CAST(UINT16, val);
 
 1317    if (fieldFlags & RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT)
 
 1319      const UINT32 val = va_arg(ap, UINT32);
 
 1320      contact.pressure = WINPR_ASSERTING_INT_CAST(UINT16, val);
 
 1322    if (fieldFlags & RDPINPUT_PEN_CONTACT_ROTATION_PRESENT)
 
 1324      const UINT32 val = va_arg(ap, UINT32);
 
 1325      contact.rotation = WINPR_ASSERTING_INT_CAST(UINT16, val);
 
 1327    if (fieldFlags & RDPINPUT_PEN_CONTACT_TILTX_PRESENT)
 
 1329      const INT32 val = va_arg(ap, INT32);
 
 1330      contact.tiltX = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1332    if (fieldFlags & RDPINPUT_PEN_CONTACT_TILTY_PRESENT)
 
 1334      const INT32 val = va_arg(ap, INT32);
 
 1335      WINPR_ASSERT((val >= INT16_MIN) && (val <= INT16_MAX));
 
 1336      contact.tiltY = WINPR_ASSERTING_INT_CAST(INT16, val);
 
 1339    error = context->AddPen(context, externalId, &contact);
 
 1350static UINT rdpei_pen_begin(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
 
 1351                            INT32 x, INT32 y, ...)
 
 1357  error = rdpei_pen_process(context, externalId,
 
 1358                            RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
 
 1359                                RDPINPUT_CONTACT_FLAG_INCONTACT,
 
 1360                            fieldFlags, x, y, ap);
 
 1371static UINT rdpei_pen_update(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
 
 1372                             INT32 x, INT32 y, ...)
 
 1378  error = rdpei_pen_process(context, externalId,
 
 1379                            RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
 
 1380                                RDPINPUT_CONTACT_FLAG_INCONTACT,
 
 1381                            fieldFlags, x, y, ap);
 
 1391static UINT rdpei_pen_end(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags, INT32 x,
 
 1397  error = rdpei_pen_process(context, externalId,
 
 1398                            RDPINPUT_CONTACT_FLAG_UP | RDPINPUT_CONTACT_FLAG_INRANGE, fieldFlags,
 
 1409static UINT rdpei_pen_hover_begin(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
 
 1410                                  INT32 x, INT32 y, ...)
 
 1416  error = rdpei_pen_process(context, externalId,
 
 1417                            RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE,
 
 1418                            fieldFlags, x, y, ap);
 
 1429static UINT rdpei_pen_hover_update(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
 
 1430                                   INT32 x, INT32 y, ...)
 
 1436  error = rdpei_pen_process(context, externalId,
 
 1437                            RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE,
 
 1438                            fieldFlags, x, y, ap);
 
 1449static UINT rdpei_pen_hover_cancel(RdpeiClientContext* context, INT32 externalId, UINT32 fieldFlags,
 
 1450                                   INT32 x, INT32 y, ...)
 
 1456  error = rdpei_pen_process(context, externalId,
 
 1457                            RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED,
 
 1458                            fieldFlags, x, y, ap);
 
 1464static UINT rdpei_pen_raw_event(RdpeiClientContext* context, INT32 externalId, UINT32 contactFlags,
 
 1465                                UINT32 fieldFlags, INT32 x, INT32 y, ...)
 
 1471  error = rdpei_pen_process(context, externalId, contactFlags, fieldFlags, x, y, ap);
 
 1476static UINT rdpei_pen_raw_event_va(RdpeiClientContext* context, INT32 externalId,
 
 1477                                   UINT32 contactFlags, UINT32 fieldFlags, INT32 x, INT32 y,
 
 1480  return rdpei_pen_process(context, externalId, contactFlags, fieldFlags, x, y, args);
 
 1483static UINT init_plugin_cb(
GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
 
 1485  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)base;
 
 1488  WINPR_UNUSED(settings);
 
 1490  rdpei->version = RDPINPUT_PROTOCOL_V300;
 
 1491  rdpei->currentFrameTime = 0;
 
 1492  rdpei->previousFrameTime = 0;
 
 1493  rdpei->maxTouchContacts = MAX_CONTACTS;
 
 1494  rdpei->maxPenContacts = MAX_PEN_CONTACTS;
 
 1495  rdpei->rdpcontext = rcontext;
 
 1497  WINPR_ASSERT(rdpei->base.log);
 
 1499  InitializeCriticalSection(&rdpei->lock);
 
 1500  rdpei->event = CreateEventA(NULL, TRUE, FALSE, NULL);
 
 1503    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"calloc failed!");
 
 1504    return CHANNEL_RC_NO_MEMORY;
 
 1507  RdpeiClientContext* context = (RdpeiClientContext*)calloc(1, 
sizeof(RdpeiClientContext));
 
 1510    WLog_Print(rdpei->base.log, WLOG_ERROR, 
"calloc failed!");
 
 1511    return CHANNEL_RC_NO_MEMORY;
 
 1514  context->clientFeaturesMask = UINT32_MAX;
 
 1515  context->handle = (
void*)rdpei;
 
 1516  context->GetVersion = rdpei_get_version;
 
 1517  context->GetFeatures = rdpei_get_features;
 
 1518  context->AddContact = rdpei_add_contact;
 
 1519  context->TouchBegin = rdpei_touch_begin;
 
 1520  context->TouchUpdate = rdpei_touch_update;
 
 1521  context->TouchEnd = rdpei_touch_end;
 
 1522  context->TouchCancel = rdpei_touch_cancel;
 
 1523  context->TouchRawEvent = rdpei_touch_raw_event;
 
 1524  context->TouchRawEventVA = rdpei_touch_raw_event_va;
 
 1525  context->AddPen = rdpei_add_pen;
 
 1526  context->PenBegin = rdpei_pen_begin;
 
 1527  context->PenUpdate = rdpei_pen_update;
 
 1528  context->PenEnd = rdpei_pen_end;
 
 1529  context->PenHoverBegin = rdpei_pen_hover_begin;
 
 1530  context->PenHoverUpdate = rdpei_pen_hover_update;
 
 1531  context->PenHoverCancel = rdpei_pen_hover_cancel;
 
 1532  context->PenRawEvent = rdpei_pen_raw_event;
 
 1533  context->PenRawEventVA = rdpei_pen_raw_event_va;
 
 1535  rdpei->context = context;
 
 1536  rdpei->base.iface.pInterface = (
void*)context;
 
 1542    rdpei->running = TRUE;
 
 1544    rdpei->thread = CreateThread(NULL, 0, rdpei_periodic_update, rdpei, 0, NULL);
 
 1547      WLog_Print(rdpei->base.log, WLOG_ERROR, 
"calloc failed!");
 
 1548      return CHANNEL_RC_NO_MEMORY;
 
 1553    if (!freerdp_client_channel_register(rdpei->rdpcontext->channels, rdpei->event,
 
 1554                                         rdpei_poll_run, rdpei))
 
 1555      return ERROR_INTERNAL_ERROR;
 
 1558  return CHANNEL_RC_OK;
 
 1563  RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)base;
 
 1564  WINPR_ASSERT(rdpei);
 
 1566  rdpei->running = FALSE;
 
 1568    (void)SetEvent(rdpei->event);
 
 1572    (void)WaitForSingleObject(rdpei->thread, INFINITE);
 
 1573    (void)CloseHandle(rdpei->thread);
 
 1576  if (rdpei->event && !rdpei->async)
 
 1577    (void)freerdp_client_channel_unregister(rdpei->rdpcontext->channels, rdpei->event);
 
 1580    (void)CloseHandle(rdpei->event);
 
 1582  DeleteCriticalSection(&rdpei->lock);
 
 1583  free(rdpei->context);
 
 1586static const IWTSVirtualChannelCallback geometry_callbacks = { rdpei_on_data_received,
 
 1588                                                             rdpei_on_close, NULL };
 
 1595FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpei_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
 
 1597  return freerdp_generic_DVCPluginEntry(pEntryPoints, RDPEI_TAG, RDPEI_DVC_CHANNEL_NAME,
 
 1599                                        &geometry_callbacks, init_plugin_cb, terminate_plugin_cb);
 
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.