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