29 #include <sys/select.h>
30 #include <sys/signal.h>
33 #include <X11/Xutil.h>
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>
43 #include <freerdp/log.h>
44 #include <freerdp/codec/color.h>
45 #include <freerdp/codec/region.h>
47 #include "x11_shadow.h"
49 #define TAG SERVER_TAG("shadow.x11")
51 static UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors);
55 #include <security/pam_appl.h>
62 } SHADOW_PAM_AUTH_DATA;
69 SHADOW_PAM_AUTH_DATA appdata;
70 } SHADOW_PAM_AUTH_INFO;
72 static int x11_shadow_pam_conv(
int num_msg,
const struct pam_message** msg,
73 struct pam_response** resp,
void* appdata_ptr)
75 int pam_status = PAM_CONV_ERR;
76 SHADOW_PAM_AUTH_DATA* appdata = NULL;
77 struct pam_response* response = NULL;
78 WINPR_ASSERT(num_msg >= 0);
79 appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
80 WINPR_ASSERT(appdata);
82 if (!(response = (
struct pam_response*)calloc((
size_t)num_msg,
sizeof(
struct pam_response))))
85 for (
int index = 0; index < num_msg; index++)
87 switch (msg[index]->msg_style)
89 case PAM_PROMPT_ECHO_ON:
90 response[index].resp = _strdup(appdata->user);
92 if (!response[index].resp)
95 response[index].resp_retcode = PAM_SUCCESS;
98 case PAM_PROMPT_ECHO_OFF:
99 response[index].resp = _strdup(appdata->password);
101 if (!response[index].resp)
104 response[index].resp_retcode = PAM_SUCCESS;
108 pam_status = PAM_CONV_ERR;
117 for (
int index = 0; index < num_msg; ++index)
119 if (response[index].resp)
121 memset(response[index].resp, 0, strlen(response[index].resp));
122 free(response[index].resp);
126 memset(response, 0,
sizeof(
struct pam_response) * (
size_t)num_msg);
132 static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
134 const char* base =
"/etc/pam.d";
135 const char* hints[] = {
"lightdm",
"gdm",
"xdm",
"login",
"sshd" };
137 for (
size_t x = 0; x < ARRAYSIZE(hints); x++)
139 char path[MAX_PATH] = { 0 };
140 const char* hint = hints[x];
142 (void)_snprintf(path,
sizeof(path),
"%s/%s", base, hint);
143 if (winpr_PathFileExists(path))
146 info->service_name = _strdup(hint);
147 return info->service_name != NULL;
150 WLog_WARN(TAG,
"Could not determine PAM service name");
154 static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
155 const char* user,
const char* domain,
const char* password)
158 SHADOW_PAM_AUTH_INFO info = { 0 };
159 WINPR_UNUSED(subsystem);
160 WINPR_UNUSED(client);
162 if (!x11_shadow_pam_get_service_name(&info))
165 info.appdata.user = user;
166 info.appdata.domain = domain;
167 info.appdata.password = password;
168 info.pamc.conv = &x11_shadow_pam_conv;
169 info.pamc.appdata_ptr = &info.appdata;
170 pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
172 if (pam_status != PAM_SUCCESS)
174 WLog_ERR(TAG,
"pam_start failure: %s", pam_strerror(info.handle, pam_status));
178 pam_status = pam_authenticate(info.handle, 0);
180 if (pam_status != PAM_SUCCESS)
182 WLog_ERR(TAG,
"pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
186 pam_status = pam_acct_mgmt(info.handle, 0);
188 if (pam_status != PAM_SUCCESS)
190 WLog_ERR(TAG,
"pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
199 static BOOL x11_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
200 rdpShadowClient* client, UINT32 flags)
203 WLog_WARN(TAG,
"not implemented");
207 static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
208 UINT16 flags, UINT8 code)
211 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
215 BOOL extended = FALSE;
217 if (!client || !subsystem)
220 if (flags & KBD_FLAGS_EXTENDED)
227 vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
232 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
236 XLockDisplay(x11->display);
237 XTestGrabControl(x11->display, True);
239 if (flags & KBD_FLAGS_RELEASE)
240 XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
242 XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
244 XTestGrabControl(x11->display, False);
245 XFlush(x11->display);
246 XUnlockDisplay(x11->display);
253 static BOOL x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
254 rdpShadowClient* client, UINT16 flags,
258 WLog_WARN(TAG,
"not implemented");
262 static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
263 UINT16 flags, UINT16 x, UINT16 y)
266 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
267 unsigned int button = 0;
269 rdpShadowServer* server = NULL;
270 rdpShadowSurface* surface = NULL;
272 if (!subsystem || !client)
275 server = subsystem->server;
280 surface = server->surface;
285 x11->lastMouseClient = client;
288 XLockDisplay(x11->display);
289 XTestGrabControl(x11->display, True);
291 if (flags & PTR_FLAGS_WHEEL)
293 BOOL negative = FALSE;
295 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
298 button = (negative) ? 5 : 4;
299 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
300 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
302 else if (flags & PTR_FLAGS_HWHEEL)
304 BOOL negative = FALSE;
306 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
309 button = (negative) ? 7 : 6;
310 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
311 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
315 if (flags & PTR_FLAGS_MOVE)
316 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
318 if (flags & PTR_FLAGS_BUTTON1)
320 else if (flags & PTR_FLAGS_BUTTON2)
322 else if (flags & PTR_FLAGS_BUTTON3)
325 if (flags & PTR_FLAGS_DOWN)
329 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
332 XTestGrabControl(x11->display, False);
333 XFlush(x11->display);
334 XUnlockDisplay(x11->display);
339 static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
340 rdpShadowClient* client, UINT16 flags, UINT16 x,
344 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
347 rdpShadowServer* server = NULL;
348 rdpShadowSurface* surface = NULL;
350 if (!subsystem || !client)
353 server = subsystem->server;
358 surface = server->surface;
363 x11->lastMouseClient = client;
366 XLockDisplay(x11->display);
367 XTestGrabControl(x11->display, True);
368 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
370 if (flags & PTR_XFLAGS_BUTTON1)
372 else if (flags & PTR_XFLAGS_BUTTON2)
375 if (flags & PTR_XFLAGS_DOWN)
379 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
381 XTestGrabControl(x11->display, False);
382 XFlush(x11->display);
383 XUnlockDisplay(x11->display);
388 static void x11_shadow_message_free(UINT32
id, SHADOW_MSG_OUT* msg)
392 case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
396 case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
403 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"",
id);
409 static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
411 UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
412 rdpShadowServer* server = NULL;
416 if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
419 templateMsg.xPos = subsystem->common.pointerX;
420 templateMsg.yPos = subsystem->common.pointerY;
421 templateMsg.common.Free = x11_shadow_message_free;
422 server = subsystem->common.server;
423 ArrayList_Lock(server->clients);
425 for (
size_t index = 0; index < ArrayList_Count(server->clients); index++)
428 rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
431 if (client == subsystem->lastMouseClient)
434 msg = malloc(
sizeof(templateMsg));
442 memcpy(msg, &templateMsg,
sizeof(templateMsg));
444 if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
448 ArrayList_Unlock(server->clients);
452 static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
455 UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
462 msg->xHot = subsystem->cursorHotX;
463 msg->yHot = subsystem->cursorHotY;
464 msg->width = subsystem->cursorWidth;
465 msg->height = subsystem->cursorHeight;
467 if (shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
468 subsystem->cursorPixels, subsystem->format, TRUE, msg->width, msg->height, msg) < 0)
474 msg->common.Free = x11_shadow_message_free;
475 return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
481 static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
486 rdpShadowServer* server = NULL;
487 rdpShadowSurface* surface = NULL;
488 server = subsystem->common.server;
489 surface = server->surface;
494 UINT32* pDstPixel = NULL;
495 XFixesCursorImage* ci = NULL;
496 XLockDisplay(subsystem->display);
497 ci = XFixesGetCursorImage(subsystem->display);
498 XUnlockDisplay(subsystem->display);
506 if (ci->width > subsystem->cursorMaxWidth)
509 if (ci->height > subsystem->cursorMaxHeight)
512 subsystem->cursorHotX = ci->xhot;
513 subsystem->cursorHotY = ci->yhot;
514 subsystem->cursorWidth = ci->width;
515 subsystem->cursorHeight = ci->height;
516 subsystem->cursorId = ci->cursor_serial;
517 n = ci->width * ci->height;
518 pDstPixel = (UINT32*)subsystem->cursorPixels;
520 for (
int k = 0; k < n; k++)
523 *pDstPixel++ = (UINT32)ci->pixels[k];
527 x11_shadow_pointer_alpha_update(subsystem);
539 XLockDisplay(subsystem->display);
541 if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
542 &root_y, &win_x, &win_y, &mask))
544 XUnlockDisplay(subsystem->display);
548 XUnlockDisplay(subsystem->display);
560 if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
562 subsystem->common.pointerX = WINPR_ASSERTING_INT_CAST(UINT32, x);
563 subsystem->common.pointerY = WINPR_ASSERTING_INT_CAST(UINT32, y);
564 x11_shadow_pointer_position_update(subsystem);
570 static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
572 if (xevent->type == MotionNotify)
577 else if (xevent->type == subsystem->xfixes_cursor_notify_event)
579 x11_shadow_query_cursor(subsystem, TRUE);
590 static void x11_shadow_validate_region(x11ShadowSubsystem* subsystem,
int x,
int y,
int width,
595 if (!subsystem->use_xfixes || !subsystem->use_xdamage)
598 region.x = WINPR_ASSERTING_INT_CAST(INT16, x);
599 region.y = WINPR_ASSERTING_INT_CAST(INT16, y);
600 region.width = WINPR_ASSERTING_INT_CAST(UINT16, width);
601 region.height = WINPR_ASSERTING_INT_CAST(UINT16, height);
602 #if defined(WITH_XFIXES) && defined(WITH_XDAMAGE)
603 XLockDisplay(subsystem->display);
604 XFixesSetRegion(subsystem->display, subsystem->xdamage_region, ®ion, 1);
605 XDamageSubtract(subsystem->display, subsystem->xdamage, subsystem->xdamage_region, None);
606 XUnlockDisplay(subsystem->display);
610 static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
620 BYTE* pSrcData = NULL;
621 BYTE* pDstData = NULL;
626 rdpShadowSurface* surface = NULL;
631 surface = subsystem->common.server->surface;
634 nWidth = subsystem->cursorWidth;
635 nHeight = subsystem->cursorHeight;
636 nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
637 nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
639 if (nXDst >= surface->width)
649 nXSrc = (UINT32)nXDst;
654 if (nYDst >= surface->height)
661 if (nYDst >= nHeight)
664 nYSrc = (UINT32)nYDst;
669 if ((nXDst + nWidth) > surface->width)
670 nWidth = (nXDst > surface->width) ? 0 : (UINT32)(surface->width - nXDst);
672 if ((nYDst + nHeight) > surface->height)
673 nHeight = (nYDst > surface->height) ? 0 : (UINT32)(surface->height - nYDst);
675 pSrcData = subsystem->cursorPixels;
676 nSrcStep = subsystem->cursorWidth * 4;
677 pDstData = surface->data;
678 nDstStep = surface->scanline;
680 for (
size_t y = 0; y < nHeight; y++)
682 const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
683 BYTE* pDstPixel = &pDstData[((WINPR_ASSERTING_INT_CAST(uint32_t, nYDst) + y) * nDstStep) +
684 (4ULL * WINPR_ASSERTING_INT_CAST(uint32_t, nXDst))];
686 for (
size_t x = 0; x < nWidth; x++)
701 pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
702 pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
703 pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
714 static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
716 XWindowAttributes attr;
717 XLockDisplay(subsystem->display);
718 XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
719 XUnlockDisplay(subsystem->display);
721 if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
723 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
726 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
727 if (!shadow_screen_resize(subsystem->common.server->screen))
730 WINPR_ASSERT(attr.width > 0);
731 WINPR_ASSERT(attr.height > 0);
733 subsystem->width = (UINT32)attr.width;
734 subsystem->height = (UINT32)attr.height;
736 virtualScreen->left = 0;
737 virtualScreen->top = 0;
738 virtualScreen->right = attr.width - 1;
739 virtualScreen->bottom = attr.height - 1;
740 virtualScreen->flags = 1;
747 static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
750 XGetErrorText(display, event->error_code, (
char*)&msg,
sizeof(msg));
751 WLog_ERR(TAG,
"X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
752 event->error_code, event->request_code, event->minor_code);
755 if (event->error_code != BadMatch)
763 static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
772 XImage* image = NULL;
773 rdpShadowServer* server = NULL;
774 rdpShadowSurface* surface = NULL;
778 server = subsystem->common.server;
779 surface = server->surface;
780 count = ArrayList_Count(server->clients);
785 EnterCriticalSection(&surface->lock);
786 surfaceRect.left = 0;
789 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->width);
790 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->height);
791 LeaveCriticalSection(&surface->lock);
793 XLockDisplay(subsystem->display);
798 XSetErrorHandler(x11_shadow_error_handler_for_capture);
799 #if defined(WITH_XDAMAGE)
800 if (subsystem->use_xshm)
802 image = subsystem->fb_image;
803 XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
804 subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
806 EnterCriticalSection(&surface->lock);
807 status = shadow_capture_compare_with_format(
808 surface->data, surface->format, surface->scanline, surface->width, surface->height,
809 (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format,
810 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
811 LeaveCriticalSection(&surface->lock);
816 EnterCriticalSection(&surface->lock);
817 image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
818 surface->width, surface->height, AllPlanes, ZPixmap);
822 status = shadow_capture_compare_with_format(
823 surface->data, surface->format, surface->scanline, surface->width, surface->height,
824 (BYTE*)image->data, subsystem->format,
825 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
827 LeaveCriticalSection(&surface->lock);
839 XSetErrorHandler(NULL);
840 XSync(subsystem->display, False);
841 XUnlockDisplay(subsystem->display);
846 EnterCriticalSection(&surface->lock);
847 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
848 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
849 empty = region16_is_empty(&(surface->invalidRegion));
850 LeaveCriticalSection(&surface->lock);
855 EnterCriticalSection(&surface->lock);
856 extents = region16_extents(&(surface->invalidRegion));
859 width = extents->right - extents->left;
860 height = extents->bottom - extents->top;
862 WINPR_ASSERT(image->bytes_per_line >= 0);
863 WINPR_ASSERT(width >= 0);
864 WINPR_ASSERT(height >= 0);
865 success = freerdp_image_copy_no_overlap(
866 surface->data, surface->format, surface->scanline,
867 WINPR_ASSERTING_INT_CAST(uint32_t, x), WINPR_ASSERTING_INT_CAST(uint32_t, y),
868 WINPR_ASSERTING_INT_CAST(uint32_t, width),
869 WINPR_ASSERTING_INT_CAST(uint32_t, height), (BYTE*)image->data, subsystem->format,
870 WINPR_ASSERTING_INT_CAST(uint32_t, image->bytes_per_line),
871 WINPR_ASSERTING_INT_CAST(UINT32, x), WINPR_ASSERTING_INT_CAST(UINT32, y), NULL,
873 LeaveCriticalSection(&surface->lock);
878 count = ArrayList_Count(server->clients);
879 shadow_subsystem_frame_update(&subsystem->common);
883 rdpShadowClient* client = NULL;
884 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
887 subsystem->common.captureFrameRate =
888 shadow_encoder_preferred_fps(client->encoder);
891 EnterCriticalSection(&surface->lock);
892 region16_clear(&(surface->invalidRegion));
893 LeaveCriticalSection(&surface->lock);
899 if (!subsystem->use_xshm && image)
900 XDestroyImage(image);
904 XSetErrorHandler(NULL);
905 XSync(subsystem->display, False);
906 XUnlockDisplay(subsystem->display);
912 static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
916 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
917 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
921 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"", message->id);
926 message->Free(message);
931 static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
933 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
937 DWORD dwInterval = 0;
938 UINT64 frameTime = 0;
942 MsgPipe = subsystem->common.MsgPipe;
944 events[nCount++] = subsystem->common.event;
945 events[nCount++] = MessageQueue_Event(MsgPipe->In);
946 subsystem->common.captureFrameRate = 16;
947 dwInterval = 1000 / subsystem->common.captureFrameRate;
948 frameTime = GetTickCount64() + dwInterval;
952 const UINT64 cTime = GetTickCount64();
953 const DWORD dwTimeout =
954 (DWORD)((cTime > frameTime) ? 0 : MIN(UINT32_MAX, frameTime - cTime));
955 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
957 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
959 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
961 if (message.id == WMQ_QUIT)
964 x11_shadow_subsystem_process_message(subsystem, &message);
968 if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
970 XLockDisplay(subsystem->display);
972 if (XEventsQueued(subsystem->display, QueuedAlready))
974 XNextEvent(subsystem->display, &xevent);
975 x11_shadow_handle_xevent(subsystem, &xevent);
978 XUnlockDisplay(subsystem->display);
981 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
983 x11_shadow_check_resize(subsystem);
984 x11_shadow_screen_grab(subsystem);
985 x11_shadow_query_cursor(subsystem, FALSE);
986 dwInterval = 1000 / subsystem->common.captureFrameRate;
987 frameTime += dwInterval;
995 static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
997 if (subsystem->display)
1000 if (!getenv(
"DISPLAY"))
1001 setenv(
"DISPLAY",
":0", 1);
1003 if (!XInitThreads())
1006 subsystem->display = XOpenDisplay(NULL);
1008 if (!subsystem->display)
1010 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
1014 subsystem->xfds = ConnectionNumber(subsystem->display);
1015 subsystem->number = DefaultScreen(subsystem->display);
1016 subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
1017 subsystem->depth = WINPR_ASSERTING_INT_CAST(UINT32, DefaultDepthOfScreen(subsystem->screen));
1018 subsystem->width = WINPR_ASSERTING_INT_CAST(UINT32, WidthOfScreen(subsystem->screen));
1019 subsystem->height = WINPR_ASSERTING_INT_CAST(UINT32, HeightOfScreen(subsystem->screen));
1020 subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
1024 static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
1027 int xfixes_event = 0;
1028 int xfixes_error = 0;
1032 if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
1035 if (!XFixesQueryVersion(subsystem->display, &major, &minor))
1038 subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
1039 XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
1040 XFixesDisplayCursorNotifyMask);
1047 static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1049 #ifdef WITH_XINERAMA
1050 int xinerama_event = 0;
1051 int xinerama_error = 0;
1053 const int rc = x11_shadow_subsystem_base_init(subsystem);
1057 if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1060 #if defined(WITH_XDAMAGE)
1063 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1067 if (!XineramaIsActive(subsystem->display))
1076 static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1081 int damage_event = 0;
1082 int damage_error = 0;
1084 if (!subsystem->use_xfixes)
1087 if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1090 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1096 subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1097 subsystem->xdamage =
1098 XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1100 if (!subsystem->xdamage)
1104 subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
1106 if (!subsystem->xdamage_region)
1116 static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1123 if (!XShmQueryExtension(subsystem->display))
1126 if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1132 subsystem->fb_shm_info.shmid = -1;
1133 subsystem->fb_shm_info.shmaddr = (
char*)-1;
1134 subsystem->fb_shm_info.readOnly = False;
1135 subsystem->fb_image =
1136 XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
1137 &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1139 if (!subsystem->fb_image)
1141 WLog_ERR(TAG,
"XShmCreateImage failed");
1145 subsystem->fb_shm_info.shmid =
1147 1ull * WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->bytes_per_line) *
1148 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1151 if (subsystem->fb_shm_info.shmid == -1)
1153 WLog_ERR(TAG,
"shmget failed");
1157 subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
1158 subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1160 if (subsystem->fb_shm_info.shmaddr == ((
char*)-1))
1162 WLog_ERR(TAG,
"shmat failed");
1166 if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1169 XSync(subsystem->display, False);
1170 shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
1171 subsystem->fb_pixmap = XShmCreatePixmap(
1172 subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1173 &(subsystem->fb_shm_info), WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->width),
1174 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1175 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->depth));
1176 XSync(subsystem->display, False);
1178 if (!subsystem->fb_pixmap)
1181 values.subwindow_mode = IncludeInferiors;
1182 values.graphics_exposures = False;
1183 #if defined(WITH_XDAMAGE)
1184 subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1185 GCSubwindowMode | GCGraphicsExposures, &values);
1186 XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1188 XSync(subsystem->display, False);
1192 UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
1194 Display* display = NULL;
1195 int displayWidth = 0;
1196 int displayHeight = 0;
1197 int numMonitors = 0;
1199 if (!getenv(
"DISPLAY"))
1200 setenv(
"DISPLAY",
":0", 1);
1202 display = XOpenDisplay(NULL);
1206 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
1210 displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1211 displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1212 #ifdef WITH_XINERAMA
1214 #if defined(WITH_XDAMAGE)
1218 int xinerama_event = 0;
1219 int xinerama_error = 0;
1220 XineramaScreenInfo* screens = NULL;
1222 const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
1224 #if defined(WITH_XDAMAGE)
1225 XDamageQueryVersion(display, &major, &minor);
1230 if (xinerama && damage && XineramaIsActive(display))
1232 screens = XineramaQueryScreens(display, &numMonitors);
1234 if (numMonitors > (INT64)maxMonitors)
1235 numMonitors = (int)maxMonitors;
1237 if (screens && (numMonitors > 0))
1239 for (
int index = 0; index < numMonitors; index++)
1242 const XineramaScreenInfo* screen = &screens[index];
1244 monitor->left = screen->x_org;
1245 monitor->top = screen->y_org;
1246 monitor->right = monitor->left + screen->width - 1;
1247 monitor->bottom = monitor->top + screen->height - 1;
1248 monitor->flags = (index == 0) ? 1 : 0;
1256 XCloseDisplay(display);
1258 if (numMonitors < 1)
1265 monitor->right = displayWidth - 1;
1266 monitor->bottom = displayHeight - 1;
1271 return WINPR_ASSERTING_INT_CAST(uint32_t, numMonitors);
1274 static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1278 int nextensions = 0;
1279 char** extensions = NULL;
1280 XVisualInfo* vi = NULL;
1281 XVisualInfo* vis = NULL;
1282 XVisualInfo
template = { 0 };
1283 XPixmapFormatValues* pf = NULL;
1284 XPixmapFormatValues* pfs = NULL;
1286 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1291 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1292 const int rc = x11_shadow_subsystem_base_init(subsystem);
1296 subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRA32
1297 : PIXEL_FORMAT_ARGB32;
1299 if ((subsystem->depth != 24) && (subsystem->depth != 32))
1301 WLog_ERR(TAG,
"unsupported X11 server color depth: %" PRIu32, subsystem->depth);
1305 extensions = XListExtensions(subsystem->display, &nextensions);
1307 if (!extensions || (nextensions < 0))
1310 for (
int i = 0; i < nextensions; i++)
1312 if (strcmp(extensions[i],
"Composite") == 0)
1313 subsystem->composite = TRUE;
1316 XFreeExtensionList(extensions);
1318 if (subsystem->composite)
1319 subsystem->use_xdamage = FALSE;
1321 pfs = XListPixmapFormats(subsystem->display, &pf_count);
1325 WLog_ERR(TAG,
"XListPixmapFormats failed");
1329 for (
int i = 0; i < pf_count; i++)
1333 if (pf->depth == (INT64)subsystem->depth)
1335 subsystem->bpp = WINPR_ASSERTING_INT_CAST(uint32_t, pf->bits_per_pixel);
1336 subsystem->scanline_pad = WINPR_ASSERTING_INT_CAST(uint32_t, pf->scanline_pad);
1342 template.class = TrueColor;
1343 template.screen = subsystem->number;
1344 vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &
template,
1349 WLog_ERR(TAG,
"XGetVisualInfo failed");
1353 for (
int i = 0; i < vi_count; i++)
1357 if (vi->depth == (INT64)subsystem->depth)
1359 subsystem->visual = vi->visual;
1365 XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1366 subsystem->cursorMaxWidth = 256;
1367 subsystem->cursorMaxHeight = 256;
1368 subsystem->cursorPixels =
1369 winpr_aligned_malloc(4ULL * subsystem->cursorMaxWidth * subsystem->cursorMaxHeight, 16);
1371 if (!subsystem->cursorPixels)
1374 x11_shadow_query_cursor(subsystem, TRUE);
1376 if (subsystem->use_xfixes)
1378 if (x11_shadow_xfixes_init(subsystem) < 0)
1379 subsystem->use_xfixes = FALSE;
1382 if (subsystem->use_xinerama)
1384 if (x11_shadow_xinerama_init(subsystem) < 0)
1385 subsystem->use_xinerama = FALSE;
1388 if (subsystem->use_xshm)
1390 if (x11_shadow_xshm_init(subsystem) < 0)
1391 subsystem->use_xshm = FALSE;
1394 if (subsystem->use_xdamage)
1396 if (x11_shadow_xdamage_init(subsystem) < 0)
1397 subsystem->use_xdamage = FALSE;
1400 if (!(subsystem->common.event =
1401 CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1405 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
1406 virtualScreen->left = 0;
1407 virtualScreen->top = 0;
1408 WINPR_ASSERT(subsystem->width <= INT32_MAX);
1409 WINPR_ASSERT(subsystem->height <= INT32_MAX);
1410 virtualScreen->right = (INT32)subsystem->width - 1;
1411 virtualScreen->bottom = (INT32)subsystem->height - 1;
1412 virtualScreen->flags = 1;
1414 "X11 Extensions: XFixes: %" PRId32
" Xinerama: %" PRId32
" XDamage: %" PRId32
1415 " XShm: %" PRId32
"",
1416 subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1417 subsystem->use_xshm);
1423 static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1425 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1430 if (subsystem->display)
1432 XCloseDisplay(subsystem->display);
1433 subsystem->display = NULL;
1436 if (subsystem->common.event)
1438 (void)CloseHandle(subsystem->common.event);
1439 subsystem->common.event = NULL;
1442 if (subsystem->cursorPixels)
1444 winpr_aligned_free(subsystem->cursorPixels);
1445 subsystem->cursorPixels = NULL;
1451 static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1453 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1458 if (!(subsystem->thread =
1459 CreateThread(NULL, 0, x11_shadow_subsystem_thread, (
void*)subsystem, 0, NULL)))
1461 WLog_ERR(TAG,
"Failed to create thread");
1468 static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1470 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1475 if (subsystem->thread)
1477 if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1478 (
void)WaitForSingleObject(subsystem->thread, INFINITE);
1480 (void)CloseHandle(subsystem->thread);
1481 subsystem->thread = NULL;
1487 static rdpShadowSubsystem* x11_shadow_subsystem_new(
void)
1489 x11ShadowSubsystem* subsystem = NULL;
1490 subsystem = (x11ShadowSubsystem*)calloc(1,
sizeof(x11ShadowSubsystem));
1496 subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1498 subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1499 subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1500 subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1501 subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1502 subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1503 subsystem->composite = FALSE;
1504 subsystem->use_xshm = FALSE;
1505 subsystem->use_xfixes = TRUE;
1506 subsystem->use_xdamage = FALSE;
1507 subsystem->use_xinerama = TRUE;
1508 return (rdpShadowSubsystem*)subsystem;
1511 static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1516 x11_shadow_subsystem_uninit(subsystem);
1520 FREERDP_ENTRY_POINT(FREERDP_API
const char* ShadowSubsystemName(
void))
1525 FREERDP_ENTRY_POINT(FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
1530 pEntryPoints->New = x11_shadow_subsystem_new;
1531 pEntryPoints->Free = x11_shadow_subsystem_free;
1532 pEntryPoints->Init = x11_shadow_subsystem_init;
1533 pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1534 pEntryPoints->Start = x11_shadow_subsystem_start;
1535 pEntryPoints->Stop = x11_shadow_subsystem_stop;
1536 pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;