29#include <sys/select.h>
30#include <sys/signal.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")
53static UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors);
57#include <security/pam_appl.h>
64} SHADOW_PAM_AUTH_DATA;
71 SHADOW_PAM_AUTH_DATA appdata;
72} SHADOW_PAM_AUTH_INFO;
74static int x11_shadow_pam_conv(
int num_msg,
const struct pam_message** msg,
75 struct pam_response** resp,
void* appdata_ptr)
77 int pam_status = PAM_CONV_ERR;
78 SHADOW_PAM_AUTH_DATA* appdata = NULL;
79 struct pam_response* response = NULL;
80 WINPR_ASSERT(num_msg >= 0);
81 appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
82 WINPR_ASSERT(appdata);
84 if (!(response = (
struct pam_response*)calloc((
size_t)num_msg,
sizeof(
struct pam_response))))
87 for (
int index = 0; index < num_msg; index++)
89 switch (msg[index]->msg_style)
91 case PAM_PROMPT_ECHO_ON:
92 response[index].resp = _strdup(appdata->user);
94 if (!response[index].resp)
97 response[index].resp_retcode = PAM_SUCCESS;
100 case PAM_PROMPT_ECHO_OFF:
101 response[index].resp = _strdup(appdata->password);
103 if (!response[index].resp)
106 response[index].resp_retcode = PAM_SUCCESS;
110 pam_status = PAM_CONV_ERR;
119 for (
int index = 0; index < num_msg; ++index)
121 if (response[index].resp)
123 memset(response[index].resp, 0, strlen(response[index].resp));
124 free(response[index].resp);
128 memset(response, 0,
sizeof(
struct pam_response) * (
size_t)num_msg);
134static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
136 const char* base =
"/etc/pam.d";
137 const char* hints[] = {
"lightdm",
"gdm",
"xdm",
"login",
"sshd" };
139 for (
size_t x = 0; x < ARRAYSIZE(hints); x++)
141 char path[MAX_PATH] = { 0 };
142 const char* hint = hints[x];
144 (void)_snprintf(path,
sizeof(path),
"%s/%s", base, hint);
145 if (winpr_PathFileExists(path))
148 info->service_name = _strdup(hint);
149 return info->service_name != NULL;
152 WLog_WARN(TAG,
"Could not determine PAM service name");
156static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
157 const char* user,
const char* domain,
const char* password)
160 SHADOW_PAM_AUTH_INFO info = { 0 };
161 WINPR_UNUSED(subsystem);
162 WINPR_UNUSED(client);
164 if (!x11_shadow_pam_get_service_name(&info))
167 info.appdata.user = user;
168 info.appdata.domain = domain;
169 info.appdata.password = password;
170 info.pamc.conv = &x11_shadow_pam_conv;
171 info.pamc.appdata_ptr = &info.appdata;
172 pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
174 if (pam_status != PAM_SUCCESS)
176 WLog_ERR(TAG,
"pam_start failure: %s", pam_strerror(info.handle, pam_status));
180 pam_status = pam_authenticate(info.handle, 0);
182 if (pam_status != PAM_SUCCESS)
184 WLog_ERR(TAG,
"pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
188 pam_status = pam_acct_mgmt(info.handle, 0);
190 if (pam_status != PAM_SUCCESS)
192 WLog_ERR(TAG,
"pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
201static BOOL x11_shadow_input_synchronize_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
202 WINPR_ATTR_UNUSED rdpShadowClient* client,
203 WINPR_ATTR_UNUSED UINT32 flags)
206 WLog_WARN(TAG,
"not implemented");
210static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
211 UINT16 flags, UINT8 code)
214 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
218 BOOL extended = FALSE;
220 if (!client || !subsystem)
223 if (flags & KBD_FLAGS_EXTENDED)
230 vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
235 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
239 XLockDisplay(x11->display);
240 XTestGrabControl(x11->display, True);
242 if (flags & KBD_FLAGS_RELEASE)
243 XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
245 XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
247 XTestGrabControl(x11->display, False);
248 XFlush(x11->display);
249 XUnlockDisplay(x11->display);
256static BOOL x11_shadow_input_unicode_keyboard_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
257 WINPR_ATTR_UNUSED rdpShadowClient* client,
258 WINPR_ATTR_UNUSED UINT16 flags,
259 WINPR_ATTR_UNUSED UINT16 code)
262 WLog_WARN(TAG,
"not implemented");
266static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
267 UINT16 flags, UINT16 x, UINT16 y)
270 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
271 unsigned int button = 0;
273 rdpShadowServer* server = NULL;
274 rdpShadowSurface* surface = NULL;
276 if (!subsystem || !client)
279 server = subsystem->server;
284 surface = server->surface;
289 x11->lastMouseClient = client;
292 XLockDisplay(x11->display);
293 XTestGrabControl(x11->display, True);
295 if (flags & PTR_FLAGS_WHEEL)
297 BOOL negative = FALSE;
299 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
302 button = (negative) ? 5 : 4;
303 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
304 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
306 else if (flags & PTR_FLAGS_HWHEEL)
308 BOOL negative = FALSE;
310 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
313 button = (negative) ? 7 : 6;
314 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
315 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
319 if (flags & PTR_FLAGS_MOVE)
320 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
322 if (flags & PTR_FLAGS_BUTTON1)
324 else if (flags & PTR_FLAGS_BUTTON2)
326 else if (flags & PTR_FLAGS_BUTTON3)
329 if (flags & PTR_FLAGS_DOWN)
333 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
336 XTestGrabControl(x11->display, False);
337 XFlush(x11->display);
338 XUnlockDisplay(x11->display);
343static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
344 rdpShadowClient* client, UINT16 flags, UINT16 x,
348 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
351 rdpShadowServer* server = NULL;
352 rdpShadowSurface* surface = NULL;
354 if (!subsystem || !client)
357 server = subsystem->server;
362 surface = server->surface;
367 x11->lastMouseClient = client;
370 XLockDisplay(x11->display);
371 XTestGrabControl(x11->display, True);
372 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
374 if (flags & PTR_XFLAGS_BUTTON1)
376 else if (flags & PTR_XFLAGS_BUTTON2)
379 if (flags & PTR_XFLAGS_DOWN)
383 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
385 XTestGrabControl(x11->display, False);
386 XFlush(x11->display);
387 XUnlockDisplay(x11->display);
392static void x11_shadow_message_free(UINT32
id, SHADOW_MSG_OUT* msg)
396 case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
400 case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
407 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"",
id);
413static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
415 UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
416 rdpShadowServer* server = NULL;
420 if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
423 templateMsg.xPos = subsystem->common.pointerX;
424 templateMsg.yPos = subsystem->common.pointerY;
425 templateMsg.common.Free = x11_shadow_message_free;
426 server = subsystem->common.server;
427 ArrayList_Lock(server->clients);
429 for (
size_t index = 0; index < ArrayList_Count(server->clients); index++)
432 rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
435 if (client == subsystem->lastMouseClient)
438 msg = malloc(
sizeof(templateMsg));
446 memcpy(msg, &templateMsg,
sizeof(templateMsg));
448 if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
452 ArrayList_Unlock(server->clients);
456static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
459 UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
466 msg->xHot = subsystem->cursorHotX;
467 msg->yHot = subsystem->cursorHotY;
468 msg->width = subsystem->cursorWidth;
469 msg->height = subsystem->cursorHeight;
471 if (shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
472 subsystem->cursorPixels, subsystem->format, TRUE, msg->width, msg->height, msg) < 0)
478 msg->common.Free = x11_shadow_message_free;
479 return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
485static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
490 rdpShadowServer* server = NULL;
491 rdpShadowSurface* surface = NULL;
492 server = subsystem->common.server;
493 surface = server->surface;
498 UINT32* pDstPixel = NULL;
499 XFixesCursorImage* ci = NULL;
500 XLockDisplay(subsystem->display);
501 ci = XFixesGetCursorImage(subsystem->display);
502 XUnlockDisplay(subsystem->display);
510 if (ci->width > subsystem->cursorMaxWidth)
513 if (ci->height > subsystem->cursorMaxHeight)
516 subsystem->cursorHotX = ci->xhot;
517 subsystem->cursorHotY = ci->yhot;
518 subsystem->cursorWidth = ci->width;
519 subsystem->cursorHeight = ci->height;
520 subsystem->cursorId = ci->cursor_serial;
521 n = ci->width * ci->height;
522 pDstPixel = (UINT32*)subsystem->cursorPixels;
524 for (
int k = 0; k < n; k++)
527 *pDstPixel++ = (UINT32)ci->pixels[k];
531 x11_shadow_pointer_alpha_update(subsystem);
543 XLockDisplay(subsystem->display);
545 if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
546 &root_y, &win_x, &win_y, &mask))
548 XUnlockDisplay(subsystem->display);
552 XUnlockDisplay(subsystem->display);
564 if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
566 subsystem->common.pointerX = WINPR_ASSERTING_INT_CAST(UINT32, x);
567 subsystem->common.pointerY = WINPR_ASSERTING_INT_CAST(UINT32, y);
568 x11_shadow_pointer_position_update(subsystem);
574static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
576 if (xevent->type == MotionNotify)
581 else if (xevent->type == subsystem->xfixes_cursor_notify_event)
583 x11_shadow_query_cursor(subsystem, TRUE);
594#if defined(USE_SHADOW_BLEND_CURSOR)
595static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
600 rdpShadowSurface* surface = subsystem->common.server->surface;
603 UINT32 nWidth = subsystem->cursorWidth;
604 UINT32 nHeight = subsystem->cursorHeight;
605 INT64 nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
606 INT64 nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
608 if (nXDst >= surface->width)
618 nXSrc = (UINT32)nXDst;
623 if (nYDst >= surface->height)
630 if (nYDst >= nHeight)
633 nYSrc = (UINT32)nYDst;
638 if ((nXDst + nWidth) > surface->width)
639 nWidth = (nXDst > surface->width) ? 0 : (UINT32)(surface->width - nXDst);
641 if ((nYDst + nHeight) > surface->height)
642 nHeight = (nYDst > surface->height) ? 0 : (UINT32)(surface->height - nYDst);
644 const BYTE* pSrcData = subsystem->cursorPixels;
645 const UINT32 nSrcStep = subsystem->cursorWidth * 4;
646 BYTE* pDstData = surface->data;
647 const UINT32 nDstStep = surface->scanline;
649 for (
size_t y = 0; y < nHeight; y++)
651 const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
652 BYTE* pDstPixel = &pDstData[((WINPR_ASSERTING_INT_CAST(uint32_t, nYDst) + y) * nDstStep) +
653 (4ULL * WINPR_ASSERTING_INT_CAST(uint32_t, nXDst))];
655 for (
size_t x = 0; x < nWidth; x++)
657 const BYTE B = *pSrcPixel++;
658 const BYTE G = *pSrcPixel++;
659 const BYTE R = *pSrcPixel++;
660 const BYTE A = *pSrcPixel++;
670 pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
671 pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
672 pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
684static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
686 XWindowAttributes attr;
687 XLockDisplay(subsystem->display);
688 XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
689 XUnlockDisplay(subsystem->display);
691 if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
693 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
696 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
697 if (!shadow_screen_resize(subsystem->common.server->screen))
700 WINPR_ASSERT(attr.width > 0);
701 WINPR_ASSERT(attr.height > 0);
703 subsystem->width = (UINT32)attr.width;
704 subsystem->height = (UINT32)attr.height;
706 virtualScreen->left = 0;
707 virtualScreen->top = 0;
708 virtualScreen->right = attr.width - 1;
709 virtualScreen->bottom = attr.height - 1;
710 virtualScreen->flags = 1;
717static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
720 XGetErrorText(display, event->error_code, (
char*)&msg,
sizeof(msg));
721 WLog_ERR(TAG,
"X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
722 event->error_code, event->request_code, event->minor_code);
725 if (event->error_code != BadMatch)
733static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
742 XImage* image = NULL;
743 rdpShadowServer* server = NULL;
744 rdpShadowSurface* surface = NULL;
748 server = subsystem->common.server;
749 surface = server->surface;
750 count = ArrayList_Count(server->clients);
755 EnterCriticalSection(&surface->lock);
756 surfaceRect.left = 0;
759 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->width);
760 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->height);
761 LeaveCriticalSection(&surface->lock);
763 XLockDisplay(subsystem->display);
768 XSetErrorHandler(x11_shadow_error_handler_for_capture);
769#if defined(WITH_XDAMAGE)
770 if (subsystem->use_xshm)
772 image = subsystem->fb_image;
773 XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
774 subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
776 EnterCriticalSection(&surface->lock);
777 status = shadow_capture_compare_with_format(
778 surface->data, surface->format, surface->scanline, surface->width, surface->height,
779 (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format,
780 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
781 LeaveCriticalSection(&surface->lock);
786 EnterCriticalSection(&surface->lock);
787 image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
788 surface->width, surface->height, AllPlanes, ZPixmap);
792 status = shadow_capture_compare_with_format(
793 surface->data, surface->format, surface->scanline, surface->width, surface->height,
794 (BYTE*)image->data, subsystem->format,
795 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), &invalidRect);
797 LeaveCriticalSection(&surface->lock);
809 XSetErrorHandler(NULL);
810 XSync(subsystem->display, False);
811 XUnlockDisplay(subsystem->display);
816 EnterCriticalSection(&surface->lock);
817 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
818 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
819 empty = region16_is_empty(&(surface->invalidRegion));
820 LeaveCriticalSection(&surface->lock);
825 EnterCriticalSection(&surface->lock);
826 extents = region16_extents(&(surface->invalidRegion));
829 width = extents->right - extents->left;
830 height = extents->bottom - extents->top;
832 WINPR_ASSERT(image->bytes_per_line >= 0);
833 WINPR_ASSERT(width >= 0);
834 WINPR_ASSERT(height >= 0);
835 success = freerdp_image_copy_no_overlap(
836 surface->data, surface->format, surface->scanline,
837 WINPR_ASSERTING_INT_CAST(uint32_t, x), WINPR_ASSERTING_INT_CAST(uint32_t, y),
838 WINPR_ASSERTING_INT_CAST(uint32_t, width),
839 WINPR_ASSERTING_INT_CAST(uint32_t, height), (BYTE*)image->data, subsystem->format,
840 WINPR_ASSERTING_INT_CAST(uint32_t, image->bytes_per_line),
841 WINPR_ASSERTING_INT_CAST(UINT32, x), WINPR_ASSERTING_INT_CAST(UINT32, y), NULL,
843 LeaveCriticalSection(&surface->lock);
847#if defined(USE_SHADOW_BLEND_CURSOR)
848 if (x11_shadow_blend_cursor(subsystem) < 0)
851 count = ArrayList_Count(server->clients);
852 shadow_subsystem_frame_update(&subsystem->common);
856 rdpShadowClient* client = NULL;
857 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
860 subsystem->common.captureFrameRate =
861 shadow_encoder_preferred_fps(client->encoder);
864 EnterCriticalSection(&surface->lock);
865 region16_clear(&(surface->invalidRegion));
866 LeaveCriticalSection(&surface->lock);
872 if (!subsystem->use_xshm && image)
873 XDestroyImage(image);
877 XSetErrorHandler(NULL);
878 XSync(subsystem->display, False);
879 XUnlockDisplay(subsystem->display);
885static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
889 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
890 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
894 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"", message->id);
899 message->Free(message);
904static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
906 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
910 DWORD dwInterval = 0;
911 UINT64 frameTime = 0;
915 MsgPipe = subsystem->common.MsgPipe;
917 events[nCount++] = subsystem->common.event;
918 events[nCount++] = MessageQueue_Event(MsgPipe->In);
919 subsystem->common.captureFrameRate = 16;
920 dwInterval = 1000 / subsystem->common.captureFrameRate;
921 frameTime = GetTickCount64() + dwInterval;
925 const UINT64 cTime = GetTickCount64();
926 const DWORD dwTimeout =
927 (DWORD)((cTime > frameTime) ? 0 : MIN(UINT32_MAX, frameTime - cTime));
928 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
930 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
932 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
934 if (message.id == WMQ_QUIT)
937 x11_shadow_subsystem_process_message(subsystem, &message);
941 if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
943 XLockDisplay(subsystem->display);
945 if (XEventsQueued(subsystem->display, QueuedAlready))
947 XNextEvent(subsystem->display, &xevent);
948 x11_shadow_handle_xevent(subsystem, &xevent);
951 XUnlockDisplay(subsystem->display);
954 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
956 x11_shadow_check_resize(subsystem);
957 x11_shadow_screen_grab(subsystem);
958 x11_shadow_query_cursor(subsystem, FALSE);
959 dwInterval = 1000 / subsystem->common.captureFrameRate;
960 frameTime += dwInterval;
968static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
970 if (subsystem->display)
974 if (!getenv(
"DISPLAY"))
977 setenv(
"DISPLAY",
":0", 1);
983 subsystem->display = XOpenDisplay(NULL);
985 if (!subsystem->display)
987 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
991 subsystem->xfds = ConnectionNumber(subsystem->display);
992 subsystem->number = DefaultScreen(subsystem->display);
993 subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
994 subsystem->depth = WINPR_ASSERTING_INT_CAST(UINT32, DefaultDepthOfScreen(subsystem->screen));
995 subsystem->width = WINPR_ASSERTING_INT_CAST(UINT32, WidthOfScreen(subsystem->screen));
996 subsystem->height = WINPR_ASSERTING_INT_CAST(UINT32, HeightOfScreen(subsystem->screen));
997 subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
1001static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
1004 int xfixes_event = 0;
1005 int xfixes_error = 0;
1009 if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
1012 if (!XFixesQueryVersion(subsystem->display, &major, &minor))
1015 subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
1016 XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
1017 XFixesDisplayCursorNotifyMask);
1024static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1027 int xinerama_event = 0;
1028 int xinerama_error = 0;
1030 const int rc = x11_shadow_subsystem_base_init(subsystem);
1034 if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1037#if defined(WITH_XDAMAGE)
1040 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1044 if (!XineramaIsActive(subsystem->display))
1053static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1058 int damage_event = 0;
1059 int damage_error = 0;
1061 if (!subsystem->use_xfixes)
1064 if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1067 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1073 subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1074 subsystem->xdamage =
1075 XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1077 if (!subsystem->xdamage)
1081 subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
1083 if (!subsystem->xdamage_region)
1093static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1100 if (!XShmQueryExtension(subsystem->display))
1103 if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1109 subsystem->fb_shm_info.shmid = -1;
1110 subsystem->fb_shm_info.shmaddr = (
char*)-1;
1111 subsystem->fb_shm_info.readOnly = False;
1112 subsystem->fb_image =
1113 XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
1114 &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1116 if (!subsystem->fb_image)
1118 WLog_ERR(TAG,
"XShmCreateImage failed");
1122 subsystem->fb_shm_info.shmid =
1124 1ull * WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->bytes_per_line) *
1125 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1128 if (subsystem->fb_shm_info.shmid == -1)
1130 WLog_ERR(TAG,
"shmget failed");
1134 subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
1135 subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1137 if (subsystem->fb_shm_info.shmaddr == ((
char*)-1))
1139 WLog_ERR(TAG,
"shmat failed");
1143 if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1146 XSync(subsystem->display, False);
1147 shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
1148 subsystem->fb_pixmap = XShmCreatePixmap(
1149 subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1150 &(subsystem->fb_shm_info), WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->width),
1151 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1152 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->depth));
1153 XSync(subsystem->display, False);
1155 if (!subsystem->fb_pixmap)
1158 values.subwindow_mode = IncludeInferiors;
1159 values.graphics_exposures = False;
1160#if defined(WITH_XDAMAGE)
1161 subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1162 GCSubwindowMode | GCGraphicsExposures, &values);
1163 XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1165 XSync(subsystem->display, False);
1169UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
1171 Display* display = NULL;
1172 int displayWidth = 0;
1173 int displayHeight = 0;
1174 int numMonitors = 0;
1177 if (!getenv(
"DISPLAY"))
1180 setenv(
"DISPLAY",
":0", 1);
1183 display = XOpenDisplay(NULL);
1187 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(NULL));
1191 displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1192 displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1195#if defined(WITH_XDAMAGE)
1199 int xinerama_event = 0;
1200 int xinerama_error = 0;
1201 XineramaScreenInfo* screens = NULL;
1203 const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
1205#if defined(WITH_XDAMAGE)
1206 XDamageQueryVersion(display, &major, &minor);
1211 if (xinerama && damage && XineramaIsActive(display))
1213 screens = XineramaQueryScreens(display, &numMonitors);
1215 if (numMonitors > (INT64)maxMonitors)
1216 numMonitors = (int)maxMonitors;
1218 if (screens && (numMonitors > 0))
1220 for (
int index = 0; index < numMonitors; index++)
1223 const XineramaScreenInfo* screen = &screens[index];
1225 monitor->left = screen->x_org;
1226 monitor->top = screen->y_org;
1227 monitor->right = monitor->left + screen->width - 1;
1228 monitor->bottom = monitor->top + screen->height - 1;
1229 monitor->flags = (index == 0) ? 1 : 0;
1237 XCloseDisplay(display);
1239 if (numMonitors < 1)
1246 monitor->right = displayWidth - 1;
1247 monitor->bottom = displayHeight - 1;
1252 return WINPR_ASSERTING_INT_CAST(uint32_t, numMonitors);
1255static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1259 int nextensions = 0;
1260 char** extensions = NULL;
1261 XVisualInfo* vi = NULL;
1262 XVisualInfo* vis = NULL;
1263 XVisualInfo
template = { 0 };
1264 XPixmapFormatValues* pf = NULL;
1265 XPixmapFormatValues* pfs = NULL;
1267 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1272 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1273 const int rc = x11_shadow_subsystem_base_init(subsystem);
1277 subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRA32
1278 : PIXEL_FORMAT_ARGB32;
1280 if ((subsystem->depth != 24) && (subsystem->depth != 32))
1282 WLog_ERR(TAG,
"unsupported X11 server color depth: %" PRIu32, subsystem->depth);
1286 extensions = XListExtensions(subsystem->display, &nextensions);
1288 if (!extensions || (nextensions < 0))
1291 for (
int i = 0; i < nextensions; i++)
1293 if (strcmp(extensions[i],
"Composite") == 0)
1294 subsystem->composite = TRUE;
1297 XFreeExtensionList(extensions);
1299 if (subsystem->composite)
1300 subsystem->use_xdamage = FALSE;
1302 pfs = XListPixmapFormats(subsystem->display, &pf_count);
1306 WLog_ERR(TAG,
"XListPixmapFormats failed");
1310 for (
int i = 0; i < pf_count; i++)
1314 if (pf->depth == (INT64)subsystem->depth)
1316 subsystem->bpp = WINPR_ASSERTING_INT_CAST(uint32_t, pf->bits_per_pixel);
1317 subsystem->scanline_pad = WINPR_ASSERTING_INT_CAST(uint32_t, pf->scanline_pad);
1323 template.class = TrueColor;
1324 template.screen = subsystem->number;
1325 vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &
template,
1330 WLog_ERR(TAG,
"XGetVisualInfo failed");
1334 for (
int i = 0; i < vi_count; i++)
1338 if (vi->depth == (INT64)subsystem->depth)
1340 subsystem->visual = vi->visual;
1346 XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1347 subsystem->cursorMaxWidth = 256;
1348 subsystem->cursorMaxHeight = 256;
1349 subsystem->cursorPixels =
1350 winpr_aligned_malloc(4ULL * subsystem->cursorMaxWidth * subsystem->cursorMaxHeight, 16);
1352 if (!subsystem->cursorPixels)
1355 x11_shadow_query_cursor(subsystem, TRUE);
1357 if (subsystem->use_xfixes)
1359 if (x11_shadow_xfixes_init(subsystem) < 0)
1360 subsystem->use_xfixes = FALSE;
1363 if (subsystem->use_xinerama)
1365 if (x11_shadow_xinerama_init(subsystem) < 0)
1366 subsystem->use_xinerama = FALSE;
1369 if (subsystem->use_xshm)
1371 if (x11_shadow_xshm_init(subsystem) < 0)
1372 subsystem->use_xshm = FALSE;
1375 if (subsystem->use_xdamage)
1377 if (x11_shadow_xdamage_init(subsystem) < 0)
1378 subsystem->use_xdamage = FALSE;
1381 if (!(subsystem->common.event =
1382 CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1386 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
1387 virtualScreen->left = 0;
1388 virtualScreen->top = 0;
1389 WINPR_ASSERT(subsystem->width <= INT32_MAX);
1390 WINPR_ASSERT(subsystem->height <= INT32_MAX);
1391 virtualScreen->right = (INT32)subsystem->width - 1;
1392 virtualScreen->bottom = (INT32)subsystem->height - 1;
1393 virtualScreen->flags = 1;
1395 "X11 Extensions: XFixes: %" PRId32
" Xinerama: %" PRId32
" XDamage: %" PRId32
1396 " XShm: %" PRId32
"",
1397 subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1398 subsystem->use_xshm);
1404static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1406 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1411 if (subsystem->display)
1413 XCloseDisplay(subsystem->display);
1414 subsystem->display = NULL;
1417 if (subsystem->common.event)
1419 (void)CloseHandle(subsystem->common.event);
1420 subsystem->common.event = NULL;
1423 if (subsystem->cursorPixels)
1425 winpr_aligned_free(subsystem->cursorPixels);
1426 subsystem->cursorPixels = NULL;
1432static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1434 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1439 if (!(subsystem->thread =
1440 CreateThread(NULL, 0, x11_shadow_subsystem_thread, (
void*)subsystem, 0, NULL)))
1442 WLog_ERR(TAG,
"Failed to create thread");
1449static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1451 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1456 if (subsystem->thread)
1458 if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1459 (
void)WaitForSingleObject(subsystem->thread, INFINITE);
1461 (void)CloseHandle(subsystem->thread);
1462 subsystem->thread = NULL;
1468static rdpShadowSubsystem* x11_shadow_subsystem_new(
void)
1470 x11ShadowSubsystem* subsystem = NULL;
1471 subsystem = (x11ShadowSubsystem*)calloc(1,
sizeof(x11ShadowSubsystem));
1477 subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1479 subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1480 subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1481 subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1482 subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1483 subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1484 subsystem->composite = FALSE;
1485 subsystem->use_xshm = FALSE;
1486 subsystem->use_xfixes = TRUE;
1487 subsystem->use_xdamage = FALSE;
1488 subsystem->use_xinerama = TRUE;
1489 return (rdpShadowSubsystem*)subsystem;
1492static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1497 x11_shadow_subsystem_uninit(subsystem);
1501FREERDP_ENTRY_POINT(FREERDP_API
const char* ShadowSubsystemName(
void))
1506FREERDP_ENTRY_POINT(FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
1511 pEntryPoints->New = x11_shadow_subsystem_new;
1512 pEntryPoints->Free = x11_shadow_subsystem_free;
1513 pEntryPoints->Init = x11_shadow_subsystem_init;
1514 pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1515 pEntryPoints->Start = x11_shadow_subsystem_start;
1516 pEntryPoints->Stop = x11_shadow_subsystem_stop;
1517 pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;