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