FreeRDP
server/rdpei_main.c
1 
23 #include <freerdp/config.h>
24 
25 #include <winpr/cast.h>
26 #include <winpr/crt.h>
27 #include <winpr/print.h>
28 #include <winpr/stream.h>
29 
30 #include "rdpei_main.h"
31 #include "../rdpei_common.h"
32 #include <freerdp/channels/rdpei.h>
33 #include <freerdp/server/rdpei.h>
34 
35 enum RdpEiState
36 {
37  STATE_INITIAL,
38  STATE_WAITING_CLIENT_READY,
39  STATE_WAITING_FRAME,
40  STATE_SUSPENDED,
41 };
42 
43 struct s_rdpei_server_private
44 {
45  HANDLE channelHandle;
46  HANDLE eventHandle;
47 
48  UINT32 expectedBytes;
49  BOOL waitingHeaders;
50  wStream* inputStream;
51  wStream* outputStream;
52 
53  UINT16 currentMsgType;
54 
55  RDPINPUT_TOUCH_EVENT touchEvent;
56  RDPINPUT_PEN_EVENT penEvent;
57 
58  enum RdpEiState automataState;
59 };
60 
61 RdpeiServerContext* rdpei_server_context_new(HANDLE vcm)
62 {
63  RdpeiServerContext* ret = calloc(1, sizeof(*ret));
64  RdpeiServerPrivate* priv = NULL;
65 
66  if (!ret)
67  return NULL;
68 
69  ret->priv = priv = calloc(1, sizeof(*ret->priv));
70  if (!priv)
71  goto fail;
72 
73  priv->inputStream = Stream_New(NULL, 256);
74  if (!priv->inputStream)
75  goto fail;
76 
77  priv->outputStream = Stream_New(NULL, 200);
78  if (!priv->inputStream)
79  goto fail;
80 
81  ret->vcm = vcm;
82  rdpei_server_context_reset(ret);
83  return ret;
84 
85 fail:
86  WINPR_PRAGMA_DIAG_PUSH
87  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
88  rdpei_server_context_free(ret);
89  WINPR_PRAGMA_DIAG_POP
90  return NULL;
91 }
92 
98 UINT rdpei_server_init(RdpeiServerContext* context)
99 {
100  void* buffer = NULL;
101  DWORD bytesReturned = 0;
102  RdpeiServerPrivate* priv = context->priv;
103  UINT32 channelId = 0;
104  BOOL status = TRUE;
105 
106  priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME,
107  WTS_CHANNEL_OPTION_DYNAMIC);
108  if (!priv->channelHandle)
109  {
110  WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
111  return CHANNEL_RC_INITIALIZATION_ERROR;
112  }
113 
114  channelId = WTSChannelGetIdByHandle(priv->channelHandle);
115 
116  IFCALLRET(context->onChannelIdAssigned, status, context, channelId);
117  if (!status)
118  {
119  WLog_ERR(TAG, "context->onChannelIdAssigned failed!");
120  goto out_close;
121  }
122 
123  if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer,
124  &bytesReturned) ||
125  (bytesReturned != sizeof(HANDLE)))
126  {
127  WLog_ERR(TAG,
128  "WTSVirtualChannelQuery failed or invalid invalid returned size(%" PRIu32 ")!",
129  bytesReturned);
130  if (buffer)
131  WTSFreeMemory(buffer);
132  goto out_close;
133  }
134  priv->eventHandle = *(HANDLE*)buffer;
135  WTSFreeMemory(buffer);
136 
137  return CHANNEL_RC_OK;
138 
139 out_close:
140  (void)WTSVirtualChannelClose(priv->channelHandle);
141  return CHANNEL_RC_INITIALIZATION_ERROR;
142 }
143 
144 void rdpei_server_context_reset(RdpeiServerContext* context)
145 {
146  RdpeiServerPrivate* priv = context->priv;
147 
148  priv->channelHandle = INVALID_HANDLE_VALUE;
149  priv->expectedBytes = RDPINPUT_HEADER_LENGTH;
150  priv->waitingHeaders = TRUE;
151  priv->automataState = STATE_INITIAL;
152  Stream_SetPosition(priv->inputStream, 0);
153 }
154 
155 void rdpei_server_context_free(RdpeiServerContext* context)
156 {
157  RdpeiServerPrivate* priv = NULL;
158 
159  if (!context)
160  return;
161  priv = context->priv;
162  if (priv)
163  {
164  if (priv->channelHandle != INVALID_HANDLE_VALUE)
165  (void)WTSVirtualChannelClose(priv->channelHandle);
166  Stream_Free(priv->inputStream, TRUE);
167  }
168  free(priv);
169  free(context);
170 }
171 
172 HANDLE rdpei_server_get_event_handle(RdpeiServerContext* context)
173 {
174  return context->priv->eventHandle;
175 }
176 
182 static UINT read_cs_ready_message(RdpeiServerContext* context, wStream* s)
183 {
184  UINT error = CHANNEL_RC_OK;
185  if (!Stream_CheckAndLogRequiredLength(TAG, s, 10))
186  return ERROR_INVALID_DATA;
187 
188  Stream_Read_UINT32(s, context->protocolFlags);
189  Stream_Read_UINT32(s, context->clientVersion);
190  Stream_Read_UINT16(s, context->maxTouchPoints);
191 
192  switch (context->clientVersion)
193  {
194  case RDPINPUT_PROTOCOL_V10:
195  case RDPINPUT_PROTOCOL_V101:
196  case RDPINPUT_PROTOCOL_V200:
197  case RDPINPUT_PROTOCOL_V300:
198  break;
199  default:
200  WLog_ERR(TAG, "unhandled RPDEI protocol version 0x%" PRIx32 "", context->clientVersion);
201  break;
202  }
203 
204  IFCALLRET(context->onClientReady, error, context);
205  if (error)
206  WLog_ERR(TAG, "context->onClientReady failed with error %" PRIu32 "", error);
207 
208  return error;
209 }
210 
216 static UINT read_touch_contact_data(RdpeiServerContext* context, wStream* s,
217  RDPINPUT_CONTACT_DATA* contactData)
218 {
219  WINPR_UNUSED(context);
220  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
221  return ERROR_INVALID_DATA;
222 
223  Stream_Read_UINT8(s, contactData->contactId);
224  if (!rdpei_read_2byte_unsigned(s, &contactData->fieldsPresent) ||
225  !rdpei_read_4byte_signed(s, &contactData->x) ||
226  !rdpei_read_4byte_signed(s, &contactData->y) ||
227  !rdpei_read_4byte_unsigned(s, &contactData->contactFlags))
228  {
229  WLog_ERR(TAG, "rdpei_read_ failed!");
230  return ERROR_INTERNAL_ERROR;
231  }
232 
233  if (contactData->fieldsPresent & CONTACT_DATA_CONTACTRECT_PRESENT)
234  {
235  if (!rdpei_read_2byte_signed(s, &contactData->contactRectLeft) ||
236  !rdpei_read_2byte_signed(s, &contactData->contactRectTop) ||
237  !rdpei_read_2byte_signed(s, &contactData->contactRectRight) ||
238  !rdpei_read_2byte_signed(s, &contactData->contactRectBottom))
239  {
240  WLog_ERR(TAG, "rdpei_read_ failed!");
241  return ERROR_INTERNAL_ERROR;
242  }
243  }
244 
245  if ((contactData->fieldsPresent & CONTACT_DATA_ORIENTATION_PRESENT) &&
246  !rdpei_read_4byte_unsigned(s, &contactData->orientation))
247  {
248  WLog_ERR(TAG, "rdpei_read_ failed!");
249  return ERROR_INTERNAL_ERROR;
250  }
251 
252  if ((contactData->fieldsPresent & CONTACT_DATA_PRESSURE_PRESENT) &&
253  !rdpei_read_4byte_unsigned(s, &contactData->pressure))
254  {
255  WLog_ERR(TAG, "rdpei_read_ failed!");
256  return ERROR_INTERNAL_ERROR;
257  }
258 
259  return CHANNEL_RC_OK;
260 }
261 
262 static UINT read_pen_contact(RdpeiServerContext* context, wStream* s,
263  RDPINPUT_PEN_CONTACT* contactData)
264 {
265  WINPR_UNUSED(context);
266  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
267  return ERROR_INVALID_DATA;
268 
269  Stream_Read_UINT8(s, contactData->deviceId);
270  if (!rdpei_read_2byte_unsigned(s, &contactData->fieldsPresent) ||
271  !rdpei_read_4byte_signed(s, &contactData->x) ||
272  !rdpei_read_4byte_signed(s, &contactData->y) ||
273  !rdpei_read_4byte_unsigned(s, &contactData->contactFlags))
274  {
275  WLog_ERR(TAG, "rdpei_read_ failed!");
276  return ERROR_INTERNAL_ERROR;
277  }
278 
279  if (contactData->fieldsPresent & RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT)
280  {
281  if (!rdpei_read_4byte_unsigned(s, &contactData->penFlags))
282  return ERROR_INVALID_DATA;
283  }
284  if (contactData->fieldsPresent & RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT)
285  {
286  if (!rdpei_read_4byte_unsigned(s, &contactData->pressure))
287  return ERROR_INVALID_DATA;
288  }
289  if (contactData->fieldsPresent & RDPINPUT_PEN_CONTACT_ROTATION_PRESENT)
290  {
291  if (!rdpei_read_2byte_unsigned(s, &contactData->rotation))
292  return ERROR_INVALID_DATA;
293  }
294  if (contactData->fieldsPresent & RDPINPUT_PEN_CONTACT_TILTX_PRESENT)
295  {
296  if (!rdpei_read_2byte_signed(s, &contactData->tiltX))
297  return ERROR_INVALID_DATA;
298  }
299  if (contactData->fieldsPresent & RDPINPUT_PEN_CONTACT_TILTY_PRESENT)
300  {
301  if (!rdpei_read_2byte_signed(s, &contactData->tiltY))
302  return ERROR_INVALID_DATA;
303  }
304 
305  return CHANNEL_RC_OK;
306 }
307 
313 static UINT read_touch_frame(RdpeiServerContext* context, wStream* s, RDPINPUT_TOUCH_FRAME* frame)
314 {
315  RDPINPUT_CONTACT_DATA* contact = NULL;
316  UINT error = 0;
317 
318  if (!rdpei_read_2byte_unsigned(s, &frame->contactCount) ||
319  !rdpei_read_8byte_unsigned(s, &frame->frameOffset))
320  {
321  WLog_ERR(TAG, "rdpei_read_ failed!");
322  return ERROR_INTERNAL_ERROR;
323  }
324 
325  frame->contacts = contact = calloc(frame->contactCount, sizeof(RDPINPUT_CONTACT_DATA));
326  if (!frame->contacts)
327  {
328  WLog_ERR(TAG, "calloc failed!");
329  return CHANNEL_RC_NO_MEMORY;
330  }
331 
332  for (UINT32 i = 0; i < frame->contactCount; i++, contact++)
333  {
334  if ((error = read_touch_contact_data(context, s, contact)))
335  {
336  WLog_ERR(TAG, "read_touch_contact_data failed with error %" PRIu32 "!", error);
337  frame->contactCount = WINPR_ASSERTING_INT_CAST(UINT16, i);
338  touch_frame_reset(frame);
339  return error;
340  }
341  }
342  return CHANNEL_RC_OK;
343 }
344 
345 static UINT read_pen_frame(RdpeiServerContext* context, wStream* s, RDPINPUT_PEN_FRAME* frame)
346 {
347  RDPINPUT_PEN_CONTACT* contact = NULL;
348  UINT error = 0;
349 
350  if (!rdpei_read_2byte_unsigned(s, &frame->contactCount) ||
351  !rdpei_read_8byte_unsigned(s, &frame->frameOffset))
352  {
353  WLog_ERR(TAG, "rdpei_read_ failed!");
354  return ERROR_INTERNAL_ERROR;
355  }
356 
357  frame->contacts = contact = calloc(frame->contactCount, sizeof(RDPINPUT_PEN_CONTACT));
358  if (!frame->contacts)
359  {
360  WLog_ERR(TAG, "calloc failed!");
361  return CHANNEL_RC_NO_MEMORY;
362  }
363 
364  for (UINT32 i = 0; i < frame->contactCount; i++, contact++)
365  {
366  if ((error = read_pen_contact(context, s, contact)))
367  {
368  WLog_ERR(TAG, "read_touch_contact_data failed with error %" PRIu32 "!", error);
369  frame->contactCount = WINPR_ASSERTING_INT_CAST(UINT16, i);
370 
371  pen_frame_reset(frame);
372  return error;
373  }
374  }
375  return CHANNEL_RC_OK;
376 }
377 
383 static UINT read_touch_event(RdpeiServerContext* context, wStream* s)
384 {
385  UINT16 frameCount = 0;
386  RDPINPUT_TOUCH_EVENT* event = &context->priv->touchEvent;
387  RDPINPUT_TOUCH_FRAME* frame = NULL;
388  UINT error = CHANNEL_RC_OK;
389 
390  if (!rdpei_read_4byte_unsigned(s, &event->encodeTime) ||
391  !rdpei_read_2byte_unsigned(s, &frameCount))
392  {
393  WLog_ERR(TAG, "rdpei_read_ failed!");
394  return ERROR_INTERNAL_ERROR;
395  }
396 
397  event->frameCount = frameCount;
398  event->frames = frame = calloc(event->frameCount, sizeof(RDPINPUT_TOUCH_FRAME));
399  if (!event->frames)
400  {
401  WLog_ERR(TAG, "calloc failed!");
402  return CHANNEL_RC_NO_MEMORY;
403  }
404 
405  for (UINT32 i = 0; i < frameCount; i++, frame++)
406  {
407  if ((error = read_touch_frame(context, s, frame)))
408  {
409  WLog_ERR(TAG, "read_touch_contact_data failed with error %" PRIu32 "!", error);
410  event->frameCount = WINPR_ASSERTING_INT_CAST(UINT16, i);
411 
412  goto out_cleanup;
413  }
414  }
415 
416  IFCALLRET(context->onTouchEvent, error, context, event);
417  if (error)
418  WLog_ERR(TAG, "context->onTouchEvent failed with error %" PRIu32 "", error);
419 
420 out_cleanup:
421  touch_event_reset(event);
422  return error;
423 }
424 
425 static UINT read_pen_event(RdpeiServerContext* context, wStream* s)
426 {
427  UINT16 frameCount = 0;
428  RDPINPUT_PEN_EVENT* event = &context->priv->penEvent;
429  RDPINPUT_PEN_FRAME* frame = NULL;
430  UINT error = CHANNEL_RC_OK;
431 
432  if (!rdpei_read_4byte_unsigned(s, &event->encodeTime) ||
433  !rdpei_read_2byte_unsigned(s, &frameCount))
434  {
435  WLog_ERR(TAG, "rdpei_read_ failed!");
436  return ERROR_INTERNAL_ERROR;
437  }
438 
439  event->frameCount = frameCount;
440  event->frames = frame = calloc(event->frameCount, sizeof(RDPINPUT_PEN_FRAME));
441  if (!event->frames)
442  {
443  WLog_ERR(TAG, "calloc failed!");
444  return CHANNEL_RC_NO_MEMORY;
445  }
446 
447  for (UINT32 i = 0; i < frameCount; i++, frame++)
448  {
449  if ((error = read_pen_frame(context, s, frame)))
450  {
451  WLog_ERR(TAG, "read_pen_frame failed with error %" PRIu32 "!", error);
452  event->frameCount = WINPR_ASSERTING_INT_CAST(UINT16, i);
453 
454  goto out_cleanup;
455  }
456  }
457 
458  IFCALLRET(context->onPenEvent, error, context, event);
459  if (error)
460  WLog_ERR(TAG, "context->onPenEvent failed with error %" PRIu32 "", error);
461 
462 out_cleanup:
463  pen_event_reset(event);
464  return error;
465 }
466 
472 static UINT read_dismiss_hovering_contact(RdpeiServerContext* context, wStream* s)
473 {
474  BYTE contactId = 0;
475  UINT error = CHANNEL_RC_OK;
476 
477  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
478  return ERROR_INVALID_DATA;
479 
480  Stream_Read_UINT8(s, contactId);
481 
482  IFCALLRET(context->onTouchReleased, error, context, contactId);
483  if (error)
484  WLog_ERR(TAG, "context->onTouchReleased failed with error %" PRIu32 "", error);
485 
486  return error;
487 }
488 
494 UINT rdpei_server_handle_messages(RdpeiServerContext* context)
495 {
496  DWORD bytesReturned = 0;
497  RdpeiServerPrivate* priv = context->priv;
498  wStream* s = priv->inputStream;
499  UINT error = CHANNEL_RC_OK;
500 
501  if (!WTSVirtualChannelRead(priv->channelHandle, 0, Stream_Pointer(s), priv->expectedBytes,
502  &bytesReturned))
503  {
504  if (GetLastError() == ERROR_NO_DATA)
505  return ERROR_READ_FAULT;
506 
507  WLog_DBG(TAG, "channel connection closed");
508  return CHANNEL_RC_OK;
509  }
510  priv->expectedBytes -= bytesReturned;
511  Stream_Seek(s, bytesReturned);
512 
513  if (priv->expectedBytes)
514  return CHANNEL_RC_OK;
515 
516  Stream_SealLength(s);
517  Stream_SetPosition(s, 0);
518 
519  if (priv->waitingHeaders)
520  {
521  UINT32 pduLen = 0;
522 
523  /* header case */
524  Stream_Read_UINT16(s, priv->currentMsgType);
525  Stream_Read_UINT16(s, pduLen);
526 
527  if (pduLen < RDPINPUT_HEADER_LENGTH)
528  {
529  WLog_ERR(TAG, "invalid pduLength %" PRIu32 "", pduLen);
530  return ERROR_INVALID_DATA;
531  }
532  priv->expectedBytes = pduLen - RDPINPUT_HEADER_LENGTH;
533  priv->waitingHeaders = FALSE;
534  Stream_SetPosition(s, 0);
535  if (priv->expectedBytes)
536  {
537  if (!Stream_EnsureCapacity(s, priv->expectedBytes))
538  {
539  WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
540  return CHANNEL_RC_NO_MEMORY;
541  }
542  return CHANNEL_RC_OK;
543  }
544  }
545 
546  /* when here we have the header + the body */
547  switch (priv->currentMsgType)
548  {
549  case EVENTID_CS_READY:
550  if (priv->automataState != STATE_WAITING_CLIENT_READY)
551  {
552  WLog_ERR(TAG, "not expecting a CS_READY packet in this state(%d)",
553  priv->automataState);
554  return ERROR_INVALID_STATE;
555  }
556 
557  if ((error = read_cs_ready_message(context, s)))
558  {
559  WLog_ERR(TAG, "read_cs_ready_message failed with error %" PRIu32 "", error);
560  return error;
561  }
562  break;
563 
564  case EVENTID_TOUCH:
565  if ((error = read_touch_event(context, s)))
566  {
567  WLog_ERR(TAG, "read_touch_event failed with error %" PRIu32 "", error);
568  return error;
569  }
570  break;
571  case EVENTID_DISMISS_HOVERING_CONTACT:
572  if ((error = read_dismiss_hovering_contact(context, s)))
573  {
574  WLog_ERR(TAG, "read_dismiss_hovering_contact failed with error %" PRIu32 "", error);
575  return error;
576  }
577  break;
578  case EVENTID_PEN:
579  if ((error = read_pen_event(context, s)))
580  {
581  WLog_ERR(TAG, "read_pen_event failed with error %" PRIu32 "", error);
582  return error;
583  }
584  break;
585  default:
586  WLog_ERR(TAG, "unexpected message type 0x%" PRIx16 "", priv->currentMsgType);
587  }
588 
589  Stream_SetPosition(s, 0);
590  priv->waitingHeaders = TRUE;
591  priv->expectedBytes = RDPINPUT_HEADER_LENGTH;
592  return error;
593 }
594 
600 UINT rdpei_server_send_sc_ready(RdpeiServerContext* context, UINT32 version, UINT32 features)
601 {
602  ULONG written = 0;
603  RdpeiServerPrivate* priv = context->priv;
604  UINT32 pduLen = 4;
605 
606  if (priv->automataState != STATE_INITIAL)
607  {
608  WLog_ERR(TAG, "called from unexpected state %d", priv->automataState);
609  return ERROR_INVALID_STATE;
610  }
611 
612  Stream_SetPosition(priv->outputStream, 0);
613 
614  if (version >= RDPINPUT_PROTOCOL_V300)
615  pduLen += 4;
616 
617  if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen))
618  {
619  WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
620  return CHANNEL_RC_NO_MEMORY;
621  }
622 
623  Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
624  Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen);
625  Stream_Write_UINT32(priv->outputStream, version);
626  if (version >= RDPINPUT_PROTOCOL_V300)
627  Stream_Write_UINT32(priv->outputStream, features);
628 
629  const size_t pos = Stream_GetPosition(priv->outputStream);
630 
631  WINPR_ASSERT(pos <= UINT32_MAX);
632  if (!WTSVirtualChannelWrite(priv->channelHandle, Stream_BufferAs(priv->outputStream, char),
633  (ULONG)pos, &written))
634  {
635  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
636  return ERROR_INTERNAL_ERROR;
637  }
638 
639  priv->automataState = STATE_WAITING_CLIENT_READY;
640  return CHANNEL_RC_OK;
641 }
642 
648 UINT rdpei_server_suspend(RdpeiServerContext* context)
649 {
650  ULONG written = 0;
651  RdpeiServerPrivate* priv = context->priv;
652 
653  switch (priv->automataState)
654  {
655  case STATE_SUSPENDED:
656  WLog_ERR(TAG, "already suspended");
657  return CHANNEL_RC_OK;
658  case STATE_WAITING_FRAME:
659  break;
660  default:
661  WLog_ERR(TAG, "called from unexpected state %d", priv->automataState);
662  return ERROR_INVALID_STATE;
663  }
664 
665  Stream_SetPosition(priv->outputStream, 0);
666  if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH))
667  {
668  WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
669  return CHANNEL_RC_NO_MEMORY;
670  }
671 
672  Stream_Write_UINT16(priv->outputStream, EVENTID_SUSPEND_TOUCH);
673  Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH);
674 
675  const size_t pos = Stream_GetPosition(priv->outputStream);
676 
677  WINPR_ASSERT(pos <= UINT32_MAX);
678  if (!WTSVirtualChannelWrite(priv->channelHandle, Stream_BufferAs(priv->outputStream, char),
679  (ULONG)pos, &written))
680  {
681  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
682  return ERROR_INTERNAL_ERROR;
683  }
684 
685  priv->automataState = STATE_SUSPENDED;
686  return CHANNEL_RC_OK;
687 }
688 
694 UINT rdpei_server_resume(RdpeiServerContext* context)
695 {
696  ULONG written = 0;
697  RdpeiServerPrivate* priv = context->priv;
698 
699  switch (priv->automataState)
700  {
701  case STATE_WAITING_FRAME:
702  WLog_ERR(TAG, "not suspended");
703  return CHANNEL_RC_OK;
704  case STATE_SUSPENDED:
705  break;
706  default:
707  WLog_ERR(TAG, "called from unexpected state %d", priv->automataState);
708  return ERROR_INVALID_STATE;
709  }
710 
711  Stream_SetPosition(priv->outputStream, 0);
712  if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH))
713  {
714  WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
715  return CHANNEL_RC_NO_MEMORY;
716  }
717 
718  Stream_Write_UINT16(priv->outputStream, EVENTID_RESUME_TOUCH);
719  Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH);
720 
721  const size_t pos = Stream_GetPosition(priv->outputStream);
722 
723  WINPR_ASSERT(pos <= UINT32_MAX);
724  if (!WTSVirtualChannelWrite(priv->channelHandle, Stream_BufferAs(priv->outputStream, char),
725  (ULONG)pos, &written))
726  {
727  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
728  return ERROR_INTERNAL_ERROR;
729  }
730 
731  priv->automataState = STATE_WAITING_FRAME;
732  return CHANNEL_RC_OK;
733 }
a touch event with some frames
a touch event with some frames
a frame containing contact points