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