FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
x11_shadow.c
1
21#include <errno.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include <sys/ipc.h>
28#include <sys/shm.h>
29#include <sys/select.h>
30#include <sys/signal.h>
31
32#include <X11/Xlib.h>
33#include <X11/Xutil.h>
34
35#include <winpr/crt.h>
36#include <winpr/assert.h>
37#include <winpr/cast.h>
38#include <winpr/path.h>
39#include <winpr/synch.h>
40#include <winpr/image.h>
41#include <winpr/sysinfo.h>
42
43#include <freerdp/log.h>
44#include <freerdp/codec/color.h>
45#include <freerdp/codec/region.h>
46
47#include "x11_shadow.h"
48
49#define TAG SERVER_TAG("shadow.x11")
50
51// #define USE_SHADOW_BLEND_CURSOR
52
53static UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors);
54
55#ifdef WITH_PAM
56
57#include <security/pam_appl.h>
58
59typedef struct
60{
61 const char* user;
62 const char* domain;
63 const char* password;
64} SHADOW_PAM_AUTH_DATA;
65
66typedef struct
67{
68 char* service_name;
69 pam_handle_t* handle;
70 struct pam_conv pamc;
71 SHADOW_PAM_AUTH_DATA appdata;
72} SHADOW_PAM_AUTH_INFO;
73
74static int x11_shadow_pam_conv(int num_msg, const struct pam_message** msg,
75 struct pam_response** resp, void* appdata_ptr)
76{
77 int pam_status = PAM_CONV_ERR;
78 SHADOW_PAM_AUTH_DATA* appdata = NULL;
79 struct pam_response* response = NULL;
80 WINPR_ASSERT(num_msg >= 0);
81 appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
82 WINPR_ASSERT(appdata);
83
84 if (!(response = (struct pam_response*)calloc((size_t)num_msg, sizeof(struct pam_response))))
85 return PAM_BUF_ERR;
86
87 for (int index = 0; index < num_msg; index++)
88 {
89 switch (msg[index]->msg_style)
90 {
91 case PAM_PROMPT_ECHO_ON:
92 response[index].resp = _strdup(appdata->user);
93
94 if (!response[index].resp)
95 goto out_fail;
96
97 response[index].resp_retcode = PAM_SUCCESS;
98 break;
99
100 case PAM_PROMPT_ECHO_OFF:
101 response[index].resp = _strdup(appdata->password);
102
103 if (!response[index].resp)
104 goto out_fail;
105
106 response[index].resp_retcode = PAM_SUCCESS;
107 break;
108
109 default:
110 pam_status = PAM_CONV_ERR;
111 goto out_fail;
112 }
113 }
114
115 *resp = response;
116 return PAM_SUCCESS;
117out_fail:
118
119 for (int index = 0; index < num_msg; ++index)
120 {
121 if (response[index].resp)
122 {
123 memset(response[index].resp, 0, strlen(response[index].resp));
124 free(response[index].resp);
125 }
126 }
127
128 memset(response, 0, sizeof(struct pam_response) * (size_t)num_msg);
129 free(response);
130 *resp = NULL;
131 return pam_status;
132}
133
134static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
135{
136 const char* base = "/etc/pam.d";
137 const char* hints[] = { "lightdm", "gdm", "xdm", "login", "sshd" };
138
139 for (size_t x = 0; x < ARRAYSIZE(hints); x++)
140 {
141 char path[MAX_PATH] = { 0 };
142 const char* hint = hints[x];
143
144 (void)_snprintf(path, sizeof(path), "%s/%s", base, hint);
145 if (winpr_PathFileExists(path))
146 {
147
148 info->service_name = _strdup(hint);
149 return info->service_name != NULL;
150 }
151 }
152 WLog_WARN(TAG, "Could not determine PAM service name");
153 return FALSE;
154}
155
156static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
157 const char* user, const char* domain, const char* password)
158{
159 int pam_status = 0;
160 SHADOW_PAM_AUTH_INFO info = { 0 };
161 WINPR_UNUSED(subsystem);
162 WINPR_UNUSED(client);
163
164 if (!x11_shadow_pam_get_service_name(&info))
165 return -1;
166
167 info.appdata.user = user;
168 info.appdata.domain = domain;
169 info.appdata.password = password;
170 info.pamc.conv = &x11_shadow_pam_conv;
171 info.pamc.appdata_ptr = &info.appdata;
172 pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
173
174 if (pam_status != PAM_SUCCESS)
175 {
176 WLog_ERR(TAG, "pam_start failure: %s", pam_strerror(info.handle, pam_status));
177 return -1;
178 }
179
180 pam_status = pam_authenticate(info.handle, 0);
181
182 if (pam_status != PAM_SUCCESS)
183 {
184 WLog_ERR(TAG, "pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
185 return -1;
186 }
187
188 pam_status = pam_acct_mgmt(info.handle, 0);
189
190 if (pam_status != PAM_SUCCESS)
191 {
192 WLog_ERR(TAG, "pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
193 return -1;
194 }
195
196 return 1;
197}
198
199#endif
200
201static BOOL x11_shadow_input_synchronize_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
202 WINPR_ATTR_UNUSED rdpShadowClient* client,
203 WINPR_ATTR_UNUSED UINT32 flags)
204{
205 /* TODO: Implement */
206 WLog_WARN(TAG, "not implemented");
207 return TRUE;
208}
209
210static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
211 UINT16 flags, UINT8 code)
212{
213#ifdef WITH_XTEST
214 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
215 DWORD vkcode = 0;
216 DWORD keycode = 0;
217 DWORD scancode = 0;
218 BOOL extended = FALSE;
219
220 if (!client || !subsystem)
221 return FALSE;
222
223 if (flags & KBD_FLAGS_EXTENDED)
224 extended = TRUE;
225
226 scancode = code;
227 if (extended)
228 scancode |= KBDEXT;
229
230 vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
231
232 if (extended)
233 vkcode |= KBDEXT;
234
235 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
236
237 if (keycode != 0)
238 {
239 XLockDisplay(x11->display);
240 XTestGrabControl(x11->display, True);
241
242 if (flags & KBD_FLAGS_RELEASE)
243 XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
244 else
245 XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
246
247 XTestGrabControl(x11->display, False);
248 XFlush(x11->display);
249 XUnlockDisplay(x11->display);
250 }
251
252#endif
253 return TRUE;
254}
255
256static BOOL x11_shadow_input_unicode_keyboard_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
257 WINPR_ATTR_UNUSED rdpShadowClient* client,
258 WINPR_ATTR_UNUSED UINT16 flags,
259 WINPR_ATTR_UNUSED UINT16 code)
260{
261 /* TODO: Implement */
262 WLog_WARN(TAG, "not implemented");
263 return TRUE;
264}
265
266static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
267 UINT16 flags, UINT16 x, UINT16 y)
268{
269#ifdef WITH_XTEST
270 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
271 unsigned int button = 0;
272 BOOL down = FALSE;
273 rdpShadowServer* server = NULL;
274 rdpShadowSurface* surface = NULL;
275
276 if (!subsystem || !client)
277 return FALSE;
278
279 server = subsystem->server;
280
281 if (!server)
282 return FALSE;
283
284 surface = server->surface;
285
286 if (!surface)
287 return FALSE;
288
289 x11->lastMouseClient = client;
290 x += surface->x;
291 y += surface->y;
292 XLockDisplay(x11->display);
293 XTestGrabControl(x11->display, True);
294
295 if (flags & PTR_FLAGS_WHEEL)
296 {
297 BOOL negative = FALSE;
298
299 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
300 negative = TRUE;
301
302 button = (negative) ? 5 : 4;
303 XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
304 XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
305 }
306 else if (flags & PTR_FLAGS_HWHEEL)
307 {
308 BOOL negative = FALSE;
309
310 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
311 negative = TRUE;
312
313 button = (negative) ? 7 : 6;
314 XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
315 XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
316 }
317 else
318 {
319 if (flags & PTR_FLAGS_MOVE)
320 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
321
322 if (flags & PTR_FLAGS_BUTTON1)
323 button = 1;
324 else if (flags & PTR_FLAGS_BUTTON2)
325 button = 3;
326 else if (flags & PTR_FLAGS_BUTTON3)
327 button = 2;
328
329 if (flags & PTR_FLAGS_DOWN)
330 down = TRUE;
331
332 if (button)
333 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
334 }
335
336 XTestGrabControl(x11->display, False);
337 XFlush(x11->display);
338 XUnlockDisplay(x11->display);
339#endif
340 return TRUE;
341}
342
343static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
344 rdpShadowClient* client, UINT16 flags, UINT16 x,
345 UINT16 y)
346{
347#ifdef WITH_XTEST
348 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
349 UINT button = 0;
350 BOOL down = FALSE;
351 rdpShadowServer* server = NULL;
352 rdpShadowSurface* surface = NULL;
353
354 if (!subsystem || !client)
355 return FALSE;
356
357 server = subsystem->server;
358
359 if (!server)
360 return FALSE;
361
362 surface = server->surface;
363
364 if (!surface)
365 return FALSE;
366
367 x11->lastMouseClient = client;
368 x += surface->x;
369 y += surface->y;
370 XLockDisplay(x11->display);
371 XTestGrabControl(x11->display, True);
372 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
373
374 if (flags & PTR_XFLAGS_BUTTON1)
375 button = 8;
376 else if (flags & PTR_XFLAGS_BUTTON2)
377 button = 9;
378
379 if (flags & PTR_XFLAGS_DOWN)
380 down = TRUE;
381
382 if (button)
383 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
384
385 XTestGrabControl(x11->display, False);
386 XFlush(x11->display);
387 XUnlockDisplay(x11->display);
388#endif
389 return TRUE;
390}
391
392static void x11_shadow_message_free(UINT32 id, SHADOW_MSG_OUT* msg)
393{
394 switch (id)
395 {
396 case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
397 free(msg);
398 break;
399
400 case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
401 free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->xorMaskData);
402 free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->andMaskData);
403 free(msg);
404 break;
405
406 default:
407 WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", id);
408 free(msg);
409 break;
410 }
411}
412
413static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
414{
415 UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
416 rdpShadowServer* server = NULL;
417 SHADOW_MSG_OUT_POINTER_POSITION_UPDATE templateMsg = { 0 };
418 int count = 0;
419
420 if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
421 return -1;
422
423 templateMsg.xPos = subsystem->common.pointerX;
424 templateMsg.yPos = subsystem->common.pointerY;
425 templateMsg.common.Free = x11_shadow_message_free;
426 server = subsystem->common.server;
427 ArrayList_Lock(server->clients);
428
429 for (size_t index = 0; index < ArrayList_Count(server->clients); index++)
430 {
432 rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
433
434 /* Skip the client which send us the latest mouse event */
435 if (client == subsystem->lastMouseClient)
436 continue;
437
438 msg = malloc(sizeof(templateMsg));
439
440 if (!msg)
441 {
442 count = -1;
443 break;
444 }
445
446 memcpy(msg, &templateMsg, sizeof(templateMsg));
447
448 if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
449 count++;
450 }
451
452 ArrayList_Unlock(server->clients);
453 return count;
454}
455
456static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
457{
459 UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
462
463 if (!msg)
464 return -1;
465
466 msg->xHot = subsystem->cursorHotX;
467 msg->yHot = subsystem->cursorHotY;
468 msg->width = subsystem->cursorWidth;
469 msg->height = subsystem->cursorHeight;
470
471 if (shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
472 subsystem->cursorPixels, subsystem->format, TRUE, msg->width, msg->height, msg) < 0)
473 {
474 free(msg);
475 return -1;
476 }
477
478 msg->common.Free = x11_shadow_message_free;
479 return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
480 NULL)
481 ? 1
482 : -1;
483}
484
485static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
486{
487 int x = 0;
488 int y = 0;
489 int n = 0;
490 rdpShadowServer* server = NULL;
491 rdpShadowSurface* surface = NULL;
492 server = subsystem->common.server;
493 surface = server->surface;
494
495 if (getImage)
496 {
497#ifdef WITH_XFIXES
498 UINT32* pDstPixel = NULL;
499 XFixesCursorImage* ci = NULL;
500 XLockDisplay(subsystem->display);
501 ci = XFixesGetCursorImage(subsystem->display);
502 XUnlockDisplay(subsystem->display);
503
504 if (!ci)
505 return -1;
506
507 x = ci->x;
508 y = ci->y;
509
510 if (ci->width > subsystem->cursorMaxWidth)
511 return -1;
512
513 if (ci->height > subsystem->cursorMaxHeight)
514 return -1;
515
516 subsystem->cursorHotX = ci->xhot;
517 subsystem->cursorHotY = ci->yhot;
518 subsystem->cursorWidth = ci->width;
519 subsystem->cursorHeight = ci->height;
520 subsystem->cursorId = ci->cursor_serial;
521 n = ci->width * ci->height;
522 pDstPixel = (UINT32*)subsystem->cursorPixels;
523
524 for (int k = 0; k < n; k++)
525 {
526 /* XFixesCursorImage.pixels is in *unsigned long*, which may be 8 bytes */
527 *pDstPixel++ = (UINT32)ci->pixels[k];
528 }
529
530 XFree(ci);
531 x11_shadow_pointer_alpha_update(subsystem);
532#endif
533 }
534 else
535 {
536 UINT32 mask = 0;
537 int win_x = 0;
538 int win_y = 0;
539 int root_x = 0;
540 int root_y = 0;
541 Window root = 0;
542 Window child = 0;
543 XLockDisplay(subsystem->display);
544
545 if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
546 &root_y, &win_x, &win_y, &mask))
547 {
548 XUnlockDisplay(subsystem->display);
549 return -1;
550 }
551
552 XUnlockDisplay(subsystem->display);
553 x = root_x;
554 y = root_y;
555 }
556
557 /* Convert to offset based on current surface */
558 if (surface)
559 {
560 x -= surface->x;
561 y -= surface->y;
562 }
563
564 if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
565 {
566 subsystem->common.pointerX = WINPR_ASSERTING_INT_CAST(UINT32, x);
567 subsystem->common.pointerY = WINPR_ASSERTING_INT_CAST(UINT32, y);
568 x11_shadow_pointer_position_update(subsystem);
569 }
570
571 return 1;
572}
573
574static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
575{
576 if (xevent->type == MotionNotify)
577 {
578 }
579
580#ifdef WITH_XFIXES
581 else if (xevent->type == subsystem->xfixes_cursor_notify_event)
582 {
583 x11_shadow_query_cursor(subsystem, TRUE);
584 }
585
586#endif
587 else
588 {
589 }
590
591 return 1;
592}
593
594#if defined(USE_SHADOW_BLEND_CURSOR)
595static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
596{
597 if (!subsystem)
598 return -1;
599
600 rdpShadowSurface* surface = subsystem->common.server->surface;
601 UINT32 nXSrc = 0;
602 UINT32 nYSrc = 0;
603 UINT32 nWidth = subsystem->cursorWidth;
604 UINT32 nHeight = subsystem->cursorHeight;
605 INT64 nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
606 INT64 nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
607
608 if (nXDst >= surface->width)
609 return 1;
610
611 if (nXDst < 0)
612 {
613 nXDst *= -1;
614
615 if (nXDst >= nWidth)
616 return 1;
617
618 nXSrc = (UINT32)nXDst;
619 nWidth -= nXDst;
620 nXDst = 0;
621 }
622
623 if (nYDst >= surface->height)
624 return 1;
625
626 if (nYDst < 0)
627 {
628 nYDst *= -1;
629
630 if (nYDst >= nHeight)
631 return 1;
632
633 nYSrc = (UINT32)nYDst;
634 nHeight -= nYDst;
635 nYDst = 0;
636 }
637
638 if ((nXDst + nWidth) > surface->width)
639 nWidth = (nXDst > surface->width) ? 0 : (UINT32)(surface->width - nXDst);
640
641 if ((nYDst + nHeight) > surface->height)
642 nHeight = (nYDst > surface->height) ? 0 : (UINT32)(surface->height - nYDst);
643
644 const BYTE* pSrcData = subsystem->cursorPixels;
645 const UINT32 nSrcStep = subsystem->cursorWidth * 4;
646 BYTE* pDstData = surface->data;
647 const UINT32 nDstStep = surface->scanline;
648
649 for (size_t y = 0; y < nHeight; y++)
650 {
651 const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
652 BYTE* pDstPixel = &pDstData[((WINPR_ASSERTING_INT_CAST(uint32_t, nYDst) + y) * nDstStep) +
653 (4ULL * WINPR_ASSERTING_INT_CAST(uint32_t, nXDst))];
654
655 for (size_t x = 0; x < nWidth; x++)
656 {
657 const BYTE B = *pSrcPixel++;
658 const BYTE G = *pSrcPixel++;
659 const BYTE R = *pSrcPixel++;
660 const BYTE A = *pSrcPixel++;
661
662 if (A == 0xFF)
663 {
664 pDstPixel[0] = B;
665 pDstPixel[1] = G;
666 pDstPixel[2] = R;
667 }
668 else
669 {
670 pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
671 pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
672 pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
673 }
674
675 pDstPixel[3] = 0xFF;
676 pDstPixel += 4;
677 }
678 }
679
680 return 1;
681}
682#endif
683
684static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
685{
686 XWindowAttributes attr;
687 XLockDisplay(subsystem->display);
688 XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
689 XUnlockDisplay(subsystem->display);
690
691 if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
692 {
693 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
694
695 /* Screen size changed. Refresh monitor definitions and trigger screen resize */
696 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
697 if (!shadow_screen_resize(subsystem->common.server->screen))
698 return FALSE;
699
700 WINPR_ASSERT(attr.width > 0);
701 WINPR_ASSERT(attr.height > 0);
702
703 subsystem->width = (UINT32)attr.width;
704 subsystem->height = (UINT32)attr.height;
705
706 virtualScreen->left = 0;
707 virtualScreen->top = 0;
708 virtualScreen->right = attr.width - 1;
709 virtualScreen->bottom = attr.height - 1;
710 virtualScreen->flags = 1;
711 return TRUE;
712 }
713
714 return FALSE;
715}
716
717static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
718{
719 char msg[256];
720 XGetErrorText(display, event->error_code, (char*)&msg, sizeof(msg));
721 WLog_ERR(TAG, "X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
722 event->error_code, event->request_code, event->minor_code);
723
724 /* Ignore BAD MATCH error during image capture. Abort in other case */
725 if (event->error_code != BadMatch)
726 {
727 abort();
728 }
729
730 return 0;
731}
732
733static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
734{
735 int rc = 0;
736 size_t count = 0;
737 int status = -1;
738 int x = 0;
739 int y = 0;
740 int width = 0;
741 int height = 0;
742 XImage* image = NULL;
743 rdpShadowServer* server = NULL;
744 rdpShadowSurface* surface = NULL;
745 RECTANGLE_16 invalidRect;
746 RECTANGLE_16 surfaceRect;
747 const RECTANGLE_16* extents = NULL;
748 server = subsystem->common.server;
749 surface = server->surface;
750 count = ArrayList_Count(server->clients);
751
752 if (count < 1)
753 return 1;
754
755 EnterCriticalSection(&surface->lock);
756 surfaceRect.left = 0;
757 surfaceRect.top = 0;
758
759 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->width);
760 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->height);
761 LeaveCriticalSection(&surface->lock);
762
763 XLockDisplay(subsystem->display);
764 /*
765 * Ignore BadMatch error during image capture. The screen size may be
766 * changed outside. We will resize to correct resolution at next frame
767 */
768 XSetErrorHandler(x11_shadow_error_handler_for_capture);
769#if defined(WITH_XDAMAGE)
770 if (subsystem->use_xshm)
771 {
772 image = subsystem->fb_image;
773 XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
774 subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
775
776 EnterCriticalSection(&surface->lock);
777 status = shadow_capture_compare_with_format(
778 surface->data, surface->format, surface->scanline, surface->width, surface->height,
779 (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format,
780 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
781 LeaveCriticalSection(&surface->lock);
782 }
783 else
784#endif
785 {
786 EnterCriticalSection(&surface->lock);
787 image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
788 surface->width, surface->height, AllPlanes, ZPixmap);
789
790 if (image)
791 {
792 status = shadow_capture_compare_with_format(
793 surface->data, surface->format, surface->scanline, surface->width, surface->height,
794 (BYTE*)image->data, subsystem->format,
795 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
796 }
797 LeaveCriticalSection(&surface->lock);
798 if (!image)
799 {
800 /*
801 * BadMatch error happened. The size may have been changed again.
802 * Give up this frame and we will resize again in next frame
803 */
804 goto fail_capture;
805 }
806 }
807
808 /* Restore the default error handler */
809 XSetErrorHandler(NULL);
810 XSync(subsystem->display, False);
811 XUnlockDisplay(subsystem->display);
812
813 if (status)
814 {
815 BOOL empty = 0;
816 EnterCriticalSection(&surface->lock);
817 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
818 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
819 empty = region16_is_empty(&(surface->invalidRegion));
820 LeaveCriticalSection(&surface->lock);
821
822 if (!empty)
823 {
824 BOOL success = 0;
825 EnterCriticalSection(&surface->lock);
826 extents = region16_extents(&(surface->invalidRegion));
827 x = extents->left;
828 y = extents->top;
829 width = extents->right - extents->left;
830 height = extents->bottom - extents->top;
831 WINPR_ASSERT(image);
832 WINPR_ASSERT(image->bytes_per_line >= 0);
833 WINPR_ASSERT(width >= 0);
834 WINPR_ASSERT(height >= 0);
835 success = freerdp_image_copy_no_overlap(
836 surface->data, surface->format, surface->scanline,
837 WINPR_ASSERTING_INT_CAST(uint32_t, x), WINPR_ASSERTING_INT_CAST(uint32_t, y),
838 WINPR_ASSERTING_INT_CAST(uint32_t, width),
839 WINPR_ASSERTING_INT_CAST(uint32_t, height), (BYTE*)image->data, subsystem->format,
840 WINPR_ASSERTING_INT_CAST(uint32_t, image->bytes_per_line),
841 WINPR_ASSERTING_INT_CAST(UINT32, x), WINPR_ASSERTING_INT_CAST(UINT32, y), NULL,
842 FREERDP_FLIP_NONE);
843 LeaveCriticalSection(&surface->lock);
844 if (!success)
845 goto fail_capture;
846
847#if defined(USE_SHADOW_BLEND_CURSOR)
848 if (x11_shadow_blend_cursor(subsystem) < 0)
849 goto fail_capture;
850#endif
851 count = ArrayList_Count(server->clients);
852 shadow_subsystem_frame_update(&subsystem->common);
853
854 if (count == 1)
855 {
856 rdpShadowClient* client = NULL;
857 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
858
859 if (client)
860 subsystem->common.captureFrameRate =
861 shadow_encoder_preferred_fps(client->encoder);
862 }
863
864 EnterCriticalSection(&surface->lock);
865 region16_clear(&(surface->invalidRegion));
866 LeaveCriticalSection(&surface->lock);
867 }
868 }
869
870 rc = 1;
871fail_capture:
872 if (!subsystem->use_xshm && image)
873 XDestroyImage(image);
874
875 if (rc != 1)
876 {
877 XSetErrorHandler(NULL);
878 XSync(subsystem->display, False);
879 XUnlockDisplay(subsystem->display);
880 }
881
882 return rc;
883}
884
885static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
886{
887 switch (message->id)
888 {
889 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
890 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
891 break;
892
893 default:
894 WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
895 break;
896 }
897
898 if (message->Free)
899 message->Free(message);
900
901 return 1;
902}
903
904static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
905{
906 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
907 XEvent xevent;
908 DWORD status = 0;
909 DWORD nCount = 0;
910 DWORD dwInterval = 0;
911 UINT64 frameTime = 0;
912 HANDLE events[32];
913 wMessage message;
914 wMessagePipe* MsgPipe = NULL;
915 MsgPipe = subsystem->common.MsgPipe;
916 nCount = 0;
917 events[nCount++] = subsystem->common.event;
918 events[nCount++] = MessageQueue_Event(MsgPipe->In);
919 subsystem->common.captureFrameRate = 16;
920 dwInterval = 1000 / subsystem->common.captureFrameRate;
921 frameTime = GetTickCount64() + dwInterval;
922
923 while (1)
924 {
925 const UINT64 cTime = GetTickCount64();
926 const DWORD dwTimeout =
927 (DWORD)((cTime > frameTime) ? 0 : MIN(UINT32_MAX, frameTime - cTime));
928 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
929
930 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
931 {
932 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
933 {
934 if (message.id == WMQ_QUIT)
935 break;
936
937 x11_shadow_subsystem_process_message(subsystem, &message);
938 }
939 }
940
941 if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
942 {
943 XLockDisplay(subsystem->display);
944
945 if (XEventsQueued(subsystem->display, QueuedAlready))
946 {
947 XNextEvent(subsystem->display, &xevent);
948 x11_shadow_handle_xevent(subsystem, &xevent);
949 }
950
951 XUnlockDisplay(subsystem->display);
952 }
953
954 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
955 {
956 x11_shadow_check_resize(subsystem);
957 x11_shadow_screen_grab(subsystem);
958 x11_shadow_query_cursor(subsystem, FALSE);
959 dwInterval = 1000 / subsystem->common.captureFrameRate;
960 frameTime += dwInterval;
961 }
962 }
963
964 ExitThread(0);
965 return 0;
966}
967
968static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
969{
970 if (subsystem->display)
971 return 1; /* initialize once */
972
973 // NOLINTNEXTLINE(concurrency-mt-unsafe)
974 if (!getenv("DISPLAY"))
975 {
976 // NOLINTNEXTLINE(concurrency-mt-unsafe)
977 setenv("DISPLAY", ":0", 1);
978 }
979
980 if (!XInitThreads())
981 return -1;
982
983 subsystem->display = XOpenDisplay(NULL);
984
985 if (!subsystem->display)
986 {
987 WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
988 return -1;
989 }
990
991 subsystem->xfds = ConnectionNumber(subsystem->display);
992 subsystem->number = DefaultScreen(subsystem->display);
993 subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
994 subsystem->depth = WINPR_ASSERTING_INT_CAST(UINT32, DefaultDepthOfScreen(subsystem->screen));
995 subsystem->width = WINPR_ASSERTING_INT_CAST(UINT32, WidthOfScreen(subsystem->screen));
996 subsystem->height = WINPR_ASSERTING_INT_CAST(UINT32, HeightOfScreen(subsystem->screen));
997 subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
998 return 1;
999}
1000
1001static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
1002{
1003#ifdef WITH_XFIXES
1004 int xfixes_event = 0;
1005 int xfixes_error = 0;
1006 int major = 0;
1007 int minor = 0;
1008
1009 if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
1010 return -1;
1011
1012 if (!XFixesQueryVersion(subsystem->display, &major, &minor))
1013 return -1;
1014
1015 subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
1016 XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
1017 XFixesDisplayCursorNotifyMask);
1018 return 1;
1019#else
1020 return -1;
1021#endif
1022}
1023
1024static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1025{
1026#ifdef WITH_XINERAMA
1027 int xinerama_event = 0;
1028 int xinerama_error = 0;
1029
1030 const int rc = x11_shadow_subsystem_base_init(subsystem);
1031 if (rc < 0)
1032 return rc;
1033
1034 if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1035 return -1;
1036
1037#if defined(WITH_XDAMAGE)
1038 int major = 0;
1039 int minor = 0;
1040 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1041 return -1;
1042#endif
1043
1044 if (!XineramaIsActive(subsystem->display))
1045 return -1;
1046
1047 return 1;
1048#else
1049 return -1;
1050#endif
1051}
1052
1053static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1054{
1055#ifdef WITH_XDAMAGE
1056 int major = 0;
1057 int minor = 0;
1058 int damage_event = 0;
1059 int damage_error = 0;
1060
1061 if (!subsystem->use_xfixes)
1062 return -1;
1063
1064 if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1065 return -1;
1066
1067 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1068 return -1;
1069
1070 if (major < 1)
1071 return -1;
1072
1073 subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1074 subsystem->xdamage =
1075 XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1076
1077 if (!subsystem->xdamage)
1078 return -1;
1079
1080#ifdef WITH_XFIXES
1081 subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
1082
1083 if (!subsystem->xdamage_region)
1084 return -1;
1085
1086#endif
1087 return 1;
1088#else
1089 return -1;
1090#endif
1091}
1092
1093static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1094{
1095 Bool pixmaps = 0;
1096 int major = 0;
1097 int minor = 0;
1098 XGCValues values;
1099
1100 if (!XShmQueryExtension(subsystem->display))
1101 return -1;
1102
1103 if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1104 return -1;
1105
1106 if (!pixmaps)
1107 return -1;
1108
1109 subsystem->fb_shm_info.shmid = -1;
1110 subsystem->fb_shm_info.shmaddr = (char*)-1;
1111 subsystem->fb_shm_info.readOnly = False;
1112 subsystem->fb_image =
1113 XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
1114 &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1115
1116 if (!subsystem->fb_image)
1117 {
1118 WLog_ERR(TAG, "XShmCreateImage failed");
1119 return -1;
1120 }
1121
1122 subsystem->fb_shm_info.shmid =
1123 shmget(IPC_PRIVATE,
1124 1ull * WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->bytes_per_line) *
1125 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1126 IPC_CREAT | 0600);
1127
1128 if (subsystem->fb_shm_info.shmid == -1)
1129 {
1130 WLog_ERR(TAG, "shmget failed");
1131 return -1;
1132 }
1133
1134 subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
1135 subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1136
1137 if (subsystem->fb_shm_info.shmaddr == ((char*)-1))
1138 {
1139 WLog_ERR(TAG, "shmat failed");
1140 return -1;
1141 }
1142
1143 if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1144 return -1;
1145
1146 XSync(subsystem->display, False);
1147 shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
1148 subsystem->fb_pixmap = XShmCreatePixmap(
1149 subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1150 &(subsystem->fb_shm_info), WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->width),
1151 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1152 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->depth));
1153 XSync(subsystem->display, False);
1154
1155 if (!subsystem->fb_pixmap)
1156 return -1;
1157
1158 values.subwindow_mode = IncludeInferiors;
1159 values.graphics_exposures = False;
1160#if defined(WITH_XDAMAGE)
1161 subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1162 GCSubwindowMode | GCGraphicsExposures, &values);
1163 XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1164#endif
1165 XSync(subsystem->display, False);
1166 return 1;
1167}
1168
1169UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
1170{
1171 Display* display = NULL;
1172 int displayWidth = 0;
1173 int displayHeight = 0;
1174 int numMonitors = 0;
1175
1176 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1177 if (!getenv("DISPLAY"))
1178 {
1179 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1180 setenv("DISPLAY", ":0", 1);
1181 }
1182
1183 display = XOpenDisplay(NULL);
1184
1185 if (!display)
1186 {
1187 WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
1188 return 0;
1189 }
1190
1191 displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1192 displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1193#ifdef WITH_XINERAMA
1194 {
1195#if defined(WITH_XDAMAGE)
1196 int major = 0;
1197 int minor = 0;
1198#endif
1199 int xinerama_event = 0;
1200 int xinerama_error = 0;
1201 XineramaScreenInfo* screens = NULL;
1202
1203 const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
1204 const Bool damage =
1205#if defined(WITH_XDAMAGE)
1206 XDamageQueryVersion(display, &major, &minor);
1207#else
1208 False;
1209#endif
1210
1211 if (xinerama && damage && XineramaIsActive(display))
1212 {
1213 screens = XineramaQueryScreens(display, &numMonitors);
1214
1215 if (numMonitors > (INT64)maxMonitors)
1216 numMonitors = (int)maxMonitors;
1217
1218 if (screens && (numMonitors > 0))
1219 {
1220 for (int index = 0; index < numMonitors; index++)
1221 {
1222 MONITOR_DEF* monitor = &monitors[index];
1223 const XineramaScreenInfo* screen = &screens[index];
1224
1225 monitor->left = screen->x_org;
1226 monitor->top = screen->y_org;
1227 monitor->right = monitor->left + screen->width - 1;
1228 monitor->bottom = monitor->top + screen->height - 1;
1229 monitor->flags = (index == 0) ? 1 : 0;
1230 }
1231 }
1232
1233 XFree(screens);
1234 }
1235 }
1236#endif
1237 XCloseDisplay(display);
1238
1239 if (numMonitors < 1)
1240 {
1241 MONITOR_DEF* monitor = &monitors[0];
1242 numMonitors = 1;
1243
1244 monitor->left = 0;
1245 monitor->top = 0;
1246 monitor->right = displayWidth - 1;
1247 monitor->bottom = displayHeight - 1;
1248 monitor->flags = 1;
1249 }
1250
1251 errno = 0;
1252 return WINPR_ASSERTING_INT_CAST(uint32_t, numMonitors);
1253}
1254
1255static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1256{
1257 int pf_count = 0;
1258 int vi_count = 0;
1259 int nextensions = 0;
1260 char** extensions = NULL;
1261 XVisualInfo* vi = NULL;
1262 XVisualInfo* vis = NULL;
1263 XVisualInfo template = { 0 };
1264 XPixmapFormatValues* pf = NULL;
1265 XPixmapFormatValues* pfs = NULL;
1266
1267 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1268
1269 if (!subsystem)
1270 return -1;
1271
1272 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1273 const int rc = x11_shadow_subsystem_base_init(subsystem);
1274 if (rc < 0)
1275 return rc;
1276
1277 subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRA32
1278 : PIXEL_FORMAT_ARGB32;
1279
1280 if ((subsystem->depth != 24) && (subsystem->depth != 32))
1281 {
1282 WLog_ERR(TAG, "unsupported X11 server color depth: %" PRIu32, subsystem->depth);
1283 return -1;
1284 }
1285
1286 extensions = XListExtensions(subsystem->display, &nextensions);
1287
1288 if (!extensions || (nextensions < 0))
1289 return -1;
1290
1291 for (int i = 0; i < nextensions; i++)
1292 {
1293 if (strcmp(extensions[i], "Composite") == 0)
1294 subsystem->composite = TRUE;
1295 }
1296
1297 XFreeExtensionList(extensions);
1298
1299 if (subsystem->composite)
1300 subsystem->use_xdamage = FALSE;
1301
1302 pfs = XListPixmapFormats(subsystem->display, &pf_count);
1303
1304 if (!pfs)
1305 {
1306 WLog_ERR(TAG, "XListPixmapFormats failed");
1307 return -1;
1308 }
1309
1310 for (int i = 0; i < pf_count; i++)
1311 {
1312 pf = pfs + i;
1313
1314 if (pf->depth == (INT64)subsystem->depth)
1315 {
1316 subsystem->bpp = WINPR_ASSERTING_INT_CAST(uint32_t, pf->bits_per_pixel);
1317 subsystem->scanline_pad = WINPR_ASSERTING_INT_CAST(uint32_t, pf->scanline_pad);
1318 break;
1319 }
1320 }
1321
1322 XFree(pfs);
1323 template.class = TrueColor;
1324 template.screen = subsystem->number;
1325 vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &template,
1326 &vi_count);
1327
1328 if (!vis)
1329 {
1330 WLog_ERR(TAG, "XGetVisualInfo failed");
1331 return -1;
1332 }
1333
1334 for (int i = 0; i < vi_count; i++)
1335 {
1336 vi = vis + i;
1337
1338 if (vi->depth == (INT64)subsystem->depth)
1339 {
1340 subsystem->visual = vi->visual;
1341 break;
1342 }
1343 }
1344
1345 XFree(vis);
1346 XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1347 subsystem->cursorMaxWidth = 256;
1348 subsystem->cursorMaxHeight = 256;
1349 subsystem->cursorPixels =
1350 winpr_aligned_malloc(4ULL * subsystem->cursorMaxWidth * subsystem->cursorMaxHeight, 16);
1351
1352 if (!subsystem->cursorPixels)
1353 return -1;
1354
1355 x11_shadow_query_cursor(subsystem, TRUE);
1356
1357 if (subsystem->use_xfixes)
1358 {
1359 if (x11_shadow_xfixes_init(subsystem) < 0)
1360 subsystem->use_xfixes = FALSE;
1361 }
1362
1363 if (subsystem->use_xinerama)
1364 {
1365 if (x11_shadow_xinerama_init(subsystem) < 0)
1366 subsystem->use_xinerama = FALSE;
1367 }
1368
1369 if (subsystem->use_xshm)
1370 {
1371 if (x11_shadow_xshm_init(subsystem) < 0)
1372 subsystem->use_xshm = FALSE;
1373 }
1374
1375 if (subsystem->use_xdamage)
1376 {
1377 if (x11_shadow_xdamage_init(subsystem) < 0)
1378 subsystem->use_xdamage = FALSE;
1379 }
1380
1381 if (!(subsystem->common.event =
1382 CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1383 return -1;
1384
1385 {
1386 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
1387 virtualScreen->left = 0;
1388 virtualScreen->top = 0;
1389 WINPR_ASSERT(subsystem->width <= INT32_MAX);
1390 WINPR_ASSERT(subsystem->height <= INT32_MAX);
1391 virtualScreen->right = (INT32)subsystem->width - 1;
1392 virtualScreen->bottom = (INT32)subsystem->height - 1;
1393 virtualScreen->flags = 1;
1394 WLog_INFO(TAG,
1395 "X11 Extensions: XFixes: %" PRId32 " Xinerama: %" PRId32 " XDamage: %" PRId32
1396 " XShm: %" PRId32 "",
1397 subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1398 subsystem->use_xshm);
1399 }
1400
1401 return 1;
1402}
1403
1404static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1405{
1406 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1407
1408 if (!subsystem)
1409 return -1;
1410
1411 if (subsystem->display)
1412 {
1413 XCloseDisplay(subsystem->display);
1414 subsystem->display = NULL;
1415 }
1416
1417 if (subsystem->common.event)
1418 {
1419 (void)CloseHandle(subsystem->common.event);
1420 subsystem->common.event = NULL;
1421 }
1422
1423 if (subsystem->cursorPixels)
1424 {
1425 winpr_aligned_free(subsystem->cursorPixels);
1426 subsystem->cursorPixels = NULL;
1427 }
1428
1429 return 1;
1430}
1431
1432static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1433{
1434 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1435
1436 if (!subsystem)
1437 return -1;
1438
1439 if (!(subsystem->thread =
1440 CreateThread(NULL, 0, x11_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
1441 {
1442 WLog_ERR(TAG, "Failed to create thread");
1443 return -1;
1444 }
1445
1446 return 1;
1447}
1448
1449static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1450{
1451 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1452
1453 if (!subsystem)
1454 return -1;
1455
1456 if (subsystem->thread)
1457 {
1458 if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1459 (void)WaitForSingleObject(subsystem->thread, INFINITE);
1460
1461 (void)CloseHandle(subsystem->thread);
1462 subsystem->thread = NULL;
1463 }
1464
1465 return 1;
1466}
1467
1468static rdpShadowSubsystem* x11_shadow_subsystem_new(void)
1469{
1470 x11ShadowSubsystem* subsystem = NULL;
1471 subsystem = (x11ShadowSubsystem*)calloc(1, sizeof(x11ShadowSubsystem));
1472
1473 if (!subsystem)
1474 return NULL;
1475
1476#ifdef WITH_PAM
1477 subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1478#endif
1479 subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1480 subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1481 subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1482 subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1483 subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1484 subsystem->composite = FALSE;
1485 subsystem->use_xshm = FALSE; /* temporarily disabled */
1486 subsystem->use_xfixes = TRUE;
1487 subsystem->use_xdamage = FALSE;
1488 subsystem->use_xinerama = TRUE;
1489 return (rdpShadowSubsystem*)subsystem;
1490}
1491
1492static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1493{
1494 if (!subsystem)
1495 return;
1496
1497 x11_shadow_subsystem_uninit(subsystem);
1498 free(subsystem);
1499}
1500
1501FREERDP_ENTRY_POINT(FREERDP_API const char* ShadowSubsystemName(void))
1502{
1503 return "X11";
1504}
1505
1506FREERDP_ENTRY_POINT(FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
1507{
1508 if (!pEntryPoints)
1509 return -1;
1510
1511 pEntryPoints->New = x11_shadow_subsystem_new;
1512 pEntryPoints->Free = x11_shadow_subsystem_free;
1513 pEntryPoints->Init = x11_shadow_subsystem_init;
1514 pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1515 pEntryPoints->Start = x11_shadow_subsystem_start;
1516 pEntryPoints->Stop = x11_shadow_subsystem_stop;
1517 pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;
1518 return 1;
1519}