FreeRDP
gfxredir_main.c
1 
20 #include <freerdp/config.h>
21 
22 #include "gfxredir_main.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <winpr/crt.h>
28 #include <winpr/synch.h>
29 #include <winpr/thread.h>
30 #include <winpr/stream.h>
31 #include <winpr/sysinfo.h>
32 #include <freerdp/channels/wtsvc.h>
33 #include <freerdp/channels/log.h>
34 
35 #include <freerdp/server/gfxredir.h>
36 #include "../gfxredir_common.h"
37 
38 #define TAG CHANNELS_TAG("gfxredir.server")
39 
45 static UINT gfxredir_recv_legacy_caps_pdu(wStream* s, GfxRedirServerContext* context)
46 {
47  UINT32 error = CHANNEL_RC_OK;
49 
50  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
51  return ERROR_INVALID_DATA;
52 
53  Stream_Read_UINT16(s, pdu.version); /* version (2 bytes) */
54 
55  if (context)
56  IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu);
57 
58  return error;
59 }
60 
66 static UINT gfxredir_recv_caps_advertise_pdu(wStream* s, UINT32 length,
67  GfxRedirServerContext* context)
68 {
69  UINT32 error = CHANNEL_RC_OK;
71 
72  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
73  return ERROR_INVALID_DATA;
74 
75  pdu.length = length;
76  Stream_GetPointer(s, pdu.caps);
77  Stream_Seek(s, length);
78 
79  if (!context->GraphicsRedirectionCapsAdvertise)
80  {
81  WLog_ERR(TAG, "server does not support CapsAdvertise PDU!");
82  return ERROR_NOT_SUPPORTED;
83  }
84  else if (context)
85  {
86  IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu);
87  }
88 
89  return error;
90 }
91 
97 static UINT gfxredir_recv_present_buffer_ack_pdu(wStream* s, GfxRedirServerContext* context)
98 {
99  UINT32 error = CHANNEL_RC_OK;
101 
102  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
103  return ERROR_INVALID_DATA;
104 
105  Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
106  Stream_Read_UINT64(s, pdu.presentId); /* presentId (8 bytes) */
107 
108  if (context)
109  {
110  IFCALLRET(context->PresentBufferAck, error, context, &pdu);
111  }
112 
113  return error;
114 }
115 
121 static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context, wStream* s)
122 {
123  UINT error = CHANNEL_RC_OK;
124  size_t beg, end;
125  GFXREDIR_HEADER header;
126  beg = Stream_GetPosition(s);
127 
128  if ((error = gfxredir_read_header(s, &header)))
129  {
130  WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error);
131  return error;
132  }
133 
134  switch (header.cmdId)
135  {
136  case GFXREDIR_CMDID_LEGACY_CAPS:
137  if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
138  WLog_ERR(TAG,
139  "gfxredir_recv_legacy_caps_pdu "
140  "failed with error %" PRIu32 "!",
141  error);
142 
143  break;
144 
145  case GFXREDIR_CMDID_CAPS_ADVERTISE:
146  if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
147  WLog_ERR(TAG,
148  "gfxredir_recv_caps_advertise "
149  "failed with error %" PRIu32 "!",
150  error);
151  break;
152 
153  case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
154  if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
155  WLog_ERR(TAG,
156  "gfxredir_recv_present_buffer_ask_pdu "
157  "failed with error %" PRIu32 "!",
158  error);
159  break;
160 
161  default:
162  error = CHANNEL_RC_BAD_PROC;
163  WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId);
164  break;
165  }
166 
167  end = Stream_GetPosition(s);
168 
169  if (end != (beg + header.length))
170  {
171  WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32 "", end,
172  (beg + header.length));
173  Stream_SetPosition(s, (beg + header.length));
174  }
175 
176  return error;
177 }
178 
184 static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
185 {
186  DWORD BytesReturned;
187  void* buffer;
188  UINT ret = CHANNEL_RC_OK;
189  GfxRedirServerPrivate* priv = context->priv;
190  wStream* s = priv->input_stream;
191 
192  /* Check whether the dynamic channel is ready */
193  if (!priv->isReady)
194  {
195  if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
196  &BytesReturned) == FALSE)
197  {
198  if (GetLastError() == ERROR_NO_DATA)
199  return ERROR_NO_DATA;
200 
201  WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
202  return ERROR_INTERNAL_ERROR;
203  }
204 
205  priv->isReady = *((BOOL*)buffer);
206  WTSFreeMemory(buffer);
207  }
208 
209  /* Consume channel event only after the dynamic channel is ready */
210  if (priv->isReady)
211  {
212  Stream_SetPosition(s, 0);
213 
214  if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned))
215  {
216  if (GetLastError() == ERROR_NO_DATA)
217  return ERROR_NO_DATA;
218 
219  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
220  return ERROR_INTERNAL_ERROR;
221  }
222 
223  if (BytesReturned < 1)
224  return CHANNEL_RC_OK;
225 
226  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
227  {
228  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
229  return CHANNEL_RC_NO_MEMORY;
230  }
231 
232  if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s),
233  Stream_Capacity(s), &BytesReturned) == FALSE)
234  {
235  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
236  return ERROR_INTERNAL_ERROR;
237  }
238 
239  Stream_SetLength(s, BytesReturned);
240  Stream_SetPosition(s, 0);
241 
242  while (Stream_GetPosition(s) < Stream_Length(s))
243  {
244  if ((ret = gfxredir_server_receive_pdu(context, s)))
245  {
246  WLog_ERR(TAG,
247  "gfxredir_server_receive_pdu "
248  "failed with error %" PRIu32 "!",
249  ret);
250  return ret;
251  }
252  }
253  }
254 
255  return ret;
256 }
257 
263 static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
264 {
265  GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
266  GfxRedirServerPrivate* priv = context->priv;
267  DWORD status;
268  DWORD nCount;
269  HANDLE events[8];
270  UINT error = CHANNEL_RC_OK;
271  nCount = 0;
272  events[nCount++] = priv->stopEvent;
273  events[nCount++] = priv->channelEvent;
274 
275  while (TRUE)
276  {
277  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
278 
279  if (status == WAIT_FAILED)
280  {
281  error = GetLastError();
282  WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
283  break;
284  }
285 
286  /* Stop Event */
287  if (status == WAIT_OBJECT_0)
288  break;
289 
290  if ((error = gfxredir_server_handle_messages(context)))
291  {
292  WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error);
293  break;
294  }
295  }
296 
297  ExitThread(error);
298  return error;
299 }
300 
312 static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, UINT32 length)
313 {
314  UINT error;
315  GFXREDIR_HEADER header;
316  wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length);
317 
318  if (!s)
319  {
320  WLog_ERR(TAG, "Stream_New failed!");
321  goto error;
322  }
323 
324  header.cmdId = cmdId;
325  header.length = GFXREDIR_HEADER_SIZE + length;
326 
327  if ((error = gfxredir_write_header(s, &header)))
328  {
329  WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
330  goto error;
331  }
332 
333  return s;
334 error:
335  Stream_Free(s, TRUE);
336  return NULL;
337 }
338 
344 static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s)
345 {
346  UINT ret;
347  ULONG written;
348 
349  if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s),
350  Stream_GetPosition(s), &written))
351  {
352  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
353  ret = ERROR_INTERNAL_ERROR;
354  goto out;
355  }
356 
357  if (written < Stream_GetPosition(s))
358  {
359  WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
360  Stream_GetPosition(s));
361  }
362 
363  ret = CHANNEL_RC_OK;
364 out:
365  Stream_Free(s, TRUE);
366  return ret;
367 }
368 
374 static UINT gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error)
375 {
376  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
377 
378  if (!s)
379  {
380  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
381  return CHANNEL_RC_NO_MEMORY;
382  }
383 
384  Stream_Write_UINT32(s, error->errorCode);
385  return gfxredir_server_packet_send(context, s);
386 }
387 
393 static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
394  const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm)
395 {
396  UINT ret;
397  wStream* s;
398 
399  if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
400  {
401  WLog_ERR(TAG, "length must be greater than header size, failed!");
402  return ERROR_INVALID_DATA;
403  }
404 
405  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
406 
407  if (!s)
408  {
409  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
410  return CHANNEL_RC_NO_MEMORY;
411  }
412 
413  Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
414  Stream_Write_UINT32(s, graphicsCapsConfirm->version);
415  Stream_Write_UINT32(s, graphicsCapsConfirm->length);
416  if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
417  Stream_Write(s, graphicsCapsConfirm->capsData,
418  graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
419  ret = gfxredir_server_packet_send(context, s);
420  if (ret == CHANNEL_RC_OK)
421  context->confirmedCapsVersion = graphicsCapsConfirm->version;
422  return ret;
423 }
424 
430 static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
431  const GFXREDIR_OPEN_POOL_PDU* openPool)
432 {
433  wStream* s;
434 
435  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
436  {
437  WLog_ERR(TAG, "open_pool is called with invalid version!");
438  return ERROR_INTERNAL_ERROR;
439  }
440 
441  if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL)
442  {
443  WLog_ERR(TAG, "section name must be provided!");
444  return ERROR_INVALID_DATA;
445  }
446 
447  /* make sure null-terminate */
448  if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
449  {
450  WLog_ERR(TAG, "section name must be terminated with NULL!");
451  return ERROR_INVALID_DATA;
452  }
453 
454  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
455  20 + (openPool->sectionNameLength * 2));
456 
457  if (!s)
458  {
459  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
460  return CHANNEL_RC_NO_MEMORY;
461  }
462 
463  Stream_Write_UINT64(s, openPool->poolId);
464  Stream_Write_UINT64(s, openPool->poolSize);
465  Stream_Write_UINT32(s, openPool->sectionNameLength);
466  Stream_Write(s, openPool->sectionName, (openPool->sectionNameLength * 2));
467  return gfxredir_server_packet_send(context, s);
468 }
469 
475 static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
476  const GFXREDIR_CLOSE_POOL_PDU* closePool)
477 {
478  wStream* s;
479 
480  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
481  {
482  WLog_ERR(TAG, "close_pool is called with invalid version!");
483  return ERROR_INTERNAL_ERROR;
484  }
485 
486  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
487 
488  if (!s)
489  {
490  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
491  return CHANNEL_RC_NO_MEMORY;
492  }
493 
494  Stream_Write_UINT64(s, closePool->poolId);
495  return gfxredir_server_packet_send(context, s);
496 }
497 
503 static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
504  const GFXREDIR_CREATE_BUFFER_PDU* createBuffer)
505 {
506  wStream* s;
507 
508  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
509  {
510  WLog_ERR(TAG, "create_buffer is called with invalid version!");
511  return ERROR_INTERNAL_ERROR;
512  }
513 
514  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
515 
516  if (!s)
517  {
518  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
519  return CHANNEL_RC_NO_MEMORY;
520  }
521 
522  Stream_Write_UINT64(s, createBuffer->poolId);
523  Stream_Write_UINT64(s, createBuffer->bufferId);
524  Stream_Write_UINT64(s, createBuffer->offset);
525  Stream_Write_UINT32(s, createBuffer->stride);
526  Stream_Write_UINT32(s, createBuffer->width);
527  Stream_Write_UINT32(s, createBuffer->height);
528  Stream_Write_UINT32(s, createBuffer->format);
529  return gfxredir_server_packet_send(context, s);
530 }
531 
537 static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
538  const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer)
539 {
540  wStream* s;
541 
542  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
543  {
544  WLog_ERR(TAG, "destroy_buffer is called with invalid version!");
545  return ERROR_INTERNAL_ERROR;
546  }
547 
548  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
549 
550  if (!s)
551  {
552  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
553  return CHANNEL_RC_NO_MEMORY;
554  }
555 
556  Stream_Write_UINT64(s, destroyBuffer->bufferId);
557  return gfxredir_server_packet_send(context, s);
558 }
559 
565 static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
566  const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer)
567 {
568  UINT32 length;
569  wStream* s;
570  RECTANGLE_32 dummyRect = { 0, 0, 0, 0 };
571 
572  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
573  {
574  WLog_ERR(TAG, "present_buffer is called with invalid version!");
575  return ERROR_INTERNAL_ERROR;
576  }
577 
578  if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
579  {
580  WLog_ERR(TAG, "numOpaqueRects is larger than its limit!");
581  return ERROR_INVALID_DATA;
582  }
583 
584  length = 64 + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
585  sizeof(RECTANGLE_32));
586 
587  s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
588 
589  if (!s)
590  {
591  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
592  return CHANNEL_RC_NO_MEMORY;
593  }
594 
595  Stream_Write_UINT64(s, presentBuffer->timestamp);
596  Stream_Write_UINT64(s, presentBuffer->presentId);
597  Stream_Write_UINT64(s, presentBuffer->windowId);
598  Stream_Write_UINT64(s, presentBuffer->bufferId);
599  Stream_Write_UINT32(s, presentBuffer->orientation);
600  Stream_Write_UINT32(s, presentBuffer->targetWidth);
601  Stream_Write_UINT32(s, presentBuffer->targetHeight);
602  Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
603  Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
604  Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
605  Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
606  Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
607  if (presentBuffer->numOpaqueRects)
608  Stream_Write(s, presentBuffer->opaqueRects,
609  (presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32)));
610  else
611  Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32));
612  return gfxredir_server_packet_send(context, s);
613 }
614 
620 static UINT gfxredir_server_open(GfxRedirServerContext* context)
621 {
622  UINT rc = ERROR_INTERNAL_ERROR;
623  GfxRedirServerPrivate* priv = context->priv;
624  DWORD BytesReturned = 0;
625  PULONG pSessionId = NULL;
626  void* buffer;
627  buffer = NULL;
628  priv->SessionId = WTS_CURRENT_SESSION;
629 
630  if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
631  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
632  {
633  WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
634  rc = ERROR_INTERNAL_ERROR;
635  goto out_close;
636  }
637 
638  priv->SessionId = (DWORD)*pSessionId;
639  WTSFreeMemory(pSessionId);
640  priv->gfxredir_channel = (HANDLE)WTSVirtualChannelOpenEx(
641  priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
642 
643  if (!priv->gfxredir_channel)
644  {
645  WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
646  rc = GetLastError();
647  goto out_close;
648  }
649 
650  /* Query for channel event handle */
651  if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
652  &BytesReturned) ||
653  (BytesReturned != sizeof(HANDLE)))
654  {
655  WLog_ERR(TAG,
656  "WTSVirtualChannelQuery failed "
657  "or invalid returned size(%" PRIu32 ")",
658  BytesReturned);
659 
660  if (buffer)
661  WTSFreeMemory(buffer);
662 
663  rc = ERROR_INTERNAL_ERROR;
664  goto out_close;
665  }
666 
667  CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE));
668  WTSFreeMemory(buffer);
669 
670  if (priv->thread == NULL)
671  {
672  if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
673  {
674  WLog_ERR(TAG, "CreateEvent failed!");
675  rc = ERROR_INTERNAL_ERROR;
676  }
677 
678  if (!(priv->thread =
679  CreateThread(NULL, 0, gfxredir_server_thread_func, (void*)context, 0, NULL)))
680  {
681  WLog_ERR(TAG, "CreateEvent failed!");
682  (void)CloseHandle(priv->stopEvent);
683  priv->stopEvent = NULL;
684  rc = ERROR_INTERNAL_ERROR;
685  }
686  }
687 
688  return CHANNEL_RC_OK;
689 out_close:
690  WTSVirtualChannelClose(priv->gfxredir_channel);
691  priv->gfxredir_channel = NULL;
692  priv->channelEvent = NULL;
693  return rc;
694 }
695 
701 static UINT gfxredir_server_close(GfxRedirServerContext* context)
702 {
703  UINT error = CHANNEL_RC_OK;
704  GfxRedirServerPrivate* priv = context->priv;
705 
706  if (priv->thread)
707  {
708  (void)SetEvent(priv->stopEvent);
709 
710  if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
711  {
712  error = GetLastError();
713  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
714  return error;
715  }
716 
717  (void)CloseHandle(priv->thread);
718  (void)CloseHandle(priv->stopEvent);
719  priv->thread = NULL;
720  priv->stopEvent = NULL;
721  }
722 
723  if (priv->gfxredir_channel)
724  {
725  WTSVirtualChannelClose(priv->gfxredir_channel);
726  priv->gfxredir_channel = NULL;
727  }
728 
729  return error;
730 }
731 
732 GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
733 {
734  GfxRedirServerContext* context;
735  GfxRedirServerPrivate* priv;
736  context = (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext));
737 
738  if (!context)
739  {
740  WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
741  return NULL;
742  }
743 
744  priv = context->priv = (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate));
745 
746  if (!context->priv)
747  {
748  WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
749  goto out_free;
750  }
751 
752  priv->input_stream = Stream_New(NULL, 4);
753 
754  if (!priv->input_stream)
755  {
756  WLog_ERR(TAG, "Stream_New failed!");
757  goto out_free_priv;
758  }
759 
760  context->vcm = vcm;
761  context->Open = gfxredir_server_open;
762  context->Close = gfxredir_server_close;
763  context->Error = gfxredir_send_error;
764  context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
765  context->OpenPool = gfxredir_send_open_pool;
766  context->ClosePool = gfxredir_send_close_pool;
767  context->CreateBuffer = gfxredir_send_create_buffer;
768  context->DestroyBuffer = gfxredir_send_destroy_buffer;
769  context->PresentBuffer = gfxredir_send_present_buffer;
770  context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
771  priv->isReady = FALSE;
772  return context;
773 out_free_priv:
774  free(context->priv);
775 out_free:
776  free(context);
777  return NULL;
778 }
779 
780 void gfxredir_server_context_free(GfxRedirServerContext* context)
781 {
782  if (!context)
783  return;
784 
785  gfxredir_server_close(context);
786 
787  if (context->priv)
788  {
789  Stream_Free(context->priv->input_stream, TRUE);
790  free(context->priv);
791  }
792 
793  free(context);
794 }