FreeRDP
Loading...
Searching...
No Matches
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 = WINPR_C_ARRAY_INIT;
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;
73 GFXREDIR_CAPS_ADVERTISE_PDU pdu = WINPR_C_ARRAY_INIT;
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;
105 GFXREDIR_PRESENT_BUFFER_ACK_PDU pdu = WINPR_C_ARRAY_INIT;
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* data)
129{
130 UINT error = CHANNEL_RC_OK;
131 GFXREDIR_HEADER header = WINPR_C_ARRAY_INIT;
132
133 WINPR_ASSERT(context);
134
135 size_t packetLen = 0;
136 if ((error = gfxredir_read_header(data, &header, &packetLen)))
137 {
138 WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error);
139 return error;
140 }
141
142 wStream sbuffer = WINPR_C_ARRAY_INIT;
143 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_ConstPointer(data), packetLen);
144 Stream_Seek(data, packetLen);
145
146 switch (header.cmdId)
147 {
148 case GFXREDIR_CMDID_LEGACY_CAPS:
149 if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
150 WLog_ERR(TAG,
151 "gfxredir_recv_legacy_caps_pdu "
152 "failed with error %" PRIu32 "!",
153 error);
154
155 break;
156
157 case GFXREDIR_CMDID_CAPS_ADVERTISE:
158 if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
159 WLog_ERR(TAG,
160 "gfxredir_recv_caps_advertise "
161 "failed with error %" PRIu32 "!",
162 error);
163 break;
164
165 case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
166 if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
167 WLog_ERR(TAG,
168 "gfxredir_recv_present_buffer_ask_pdu "
169 "failed with error %" PRIu32 "!",
170 error);
171 break;
172
173 default:
174 error = CHANNEL_RC_BAD_PROC;
175 WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId);
176 break;
177 }
178
179 const size_t end = Stream_GetPosition(s);
180
181 if (end != packetLen)
182 {
183 WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %" PRIuz ", Expected: %" PRIuz "", end,
184 packetLen);
185 }
186
187 return error;
188}
189
195static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
196{
197 UINT ret = CHANNEL_RC_OK;
198
199 WINPR_ASSERT(context);
200
201 GfxRedirServerPrivate* priv = context->priv;
202 WINPR_ASSERT(priv);
203
204 wStream* s = priv->input_stream;
205
206 /* Check whether the dynamic channel is ready */
207 if (!priv->isReady)
208 {
209 void* buffer = nullptr;
210 DWORD BytesReturned = 0;
211 if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
212 &BytesReturned) == FALSE)
213 {
214 if (GetLastError() == ERROR_NO_DATA)
215 return ERROR_NO_DATA;
216
217 WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
218 return ERROR_INTERNAL_ERROR;
219 }
220
221 priv->isReady = *((BOOL*)buffer);
222 WTSFreeMemory(buffer);
223 }
224
225 /* Consume channel event only after the dynamic channel is ready */
226 if (priv->isReady)
227 {
228 Stream_ResetPosition(s);
229
230 DWORD BytesReturned = 0;
231 if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, nullptr, 0, &BytesReturned))
232 {
233 if (GetLastError() == ERROR_NO_DATA)
234 return ERROR_NO_DATA;
235
236 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
237 return ERROR_INTERNAL_ERROR;
238 }
239
240 if (BytesReturned < 1)
241 return CHANNEL_RC_OK;
242
243 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
244 {
245 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
246 return CHANNEL_RC_NO_MEMORY;
247 }
248
249 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
250 if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), cap,
251 &BytesReturned) == FALSE)
252 {
253 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
254 return ERROR_INTERNAL_ERROR;
255 }
256
257 if (!Stream_SetLength(s, BytesReturned))
258 return ERROR_INTERNAL_ERROR;
259
260 Stream_ResetPosition(s);
261
262 while (Stream_GetPosition(s) < Stream_Length(s))
263 {
264 if ((ret = gfxredir_server_receive_pdu(context, s)))
265 {
266 WLog_ERR(TAG,
267 "gfxredir_server_receive_pdu "
268 "failed with error %" PRIu32 "!",
269 ret);
270 return ret;
271 }
272 }
273 }
274
275 return ret;
276}
277
283static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
284{
285 GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
286 WINPR_ASSERT(context);
287
288 GfxRedirServerPrivate* priv = context->priv;
289 WINPR_ASSERT(priv);
290
291 HANDLE events[8] = WINPR_C_ARRAY_INIT;
292 UINT error = CHANNEL_RC_OK;
293 DWORD nCount = 0;
294 events[nCount++] = priv->stopEvent;
295 events[nCount++] = priv->channelEvent;
296
297 while (TRUE)
298 {
299 const DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
300
301 if (status == WAIT_FAILED)
302 {
303 error = GetLastError();
304 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
305 break;
306 }
307
308 /* Stop Event */
309 if (status == WAIT_OBJECT_0)
310 break;
311
312 if ((error = gfxredir_server_handle_messages(context)))
313 {
314 WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error);
315 break;
316 }
317 }
318
319 ExitThread(error);
320 return error;
321}
322
334static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, size_t length)
335{
336 GFXREDIR_HEADER header = WINPR_C_ARRAY_INIT;
337 wStream* s = Stream_New(nullptr, GFXREDIR_HEADER_SIZE + length);
338
339 if (!s)
340 {
341 WLog_ERR(TAG, "Stream_New failed!");
342 goto error;
343 }
344
345 header.cmdId = cmdId;
346 header.length = WINPR_ASSERTING_INT_CAST(UINT32, GFXREDIR_HEADER_SIZE + length);
347
348 const UINT error = gfxredir_write_header(s, &header);
349 if (error)
350 {
351 WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
352 goto error;
353 }
354
355 return s;
356error:
357 Stream_Free(s, TRUE);
358 return nullptr;
359}
360
366static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s)
367{
368 UINT ret = ERROR_INTERNAL_ERROR;
369 ULONG written = 0;
370
371 WINPR_ASSERT(context);
372
373 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
374 if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), cap,
375 &written))
376 {
377 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
378 ret = ERROR_INTERNAL_ERROR;
379 goto out;
380 }
381
382 if (written < Stream_GetPosition(s))
383 {
384 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
385 Stream_GetPosition(s));
386 }
387
388 ret = CHANNEL_RC_OK;
389out:
390 Stream_Free(s, TRUE);
391 return ret;
392}
393
399static UINT gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error)
400{
401 WINPR_ASSERT(context);
402 WINPR_ASSERT(error);
403
404 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
405
406 if (!s)
407 {
408 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
409 return CHANNEL_RC_NO_MEMORY;
410 }
411
412 Stream_Write_UINT32(s, error->errorCode);
413 return gfxredir_server_packet_send(context, s);
414}
415
421static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
422 const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm)
423{
424 WINPR_ASSERT(context);
425 WINPR_ASSERT(graphicsCapsConfirm);
426
427 if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
428 {
429 WLog_ERR(TAG, "length must be greater than header size, failed!");
430 return ERROR_INVALID_DATA;
431 }
432
433 wStream* s =
434 gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
435
436 if (!s)
437 {
438 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
439 return CHANNEL_RC_NO_MEMORY;
440 }
441
442 Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
443 Stream_Write_UINT32(s, graphicsCapsConfirm->version);
444 Stream_Write_UINT32(s, graphicsCapsConfirm->length);
445 if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
446 Stream_Write(s, graphicsCapsConfirm->capsData,
447 graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
448 const UINT ret = gfxredir_server_packet_send(context, s);
449 if (ret == CHANNEL_RC_OK)
450 context->confirmedCapsVersion = graphicsCapsConfirm->version;
451 return ret;
452}
453
459static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
460 const GFXREDIR_OPEN_POOL_PDU* openPool)
461{
462 WINPR_ASSERT(context);
463 WINPR_ASSERT(openPool);
464
465 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
466 {
467 WLog_ERR(TAG, "open_pool is called with invalid version!");
468 return ERROR_INTERNAL_ERROR;
469 }
470
471 if (openPool->sectionNameLength == 0 || openPool->sectionName == nullptr)
472 {
473 WLog_ERR(TAG, "section name must be provided!");
474 return ERROR_INVALID_DATA;
475 }
476
477 /* make sure null-terminate */
478 if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
479 {
480 WLog_ERR(TAG, "section name must be terminated with nullptr!");
481 return ERROR_INVALID_DATA;
482 }
483
484 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
485 20 + (openPool->sectionNameLength * 2));
486
487 if (!s)
488 {
489 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
490 return CHANNEL_RC_NO_MEMORY;
491 }
492
493 Stream_Write_UINT64(s, openPool->poolId);
494 Stream_Write_UINT64(s, openPool->poolSize);
495 Stream_Write_UINT32(s, openPool->sectionNameLength);
496 Stream_Write(s, openPool->sectionName, (2ULL * openPool->sectionNameLength));
497 return gfxredir_server_packet_send(context, s);
498}
499
505static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
506 const GFXREDIR_CLOSE_POOL_PDU* closePool)
507{
508 WINPR_ASSERT(context);
509 WINPR_ASSERT(closePool);
510
511 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
512 {
513 WLog_ERR(TAG, "close_pool is called with invalid version!");
514 return ERROR_INTERNAL_ERROR;
515 }
516
517 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
518
519 if (!s)
520 {
521 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
522 return CHANNEL_RC_NO_MEMORY;
523 }
524
525 Stream_Write_UINT64(s, closePool->poolId);
526 return gfxredir_server_packet_send(context, s);
527}
528
534static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
535 const GFXREDIR_CREATE_BUFFER_PDU* createBuffer)
536{
537 WINPR_ASSERT(context);
538 WINPR_ASSERT(createBuffer);
539
540 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
541 {
542 WLog_ERR(TAG, "create_buffer is called with invalid version!");
543 return ERROR_INTERNAL_ERROR;
544 }
545
546 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
547
548 if (!s)
549 {
550 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
551 return CHANNEL_RC_NO_MEMORY;
552 }
553
554 Stream_Write_UINT64(s, createBuffer->poolId);
555 Stream_Write_UINT64(s, createBuffer->bufferId);
556 Stream_Write_UINT64(s, createBuffer->offset);
557 Stream_Write_UINT32(s, createBuffer->stride);
558 Stream_Write_UINT32(s, createBuffer->width);
559 Stream_Write_UINT32(s, createBuffer->height);
560 Stream_Write_UINT32(s, createBuffer->format);
561 return gfxredir_server_packet_send(context, s);
562}
563
569static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
570 const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer)
571{
572 WINPR_ASSERT(context);
573 WINPR_ASSERT(destroyBuffer);
574
575 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
576 {
577 WLog_ERR(TAG, "destroy_buffer is called with invalid version!");
578 return ERROR_INTERNAL_ERROR;
579 }
580
581 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
582
583 if (!s)
584 {
585 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
586 return CHANNEL_RC_NO_MEMORY;
587 }
588
589 Stream_Write_UINT64(s, destroyBuffer->bufferId);
590 return gfxredir_server_packet_send(context, s);
591}
592
598static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
599 const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer)
600{
601 RECTANGLE_32 dummyRect = { 0, 0, 0, 0 };
602
603 WINPR_ASSERT(context);
604 WINPR_ASSERT(presentBuffer);
605
606 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
607 {
608 WLog_ERR(TAG, "present_buffer is called with invalid version!");
609 return ERROR_INTERNAL_ERROR;
610 }
611
612 if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
613 {
614 WLog_ERR(TAG, "numOpaqueRects is larger than its limit!");
615 return ERROR_INVALID_DATA;
616 }
617
618 const size_t length =
619 64ULL + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
620 sizeof(RECTANGLE_32));
621
622 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
623
624 if (!s)
625 {
626 WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
627 return CHANNEL_RC_NO_MEMORY;
628 }
629
630 Stream_Write_UINT64(s, presentBuffer->timestamp);
631 Stream_Write_UINT64(s, presentBuffer->presentId);
632 Stream_Write_UINT64(s, presentBuffer->windowId);
633 Stream_Write_UINT64(s, presentBuffer->bufferId);
634 Stream_Write_UINT32(s, presentBuffer->orientation);
635 Stream_Write_UINT32(s, presentBuffer->targetWidth);
636 Stream_Write_UINT32(s, presentBuffer->targetHeight);
637 Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
638 Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
639 Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
640 Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
641 Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
642 if (presentBuffer->numOpaqueRects)
643 Stream_Write(s, presentBuffer->opaqueRects,
644 (presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32)));
645 else
646 Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32));
647 return gfxredir_server_packet_send(context, s);
648}
649
655static UINT gfxredir_server_open(GfxRedirServerContext* context)
656{
657 UINT rc = ERROR_INTERNAL_ERROR;
658 WINPR_ASSERT(context);
659
660 GfxRedirServerPrivate* priv = context->priv;
661 WINPR_ASSERT(priv);
662
663 DWORD BytesReturned = 0;
664 PULONG pSessionId = nullptr;
665 void* buffer = nullptr;
666 priv->SessionId = WTS_CURRENT_SESSION;
667
668 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
669 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
670 {
671 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
672 rc = ERROR_INTERNAL_ERROR;
673 goto out_close;
674 }
675
676 priv->SessionId = (DWORD)*pSessionId;
677 WTSFreeMemory(pSessionId);
678 priv->gfxredir_channel = WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME,
679 WTS_CHANNEL_OPTION_DYNAMIC);
680
681 if (!priv->gfxredir_channel)
682 {
683 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
684 rc = GetLastError();
685 goto out_close;
686 }
687
688 /* Query for channel event handle */
689 if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
690 &BytesReturned) ||
691 (BytesReturned != sizeof(HANDLE)))
692 {
693 WLog_ERR(TAG,
694 "WTSVirtualChannelQuery failed "
695 "or invalid returned size(%" PRIu32 ")",
696 BytesReturned);
697
698 if (buffer)
699 WTSFreeMemory(buffer);
700
701 rc = ERROR_INTERNAL_ERROR;
702 goto out_close;
703 }
704
705 priv->channelEvent = *(HANDLE*)buffer;
706 WTSFreeMemory(buffer);
707
708 if (priv->thread == nullptr)
709 {
710 if (!(priv->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
711 {
712 WLog_ERR(TAG, "CreateEvent failed!");
713 goto out_close;
714 }
715
716 if (!(priv->thread = CreateThread(nullptr, 0, gfxredir_server_thread_func, (void*)context,
717 0, nullptr)))
718 {
719 WLog_ERR(TAG, "CreateEvent failed!");
720 (void)CloseHandle(priv->stopEvent);
721 priv->stopEvent = nullptr;
722 goto out_close;
723 }
724 }
725
726 return CHANNEL_RC_OK;
727out_close:
728 WTSVirtualChannelClose(priv->gfxredir_channel);
729 priv->gfxredir_channel = nullptr;
730 priv->channelEvent = nullptr;
731 return rc;
732}
733
739static UINT gfxredir_server_close(GfxRedirServerContext* context)
740{
741 UINT error = CHANNEL_RC_OK;
742 WINPR_ASSERT(context);
743
744 GfxRedirServerPrivate* priv = context->priv;
745 WINPR_ASSERT(priv);
746
747 if (priv->thread)
748 {
749 (void)SetEvent(priv->stopEvent);
750
751 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
752 {
753 error = GetLastError();
754 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
755 return error;
756 }
757
758 (void)CloseHandle(priv->thread);
759 (void)CloseHandle(priv->stopEvent);
760 priv->thread = nullptr;
761 priv->stopEvent = nullptr;
762 }
763
764 if (priv->gfxredir_channel)
765 {
766 WTSVirtualChannelClose(priv->gfxredir_channel);
767 priv->gfxredir_channel = nullptr;
768 }
769
770 return error;
771}
772
773GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
774{
775 GfxRedirServerContext* context =
776 (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext));
777
778 if (!context)
779 {
780 WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
781 return nullptr;
782 }
783
784 GfxRedirServerPrivate* priv = context->priv =
785 (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate));
786
787 if (!context->priv)
788 {
789 WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
790 goto fail;
791 }
792
793 priv->input_stream = Stream_New(nullptr, 4);
794
795 if (!priv->input_stream)
796 {
797 WLog_ERR(TAG, "Stream_New failed!");
798 goto fail;
799 }
800
801 context->vcm = vcm;
802 context->Open = gfxredir_server_open;
803 context->Close = gfxredir_server_close;
804 context->Error = gfxredir_send_error;
805 context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
806 context->OpenPool = gfxredir_send_open_pool;
807 context->ClosePool = gfxredir_send_close_pool;
808 context->CreateBuffer = gfxredir_send_create_buffer;
809 context->DestroyBuffer = gfxredir_send_destroy_buffer;
810 context->PresentBuffer = gfxredir_send_present_buffer;
811 context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
812 priv->isReady = FALSE;
813 return context;
814fail:
815 gfxredir_server_context_free(context);
816 return nullptr;
817}
818
819void gfxredir_server_context_free(GfxRedirServerContext* context)
820{
821 if (!context)
822 return;
823
824 gfxredir_server_close(context);
825
826 if (context->priv)
827 {
828 Stream_Free(context->priv->input_stream, TRUE);
829 free(context->priv);
830 }
831
832 free(context);
833}