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/path.h>
38 #include <winpr/synch.h>
39 #include <winpr/image.h>
40 #include <winpr/sysinfo.h>
42 #include <freerdp/log.h>
43 #include <freerdp/codec/color.h>
44 #include <freerdp/codec/region.h>
46 #include "x11_shadow.h"
48 #define TAG SERVER_TAG("shadow.x11")
50 static UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors);
54 #include <security/pam_appl.h>
61 } SHADOW_PAM_AUTH_DATA;
68 SHADOW_PAM_AUTH_DATA appdata;
69 } SHADOW_PAM_AUTH_INFO;
71 static int x11_shadow_pam_conv(
int num_msg,
const struct pam_message** msg,
72 struct pam_response** resp,
void* appdata_ptr)
74 int pam_status = PAM_CONV_ERR;
75 SHADOW_PAM_AUTH_DATA* appdata = NULL;
76 struct pam_response* response = NULL;
77 WINPR_ASSERT(num_msg >= 0);
78 appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
79 WINPR_ASSERT(appdata);
81 if (!(response = (
struct pam_response*)calloc((
size_t)num_msg,
sizeof(
struct pam_response))))
84 for (
int index = 0; index < num_msg; index++)
86 switch (msg[index]->msg_style)
88 case PAM_PROMPT_ECHO_ON:
89 response[index].resp = _strdup(appdata->user);
91 if (!response[index].resp)
94 response[index].resp_retcode = PAM_SUCCESS;
97 case PAM_PROMPT_ECHO_OFF:
98 response[index].resp = _strdup(appdata->password);
100 if (!response[index].resp)
103 response[index].resp_retcode = PAM_SUCCESS;
107 pam_status = PAM_CONV_ERR;
116 for (
int index = 0; index < num_msg; ++index)
118 if (response[index].resp)
120 memset(response[index].resp, 0, strlen(response[index].resp));
121 free(response[index].resp);
125 memset(response, 0,
sizeof(
struct pam_response) * (
size_t)num_msg);
131 static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
133 const char* base =
"/etc/pam.d";
134 const char* hints[] = {
"lightdm",
"gdm",
"xdm",
"login",
"sshd" };
136 for (
size_t x = 0; x < ARRAYSIZE(hints); x++)
138 char path[MAX_PATH] = { 0 };
139 const char* hint = hints[x];
141 (void)_snprintf(path,
sizeof(path),
"%s/%s", base, hint);
142 if (winpr_PathFileExists(path))
145 info->service_name = _strdup(hint);
146 return info->service_name != NULL;
149 WLog_WARN(TAG,
"Could not determine PAM service name");
153 static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
154 const char* user,
const char* domain,
const char* password)
157 SHADOW_PAM_AUTH_INFO info = { 0 };
158 WINPR_UNUSED(subsystem);
159 WINPR_UNUSED(client);
161 if (!x11_shadow_pam_get_service_name(&info))
164 info.appdata.user = user;
165 info.appdata.domain = domain;
166 info.appdata.password = password;
167 info.pamc.conv = &x11_shadow_pam_conv;
168 info.pamc.appdata_ptr = &info.appdata;
169 pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
171 if (pam_status != PAM_SUCCESS)
173 WLog_ERR(TAG,
"pam_start failure: %s", pam_strerror(info.handle, pam_status));
177 pam_status = pam_authenticate(info.handle, 0);
179 if (pam_status != PAM_SUCCESS)
181 WLog_ERR(TAG,
"pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
185 pam_status = pam_acct_mgmt(info.handle, 0);
187 if (pam_status != PAM_SUCCESS)
189 WLog_ERR(TAG,
"pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
198 static BOOL x11_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
199 rdpShadowClient* client, UINT32 flags)
202 WLog_WARN(TAG,
"not implemented");
206 static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
207 UINT16 flags, UINT8 code)
210 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
214 BOOL extended = FALSE;
216 if (!client || !subsystem)
219 if (flags & KBD_FLAGS_EXTENDED)
226 vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
231 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
235 XLockDisplay(x11->display);
236 XTestGrabControl(x11->display, True);
238 if (flags & KBD_FLAGS_RELEASE)
239 XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
241 XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
243 XTestGrabControl(x11->display, False);
244 XFlush(x11->display);
245 XUnlockDisplay(x11->display);
252 static BOOL x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
253 rdpShadowClient* client, UINT16 flags,
257 WLog_WARN(TAG,
"not implemented");
261 static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
262 UINT16 flags, UINT16 x, UINT16 y)
265 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
266 unsigned int button = 0;
268 rdpShadowServer* server = NULL;
269 rdpShadowSurface* surface = NULL;
271 if (!subsystem || !client)
274 server = subsystem->server;
279 surface = server->surface;
284 x11->lastMouseClient = client;
287 XLockDisplay(x11->display);
288 XTestGrabControl(x11->display, True);
290 if (flags & PTR_FLAGS_WHEEL)
292 BOOL negative = FALSE;
294 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
297 button = (negative) ? 5 : 4;
298 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
299 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
301 else if (flags & PTR_FLAGS_HWHEEL)
303 BOOL negative = FALSE;
305 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
308 button = (negative) ? 7 : 6;
309 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
310 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
314 if (flags & PTR_FLAGS_MOVE)
315 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
317 if (flags & PTR_FLAGS_BUTTON1)
319 else if (flags & PTR_FLAGS_BUTTON2)
321 else if (flags & PTR_FLAGS_BUTTON3)
324 if (flags & PTR_FLAGS_DOWN)
328 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
331 XTestGrabControl(x11->display, False);
332 XFlush(x11->display);
333 XUnlockDisplay(x11->display);
338 static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
339 rdpShadowClient* client, UINT16 flags, UINT16 x,
343 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
346 rdpShadowServer* server = NULL;
347 rdpShadowSurface* surface = NULL;
349 if (!subsystem || !client)
352 server = subsystem->server;
357 surface = server->surface;
362 x11->lastMouseClient = client;
365 XLockDisplay(x11->display);
366 XTestGrabControl(x11->display, True);
367 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
369 if (flags & PTR_XFLAGS_BUTTON1)
371 else if (flags & PTR_XFLAGS_BUTTON2)
374 if (flags & PTR_XFLAGS_DOWN)
378 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
380 XTestGrabControl(x11->display, False);
381 XFlush(x11->display);
382 XUnlockDisplay(x11->display);
387 static void x11_shadow_message_free(UINT32
id, SHADOW_MSG_OUT* msg)
391 case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
395 case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
402 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"",
id);
408 static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
410 UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
411 rdpShadowServer* server = NULL;
415 if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
418 templateMsg.xPos = subsystem->common.pointerX;
419 templateMsg.yPos = subsystem->common.pointerY;
420 templateMsg.common.Free = x11_shadow_message_free;
421 server = subsystem->common.server;
422 ArrayList_Lock(server->clients);
424 for (
size_t index = 0; index < ArrayList_Count(server->clients); index++)
427 rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
430 if (client == subsystem->lastMouseClient)
433 msg = malloc(
sizeof(templateMsg));
441 memcpy(msg, &templateMsg,
sizeof(templateMsg));
443 if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
447 ArrayList_Unlock(server->clients);
451 static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
454 UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
461 msg->xHot = subsystem->cursorHotX;
462 msg->yHot = subsystem->cursorHotY;
463 msg->width = subsystem->cursorWidth;
464 msg->height = subsystem->cursorHeight;
466 if (shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
467 subsystem->cursorPixels, subsystem->format, TRUE, msg->width, msg->height, msg) < 0)
473 msg->common.Free = x11_shadow_message_free;
474 return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
480 static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
485 rdpShadowServer* server = NULL;
486 rdpShadowSurface* surface = NULL;
487 server = subsystem->common.server;
488 surface = server->surface;
493 UINT32* pDstPixel = NULL;
494 XFixesCursorImage* ci = NULL;
495 XLockDisplay(subsystem->display);
496 ci = XFixesGetCursorImage(subsystem->display);
497 XUnlockDisplay(subsystem->display);
505 if (ci->width > subsystem->cursorMaxWidth)
508 if (ci->height > subsystem->cursorMaxHeight)
511 subsystem->cursorHotX = ci->xhot;
512 subsystem->cursorHotY = ci->yhot;
513 subsystem->cursorWidth = ci->width;
514 subsystem->cursorHeight = ci->height;
515 subsystem->cursorId = ci->cursor_serial;
516 n = ci->width * ci->height;
517 pDstPixel = (UINT32*)subsystem->cursorPixels;
519 for (
int k = 0; k < n; k++)
522 *pDstPixel++ = (UINT32)ci->pixels[k];
526 x11_shadow_pointer_alpha_update(subsystem);
538 XLockDisplay(subsystem->display);
540 if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
541 &root_y, &win_x, &win_y, &mask))
543 XUnlockDisplay(subsystem->display);
547 XUnlockDisplay(subsystem->display);
559 if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
561 subsystem->common.pointerX = x;
562 subsystem->common.pointerY = y;
563 x11_shadow_pointer_position_update(subsystem);
569 static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
571 if (xevent->type == MotionNotify)
576 else if (xevent->type == subsystem->xfixes_cursor_notify_event)
578 x11_shadow_query_cursor(subsystem, TRUE);
589 static void x11_shadow_validate_region(x11ShadowSubsystem* subsystem,
int x,
int y,
int width,
594 if (!subsystem->use_xfixes || !subsystem->use_xdamage)
599 region.width = width;
600 region.height = height;
601 #if defined(WITH_XFIXES) && defined(WITH_XDAMAGE)
602 XLockDisplay(subsystem->display);
603 XFixesSetRegion(subsystem->display, subsystem->xdamage_region, ®ion, 1);
604 XDamageSubtract(subsystem->display, subsystem->xdamage, subsystem->xdamage_region, None);
605 XUnlockDisplay(subsystem->display);
609 static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
619 BYTE* pSrcData = NULL;
620 BYTE* pDstData = NULL;
625 rdpShadowSurface* surface = NULL;
630 surface = subsystem->common.server->surface;
633 nWidth = subsystem->cursorWidth;
634 nHeight = subsystem->cursorHeight;
635 nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
636 nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
638 if (nXDst >= surface->width)
648 nXSrc = (UINT32)nXDst;
653 if (nYDst >= surface->height)
660 if (nYDst >= nHeight)
663 nYSrc = (UINT32)nYDst;
668 if ((nXDst + nWidth) > surface->width)
669 nWidth = (nXDst > surface->width) ? 0 : (UINT32)(surface->width - nXDst);
671 if ((nYDst + nHeight) > surface->height)
672 nHeight = (nYDst > surface->height) ? 0 : (UINT32)(surface->height - nYDst);
674 pSrcData = subsystem->cursorPixels;
675 nSrcStep = subsystem->cursorWidth * 4;
676 pDstData = surface->data;
677 nDstStep = surface->scanline;
679 for (
size_t y = 0; y < nHeight; y++)
681 const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
682 BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (4ULL * nXDst)];
684 for (
size_t x = 0; x < nWidth; x++)
699 pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
700 pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
701 pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
712 static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
714 XWindowAttributes attr;
715 XLockDisplay(subsystem->display);
716 XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
717 XUnlockDisplay(subsystem->display);
719 if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
721 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
724 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
725 if (!shadow_screen_resize(subsystem->common.server->screen))
728 WINPR_ASSERT(attr.width > 0);
729 WINPR_ASSERT(attr.height > 0);
731 subsystem->width = (UINT32)attr.width;
732 subsystem->height = (UINT32)attr.height;
734 virtualScreen->left = 0;
735 virtualScreen->top = 0;
736 virtualScreen->right = attr.width - 1;
737 virtualScreen->bottom = attr.height - 1;
738 virtualScreen->flags = 1;
745 static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
748 XGetErrorText(display, event->error_code, (
char*)&msg,
sizeof(msg));
749 WLog_ERR(TAG,
"X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
750 event->error_code, event->request_code, event->minor_code);
753 if (event->error_code != BadMatch)
761 static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
770 XImage* image = NULL;
771 rdpShadowServer* server = NULL;
772 rdpShadowSurface* surface = NULL;
776 server = subsystem->common.server;
777 surface = server->surface;
778 count = ArrayList_Count(server->clients);
783 EnterCriticalSection(&surface->lock);
784 surfaceRect.left = 0;
786 surfaceRect.right = surface->width;
787 surfaceRect.bottom = surface->height;
788 LeaveCriticalSection(&surface->lock);
790 XLockDisplay(subsystem->display);
795 XSetErrorHandler(x11_shadow_error_handler_for_capture);
796 #if defined(WITH_XDAMAGE)
797 if (subsystem->use_xshm)
799 image = subsystem->fb_image;
800 XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
801 subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
803 EnterCriticalSection(&surface->lock);
804 status = shadow_capture_compare_with_format(
805 surface->data, surface->format, surface->scanline, surface->width, surface->height,
806 (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format, image->bytes_per_line,
808 LeaveCriticalSection(&surface->lock);
813 EnterCriticalSection(&surface->lock);
814 image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
815 surface->width, surface->height, AllPlanes, ZPixmap);
819 status = shadow_capture_compare_with_format(
820 surface->data, surface->format, surface->scanline, surface->width, surface->height,
821 (BYTE*)image->data, subsystem->format, image->bytes_per_line, &invalidRect);
823 LeaveCriticalSection(&surface->lock);
835 XSetErrorHandler(NULL);
836 XSync(subsystem->display, False);
837 XUnlockDisplay(subsystem->display);
842 EnterCriticalSection(&surface->lock);
843 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
844 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
845 empty = region16_is_empty(&(surface->invalidRegion));
846 LeaveCriticalSection(&surface->lock);
851 EnterCriticalSection(&surface->lock);
852 extents = region16_extents(&(surface->invalidRegion));
855 width = extents->right - extents->left;
856 height = extents->bottom - extents->top;
858 WINPR_ASSERT(image->bytes_per_line >= 0);
859 WINPR_ASSERT(width >= 0);
860 WINPR_ASSERT(height >= 0);
861 success = freerdp_image_copy_no_overlap(
862 surface->data, surface->format, surface->scanline, x, y, (UINT32)width,
863 (UINT32)height, (BYTE*)image->data, subsystem->format,
864 (UINT32)image->bytes_per_line, x, y, NULL, FREERDP_FLIP_NONE);
865 LeaveCriticalSection(&surface->lock);
870 count = ArrayList_Count(server->clients);
871 shadow_subsystem_frame_update(&subsystem->common);
875 rdpShadowClient* client = NULL;
876 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
879 subsystem->common.captureFrameRate =
880 shadow_encoder_preferred_fps(client->encoder);
883 EnterCriticalSection(&surface->lock);
884 region16_clear(&(surface->invalidRegion));
885 LeaveCriticalSection(&surface->lock);
891 if (!subsystem->use_xshm && image)
892 XDestroyImage(image);
896 XSetErrorHandler(NULL);
897 XSync(subsystem->display, False);
898 XUnlockDisplay(subsystem->display);
904 static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
908 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
909 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
913 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"", message->id);
918 message->Free(message);
923 static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
925 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
929 DWORD dwInterval = 0;
930 UINT64 frameTime = 0;
934 MsgPipe = subsystem->common.MsgPipe;
936 events[nCount++] = subsystem->common.event;
937 events[nCount++] = MessageQueue_Event(MsgPipe->In);
938 subsystem->common.captureFrameRate = 16;
939 dwInterval = 1000 / subsystem->common.captureFrameRate;
940 frameTime = GetTickCount64() + dwInterval;
944 const UINT64 cTime = GetTickCount64();
945 const DWORD dwTimeout =
946 (DWORD)((cTime > frameTime) ? 0 : MIN(UINT32_MAX, frameTime - cTime));
947 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
949 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
951 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
953 if (message.id == WMQ_QUIT)
956 x11_shadow_subsystem_process_message(subsystem, &message);
960 if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
962 XLockDisplay(subsystem->display);
964 if (XEventsQueued(subsystem->display, QueuedAlready))
966 XNextEvent(subsystem->display, &xevent);
967 x11_shadow_handle_xevent(subsystem, &xevent);
970 XUnlockDisplay(subsystem->display);
973 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
975 x11_shadow_check_resize(subsystem);
976 x11_shadow_screen_grab(subsystem);
977 x11_shadow_query_cursor(subsystem, FALSE);
978 dwInterval = 1000 / subsystem->common.captureFrameRate;
979 frameTime += dwInterval;
987 static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
989 if (subsystem->display)
992 if (!getenv(
"DISPLAY"))
993 setenv(
"DISPLAY",
":0", 1);
998 subsystem->display = XOpenDisplay(NULL);
1000 if (!subsystem->display)
1002 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
1006 subsystem->xfds = ConnectionNumber(subsystem->display);
1007 subsystem->number = DefaultScreen(subsystem->display);
1008 subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
1009 subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
1010 subsystem->width = WidthOfScreen(subsystem->screen);
1011 subsystem->height = HeightOfScreen(subsystem->screen);
1012 subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
1016 static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
1019 int xfixes_event = 0;
1020 int xfixes_error = 0;
1024 if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
1027 if (!XFixesQueryVersion(subsystem->display, &major, &minor))
1030 subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
1031 XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
1032 XFixesDisplayCursorNotifyMask);
1039 static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1041 #ifdef WITH_XINERAMA
1042 int xinerama_event = 0;
1043 int xinerama_error = 0;
1045 const int rc = x11_shadow_subsystem_base_init(subsystem);
1049 if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1052 #if defined(WITH_XDAMAGE)
1055 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1059 if (!XineramaIsActive(subsystem->display))
1068 static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1073 int damage_event = 0;
1074 int damage_error = 0;
1076 if (!subsystem->use_xfixes)
1079 if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1082 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1088 subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1089 subsystem->xdamage =
1090 XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1092 if (!subsystem->xdamage)
1096 subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
1098 if (!subsystem->xdamage_region)
1108 static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1115 if (!XShmQueryExtension(subsystem->display))
1118 if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1124 subsystem->fb_shm_info.shmid = -1;
1125 subsystem->fb_shm_info.shmaddr = (
char*)-1;
1126 subsystem->fb_shm_info.readOnly = False;
1127 subsystem->fb_image =
1128 XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
1129 &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1131 if (!subsystem->fb_image)
1133 WLog_ERR(TAG,
"XShmCreateImage failed");
1137 subsystem->fb_shm_info.shmid = shmget(
1138 IPC_PRIVATE, 1ull * subsystem->fb_image->bytes_per_line * subsystem->fb_image->height,
1141 if (subsystem->fb_shm_info.shmid == -1)
1143 WLog_ERR(TAG,
"shmget failed");
1147 subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
1148 subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1150 if (subsystem->fb_shm_info.shmaddr == ((
char*)-1))
1152 WLog_ERR(TAG,
"shmat failed");
1156 if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1159 XSync(subsystem->display, False);
1160 shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
1161 subsystem->fb_pixmap =
1162 XShmCreatePixmap(subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1163 &(subsystem->fb_shm_info), subsystem->fb_image->width,
1164 subsystem->fb_image->height, subsystem->fb_image->depth);
1165 XSync(subsystem->display, False);
1167 if (!subsystem->fb_pixmap)
1170 values.subwindow_mode = IncludeInferiors;
1171 values.graphics_exposures = False;
1172 #if defined(WITH_XDAMAGE)
1173 subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1174 GCSubwindowMode | GCGraphicsExposures, &values);
1175 XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1177 XSync(subsystem->display, False);
1181 UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
1183 Display* display = NULL;
1184 int displayWidth = 0;
1185 int displayHeight = 0;
1186 int numMonitors = 0;
1188 if (!getenv(
"DISPLAY"))
1189 setenv(
"DISPLAY",
":0", 1);
1191 display = XOpenDisplay(NULL);
1195 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
1199 displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1200 displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1201 #ifdef WITH_XINERAMA
1203 #if defined(WITH_XDAMAGE)
1207 int xinerama_event = 0;
1208 int xinerama_error = 0;
1209 XineramaScreenInfo* screens = NULL;
1211 const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
1213 #if defined(WITH_XDAMAGE)
1214 XDamageQueryVersion(display, &major, &minor);
1219 if (xinerama && damage && XineramaIsActive(display))
1221 screens = XineramaQueryScreens(display, &numMonitors);
1223 if (numMonitors > (INT64)maxMonitors)
1224 numMonitors = (int)maxMonitors;
1226 if (screens && (numMonitors > 0))
1228 for (
int index = 0; index < numMonitors; index++)
1231 const XineramaScreenInfo* screen = &screens[index];
1233 monitor->left = screen->x_org;
1234 monitor->top = screen->y_org;
1235 monitor->right = monitor->left + screen->width - 1;
1236 monitor->bottom = monitor->top + screen->height - 1;
1237 monitor->flags = (index == 0) ? 1 : 0;
1245 XCloseDisplay(display);
1247 if (numMonitors < 1)
1254 monitor->right = displayWidth - 1;
1255 monitor->bottom = displayHeight - 1;
1263 static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1267 int nextensions = 0;
1268 char** extensions = NULL;
1269 XVisualInfo* vi = NULL;
1270 XVisualInfo* vis = NULL;
1271 XVisualInfo
template = { 0 };
1272 XPixmapFormatValues* pf = NULL;
1273 XPixmapFormatValues* pfs = NULL;
1275 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1280 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1281 const int rc = x11_shadow_subsystem_base_init(subsystem);
1285 subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRA32
1286 : PIXEL_FORMAT_ARGB32;
1288 if ((subsystem->depth != 24) && (subsystem->depth != 32))
1290 WLog_ERR(TAG,
"unsupported X11 server color depth: %" PRIu32, subsystem->depth);
1294 extensions = XListExtensions(subsystem->display, &nextensions);
1296 if (!extensions || (nextensions < 0))
1299 for (
int i = 0; i < nextensions; i++)
1301 if (strcmp(extensions[i],
"Composite") == 0)
1302 subsystem->composite = TRUE;
1305 XFreeExtensionList(extensions);
1307 if (subsystem->composite)
1308 subsystem->use_xdamage = FALSE;
1310 pfs = XListPixmapFormats(subsystem->display, &pf_count);
1314 WLog_ERR(TAG,
"XListPixmapFormats failed");
1318 for (
int i = 0; i < pf_count; i++)
1322 if (pf->depth == (INT64)subsystem->depth)
1324 subsystem->bpp = pf->bits_per_pixel;
1325 subsystem->scanline_pad = pf->scanline_pad;
1331 template.class = TrueColor;
1332 template.screen = subsystem->number;
1333 vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &
template,
1338 WLog_ERR(TAG,
"XGetVisualInfo failed");
1342 for (
int i = 0; i < vi_count; i++)
1346 if (vi->depth == (INT64)subsystem->depth)
1348 subsystem->visual = vi->visual;
1354 XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1355 subsystem->cursorMaxWidth = 256;
1356 subsystem->cursorMaxHeight = 256;
1357 subsystem->cursorPixels =
1358 winpr_aligned_malloc(4ULL * subsystem->cursorMaxWidth * subsystem->cursorMaxHeight, 16);
1360 if (!subsystem->cursorPixels)
1363 x11_shadow_query_cursor(subsystem, TRUE);
1365 if (subsystem->use_xfixes)
1367 if (x11_shadow_xfixes_init(subsystem) < 0)
1368 subsystem->use_xfixes = FALSE;
1371 if (subsystem->use_xinerama)
1373 if (x11_shadow_xinerama_init(subsystem) < 0)
1374 subsystem->use_xinerama = FALSE;
1377 if (subsystem->use_xshm)
1379 if (x11_shadow_xshm_init(subsystem) < 0)
1380 subsystem->use_xshm = FALSE;
1383 if (subsystem->use_xdamage)
1385 if (x11_shadow_xdamage_init(subsystem) < 0)
1386 subsystem->use_xdamage = FALSE;
1389 if (!(subsystem->common.event =
1390 CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1394 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
1395 virtualScreen->left = 0;
1396 virtualScreen->top = 0;
1397 WINPR_ASSERT(subsystem->width <= INT32_MAX);
1398 WINPR_ASSERT(subsystem->height <= INT32_MAX);
1399 virtualScreen->right = (INT32)subsystem->width - 1;
1400 virtualScreen->bottom = (INT32)subsystem->height - 1;
1401 virtualScreen->flags = 1;
1403 "X11 Extensions: XFixes: %" PRId32
" Xinerama: %" PRId32
" XDamage: %" PRId32
1404 " XShm: %" PRId32
"",
1405 subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1406 subsystem->use_xshm);
1412 static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1414 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1419 if (subsystem->display)
1421 XCloseDisplay(subsystem->display);
1422 subsystem->display = NULL;
1425 if (subsystem->common.event)
1427 (void)CloseHandle(subsystem->common.event);
1428 subsystem->common.event = NULL;
1431 if (subsystem->cursorPixels)
1433 winpr_aligned_free(subsystem->cursorPixels);
1434 subsystem->cursorPixels = NULL;
1440 static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1442 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1447 if (!(subsystem->thread =
1448 CreateThread(NULL, 0, x11_shadow_subsystem_thread, (
void*)subsystem, 0, NULL)))
1450 WLog_ERR(TAG,
"Failed to create thread");
1457 static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1459 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1464 if (subsystem->thread)
1466 if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1467 (
void)WaitForSingleObject(subsystem->thread, INFINITE);
1469 (void)CloseHandle(subsystem->thread);
1470 subsystem->thread = NULL;
1476 static rdpShadowSubsystem* x11_shadow_subsystem_new(
void)
1478 x11ShadowSubsystem* subsystem = NULL;
1479 subsystem = (x11ShadowSubsystem*)calloc(1,
sizeof(x11ShadowSubsystem));
1485 subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1487 subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1488 subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1489 subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1490 subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1491 subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1492 subsystem->composite = FALSE;
1493 subsystem->use_xshm = FALSE;
1494 subsystem->use_xfixes = TRUE;
1495 subsystem->use_xdamage = FALSE;
1496 subsystem->use_xinerama = TRUE;
1497 return (rdpShadowSubsystem*)subsystem;
1500 static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1505 x11_shadow_subsystem_uninit(subsystem);
1509 FREERDP_ENTRY_POINT(FREERDP_API
const char* ShadowSubsystemName(
void))
1514 FREERDP_ENTRY_POINT(FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
1519 pEntryPoints->New = x11_shadow_subsystem_new;
1520 pEntryPoints->Free = x11_shadow_subsystem_free;
1521 pEntryPoints->Init = x11_shadow_subsystem_init;
1522 pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1523 pEntryPoints->Start = x11_shadow_subsystem_start;
1524 pEntryPoints->Stop = x11_shadow_subsystem_stop;
1525 pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;