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;
75static int x11_shadow_pam_conv(
int num_msg,
const struct pam_message** msg,
76 struct pam_response** resp,
void* appdata_ptr)
78 int pam_status = PAM_CONV_ERR;
79 SHADOW_PAM_AUTH_DATA* appdata =
nullptr;
80 struct pam_response* response =
nullptr;
81 WINPR_ASSERT(num_msg >= 0);
82 appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
83 WINPR_ASSERT(appdata);
85 if (!(response = (
struct pam_response*)calloc((
size_t)num_msg,
sizeof(
struct pam_response))))
88 for (
int index = 0; index < num_msg; index++)
90 switch (msg[index]->msg_style)
92 case PAM_PROMPT_ECHO_ON:
93 response[index].resp = _strdup(appdata->user);
95 if (!response[index].resp)
98 response[index].resp_retcode = PAM_SUCCESS;
101 case PAM_PROMPT_ECHO_OFF:
102 response[index].resp = _strdup(appdata->password);
104 if (!response[index].resp)
107 response[index].resp_retcode = PAM_SUCCESS;
111 pam_status = PAM_CONV_ERR;
120 for (
int index = 0; index < num_msg; ++index)
122 if (response[index].resp)
124 memset(response[index].resp, 0, strlen(response[index].resp));
125 free(response[index].resp);
129 memset(response, 0,
sizeof(
struct pam_response) * (
size_t)num_msg);
136static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
138 const char* base =
"/etc/pam.d";
139 const char* hints[] = {
"lightdm",
"gdm",
"xdm",
"login",
"sshd" };
141 for (
size_t x = 0; x < ARRAYSIZE(hints); x++)
143 char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
144 const char* hint = hints[x];
146 (void)_snprintf(path,
sizeof(path),
"%s/%s", base, hint);
147 if (winpr_PathFileExists(path))
150 info->service_name = _strdup(hint);
151 return info->service_name !=
nullptr;
154 WLog_WARN(TAG,
"Could not determine PAM service name");
159static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
160 const char* user,
const char* domain,
const char* password)
163 SHADOW_PAM_AUTH_INFO info = WINPR_C_ARRAY_INIT;
164 WINPR_UNUSED(subsystem);
165 WINPR_UNUSED(client);
167 if (!x11_shadow_pam_get_service_name(&info))
170 info.appdata.user = user;
171 info.appdata.domain = domain;
172 info.appdata.password = password;
173 info.pamc.conv = &x11_shadow_pam_conv;
174 info.pamc.appdata_ptr = &info.appdata;
175 pam_status = pam_start(info.service_name,
nullptr, &info.pamc, &info.handle);
177 if (pam_status != PAM_SUCCESS)
179 WLog_ERR(TAG,
"pam_start failure: %s", pam_strerror(info.handle, pam_status));
183 pam_status = pam_authenticate(info.handle, 0);
185 if (pam_status != PAM_SUCCESS)
187 WLog_ERR(TAG,
"pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
191 pam_status = pam_acct_mgmt(info.handle, 0);
193 if (pam_status != PAM_SUCCESS)
195 WLog_ERR(TAG,
"pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
205static BOOL x11_shadow_input_synchronize_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
206 WINPR_ATTR_UNUSED rdpShadowClient* client,
207 WINPR_ATTR_UNUSED UINT32 flags)
210 WLog_WARN(TAG,
"not implemented");
215static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
216 UINT16 flags, UINT8 code)
219 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
223 BOOL extended = FALSE;
225 if (!client || !subsystem)
228 if (flags & KBD_FLAGS_EXTENDED)
235 vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
240 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
244 XLockDisplay(x11->display);
245 XTestGrabControl(x11->display, True);
247 if (flags & KBD_FLAGS_RELEASE)
248 XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
250 XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
252 XTestGrabControl(x11->display, False);
253 XFlush(x11->display);
254 XUnlockDisplay(x11->display);
257 WLog_WARN(TAG,
"KeyboardEvent not supported by backend, ignoring");
263static BOOL x11_shadow_input_unicode_keyboard_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
264 WINPR_ATTR_UNUSED rdpShadowClient* client,
265 WINPR_ATTR_UNUSED UINT16 flags,
266 WINPR_ATTR_UNUSED UINT16 code)
269 WLog_WARN(TAG,
"not implemented");
274static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
275 UINT16 flags, UINT16 x, UINT16 y)
278 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
279 unsigned int button = 0;
281 rdpShadowServer* server =
nullptr;
282 rdpShadowSurface* surface =
nullptr;
284 if (!subsystem || !client)
287 server = subsystem->server;
292 surface = server->surface;
297 x11->lastMouseClient = client;
300 XLockDisplay(x11->display);
301 XTestGrabControl(x11->display, True);
303 if (flags & PTR_FLAGS_WHEEL)
305 BOOL negative = FALSE;
307 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
310 button = (negative) ? 5 : 4;
311 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
312 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
314 else if (flags & PTR_FLAGS_HWHEEL)
316 BOOL negative = FALSE;
318 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
321 button = (negative) ? 7 : 6;
322 XTestFakeButtonEvent(x11->display, button, True, (
unsigned long)CurrentTime);
323 XTestFakeButtonEvent(x11->display, button, False, (
unsigned long)CurrentTime);
327 if (flags & PTR_FLAGS_MOVE)
328 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
330 if (flags & PTR_FLAGS_BUTTON1)
332 else if (flags & PTR_FLAGS_BUTTON2)
334 else if (flags & PTR_FLAGS_BUTTON3)
337 if (flags & PTR_FLAGS_DOWN)
341 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
344 XTestGrabControl(x11->display, False);
345 XFlush(x11->display);
346 XUnlockDisplay(x11->display);
348 WLog_WARN(TAG,
"MouseEvent not supported by backend, ignoring");
354static BOOL x11_shadow_input_rel_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
355 UINT16 flags, INT16 xDelta, INT16 yDelta)
358 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
361 unsigned int button = 0;
364 if (!subsystem || !client)
367 rdpShadowServer* server = subsystem->server;
372 rdpShadowSurface* surface = server->surface;
377 x11->lastMouseClient = client;
379 XLockDisplay(x11->display);
380 XTestGrabControl(x11->display, True);
382 if (flags & PTR_FLAGS_MOVE)
383 XTestFakeRelativeMotionEvent(x11->display, xDelta, yDelta, 0);
385 if (flags & PTR_FLAGS_BUTTON1)
387 else if (flags & PTR_FLAGS_BUTTON2)
389 else if (flags & PTR_FLAGS_BUTTON3)
391 else if (flags & PTR_XFLAGS_BUTTON1)
393 else if (flags & PTR_XFLAGS_BUTTON2)
396 if (flags & PTR_FLAGS_DOWN)
400 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
402 XTestGrabControl(x11->display, False);
403 XFlush(x11->display);
404 XUnlockDisplay(x11->display);
406 WLog_WARN(TAG,
"RelMouseEvent not supported by backend, ignoring");
412static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
413 rdpShadowClient* client, UINT16 flags, UINT16 x,
417 x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
420 rdpShadowServer* server =
nullptr;
421 rdpShadowSurface* surface =
nullptr;
423 if (!subsystem || !client)
426 server = subsystem->server;
431 surface = server->surface;
436 x11->lastMouseClient = client;
439 XLockDisplay(x11->display);
440 XTestGrabControl(x11->display, True);
441 XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
443 if (flags & PTR_XFLAGS_BUTTON1)
445 else if (flags & PTR_XFLAGS_BUTTON2)
448 if (flags & PTR_XFLAGS_DOWN)
452 XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
454 XTestGrabControl(x11->display, False);
455 XFlush(x11->display);
456 XUnlockDisplay(x11->display);
458 WLog_WARN(TAG,
"ExtendedMouseEvent not supported by backend, ignoring");
463static void x11_shadow_message_free(UINT32
id, SHADOW_MSG_OUT* msg)
467 case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
471 case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
478 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"",
id);
485static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
487 UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
488 rdpShadowServer* server =
nullptr;
492 if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
495 templateMsg.xPos = subsystem->common.pointerX;
496 templateMsg.yPos = subsystem->common.pointerY;
497 templateMsg.common.Free = x11_shadow_message_free;
498 server = subsystem->common.server;
499 ArrayList_Lock(server->clients);
501 for (
size_t index = 0; index < ArrayList_Count(server->clients); index++)
504 rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
507 if (client == subsystem->lastMouseClient)
510 msg = malloc(
sizeof(templateMsg));
518 memcpy(msg, &templateMsg,
sizeof(templateMsg));
520 if (shadow_client_post_msg(client,
nullptr, msgId, (SHADOW_MSG_OUT*)msg,
nullptr))
524 ArrayList_Unlock(server->clients);
529static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
531 UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
538 msg->xHot = subsystem->cursorHotX;
539 msg->yHot = subsystem->cursorHotY;
540 msg->width = subsystem->cursorWidth;
541 msg->height = subsystem->cursorHeight;
543 if (shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
544 subsystem->cursorPixels, subsystem->format, TRUE, msg->width, msg->height, msg) < 0)
550 msg->common.Free = x11_shadow_message_free;
551 const int count = shadow_client_boardcast_msg(subsystem->common.server,
nullptr, msgId,
552 (SHADOW_MSG_OUT*)msg,
nullptr);
559static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
564 rdpShadowServer* server =
nullptr;
565 rdpShadowSurface* surface =
nullptr;
566 server = subsystem->common.server;
567 surface = server->surface;
572 UINT32* pDstPixel =
nullptr;
573 XFixesCursorImage* ci =
nullptr;
574 XLockDisplay(subsystem->display);
575 ci = XFixesGetCursorImage(subsystem->display);
576 XUnlockDisplay(subsystem->display);
584 if (ci->width > subsystem->cursorMaxWidth)
587 if (ci->height > subsystem->cursorMaxHeight)
590 subsystem->cursorHotX = ci->xhot;
591 subsystem->cursorHotY = ci->yhot;
592 subsystem->cursorWidth = ci->width;
593 subsystem->cursorHeight = ci->height;
594 subsystem->cursorId = ci->cursor_serial;
595 n = ci->width * ci->height;
596 pDstPixel = (UINT32*)subsystem->cursorPixels;
598 for (
int k = 0; k < n; k++)
601 *pDstPixel++ = (UINT32)ci->pixels[k];
605 const int rc = x11_shadow_pointer_alpha_update(subsystem);
619 XLockDisplay(subsystem->display);
621 if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
622 &root_y, &win_x, &win_y, &mask))
624 XUnlockDisplay(subsystem->display);
628 XUnlockDisplay(subsystem->display);
640 if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
642 subsystem->common.pointerX = (UINT32)MAX(0, x);
643 subsystem->common.pointerY = (UINT32)MAX(0, y);
644 return x11_shadow_pointer_position_update(subsystem);
651static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
653 if (xevent->type == MotionNotify)
658 else if (xevent->type == subsystem->xfixes_cursor_notify_event)
660 return x11_shadow_query_cursor(subsystem, TRUE);
671#if defined(USE_SHADOW_BLEND_CURSOR)
673static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
678 rdpShadowSurface* surface = subsystem->common.server->surface;
681 UINT32 nWidth = subsystem->cursorWidth;
682 UINT32 nHeight = subsystem->cursorHeight;
683 INT64 nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
684 INT64 nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
686 if (nXDst >= surface->width)
696 nXSrc = (UINT32)nXDst;
701 if (nYDst >= surface->height)
708 if (nYDst >= nHeight)
711 nYSrc = (UINT32)nYDst;
716 if ((nXDst + nWidth) > surface->width)
717 nWidth = (nXDst > surface->width) ? 0 : (UINT32)(surface->width - nXDst);
719 if ((nYDst + nHeight) > surface->height)
720 nHeight = (nYDst > surface->height) ? 0 : (UINT32)(surface->height - nYDst);
722 const BYTE* pSrcData = subsystem->cursorPixels;
723 const UINT32 nSrcStep = subsystem->cursorWidth * 4;
724 BYTE* pDstData = surface->data;
725 const UINT32 nDstStep = surface->scanline;
727 for (
size_t y = 0; y < nHeight; y++)
729 const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (4ULL * nXSrc)];
730 BYTE* pDstPixel = &pDstData[((WINPR_ASSERTING_INT_CAST(uint32_t, nYDst) + y) * nDstStep) +
731 (4ULL * WINPR_ASSERTING_INT_CAST(uint32_t, nXDst))];
733 for (
size_t x = 0; x < nWidth; x++)
735 const BYTE B = *pSrcPixel++;
736 const BYTE G = *pSrcPixel++;
737 const BYTE R = *pSrcPixel++;
738 const BYTE A = *pSrcPixel++;
748 pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
749 pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
750 pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
763static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
765 XWindowAttributes attr;
766 XLockDisplay(subsystem->display);
767 XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
768 XUnlockDisplay(subsystem->display);
770 if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
772 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
775 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
776 if (!shadow_screen_resize(subsystem->common.server->screen))
779 WINPR_ASSERT(attr.width > 0);
780 WINPR_ASSERT(attr.height > 0);
782 subsystem->width = (UINT32)attr.width;
783 subsystem->height = (UINT32)attr.height;
785 virtualScreen->left = 0;
786 virtualScreen->top = 0;
787 virtualScreen->right = attr.width - 1;
788 virtualScreen->bottom = attr.height - 1;
789 virtualScreen->flags = 1;
797static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
800 XGetErrorText(display, event->error_code, (
char*)&msg,
sizeof(msg));
801 WLog_ERR(TAG,
"X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
802 event->error_code, event->request_code, event->minor_code);
805 if (event->error_code != BadMatch)
814static int x11_shadow_screen_grab_disp_locked(x11ShadowSubsystem* subsystem, XImage** ppimage,
817 WINPR_ASSERT(subsystem);
818 WINPR_ASSERT(ppimage);
819 WINPR_ASSERT(invalidRect);
821 rdpShadowServer* server = subsystem->common.server;
822 WINPR_ASSERT(server);
824 rdpShadowSurface* surface = server->surface;
825 WINPR_ASSERT(surface);
829#if defined(WITH_XDAMAGE)
830 if (subsystem->use_xshm)
832 XImage* image = subsystem->fb_image;
835 XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
836 subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
838 EnterCriticalSection(&surface->lock);
839 status = shadow_capture_compare_with_format(
840 surface->data, surface->format, surface->scanline, surface->width, surface->height,
841 (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format,
842 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), invalidRect);
843 LeaveCriticalSection(&surface->lock);
848 EnterCriticalSection(&surface->lock);
849 XImage* image = XGetImage(subsystem->display, subsystem->root_window, surface->x,
850 surface->y, surface->width, surface->height, AllPlanes, ZPixmap);
854 status = shadow_capture_compare_with_format(
855 surface->data, surface->format, surface->scanline, surface->width, surface->height,
856 (BYTE*)image->data, subsystem->format,
857 WINPR_ASSERTING_INT_CAST(UINT32, image->bytes_per_line), invalidRect);
860 LeaveCriticalSection(&surface->lock);
867static BOOL x11_shadow_surface_update_invalid(rdpShadowSurface* surface,
871 WINPR_ASSERT(surface);
872 WINPR_ASSERT(invalidRect);
873 WINPR_ASSERT(surfaceRect);
875 EnterCriticalSection(&surface->lock);
877 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), invalidRect);
879 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), surfaceRect);
880 const BOOL empty = region16_is_empty(&(surface->invalidRegion));
881 LeaveCriticalSection(&surface->lock);
882 return !rc1 || !rc2 || empty;
886static BOOL x11_shadow_surface_update_contents(rdpShadowSurface* surface, UINT32 format,
889 WINPR_ASSERT(surface);
892 EnterCriticalSection(&surface->lock);
893 const RECTANGLE_16* extents = region16_extents(&(surface->invalidRegion));
894 const UINT16 x = extents->left;
895 const UINT16 y = extents->top;
896 const UINT16 width = extents->right - extents->left;
897 const UINT16 height = extents->bottom - extents->top;
899 WINPR_ASSERT(image->bytes_per_line >= 0);
900 WINPR_ASSERT(width >= 0);
901 WINPR_ASSERT(height >= 0);
902 const BOOL success = freerdp_image_copy_no_overlap(
903 surface->data, surface->format, surface->scanline, WINPR_ASSERTING_INT_CAST(uint32_t, x),
904 WINPR_ASSERTING_INT_CAST(uint32_t, y), WINPR_ASSERTING_INT_CAST(uint32_t, width),
905 WINPR_ASSERTING_INT_CAST(uint32_t, height), (BYTE*)image->data, format,
906 WINPR_ASSERTING_INT_CAST(uint32_t, image->bytes_per_line),
907 WINPR_ASSERTING_INT_CAST(UINT32, x), WINPR_ASSERTING_INT_CAST(UINT32, y),
nullptr,
909 LeaveCriticalSection(&surface->lock);
914static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
918 WINPR_ASSERT(subsystem);
921 rdpShadowServer* server = subsystem->common.server;
922 WINPR_ASSERT(server);
924 rdpShadowSurface* surface = server->surface;
925 WINPR_ASSERT(surface);
927 size_t count = ArrayList_Count(server->clients);
933 EnterCriticalSection(&surface->lock);
934 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->width);
935 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->height);
936 LeaveCriticalSection(&surface->lock);
939 XImage* image =
nullptr;
943 XLockDisplay(subsystem->display);
948 XSetErrorHandler(x11_shadow_error_handler_for_capture);
950 status = x11_shadow_screen_grab_disp_locked(subsystem, &image, &invalidRect);
955 XSetErrorHandler(
nullptr);
956 XSync(subsystem->display, False);
957 XUnlockDisplay(subsystem->display);
962 const BOOL empty = x11_shadow_surface_update_invalid(surface, &invalidRect, &surfaceRect);
967 x11_shadow_surface_update_contents(surface, subsystem->format, image);
971#if defined(USE_SHADOW_BLEND_CURSOR)
972 if (x11_shadow_blend_cursor(subsystem) < 0)
975 count = ArrayList_Count(server->clients);
976 shadow_subsystem_frame_update(&subsystem->common);
980 rdpShadowClient* client =
nullptr;
981 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
984 subsystem->common.captureFrameRate =
985 shadow_encoder_preferred_fps(client->encoder);
989 EnterCriticalSection(&surface->lock);
990 region16_clear(&(surface->invalidRegion));
991 LeaveCriticalSection(&surface->lock);
998 if (!subsystem->use_xshm && image)
999 XDestroyImage(image);
1005static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
1007 switch (message->id)
1009 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
1010 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
1014 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"", message->id);
1019 message->Free(message);
1025static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
1027 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
1031 DWORD dwInterval = 0;
1032 UINT64 frameTime = 0;
1036 MsgPipe = subsystem->common.MsgPipe;
1038 events[nCount++] = subsystem->common.event;
1039 events[nCount++] = MessageQueue_Event(MsgPipe->In);
1040 subsystem->common.captureFrameRate = 16;
1041 dwInterval = 1000 / subsystem->common.captureFrameRate;
1042 frameTime = GetTickCount64() + dwInterval;
1046 const UINT64 cTime = GetTickCount64();
1047 const DWORD dwTimeout =
1048 (DWORD)((cTime > frameTime) ? 0 : MIN(UINT32_MAX, frameTime - cTime));
1049 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
1051 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
1053 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
1055 if (message.id == WMQ_QUIT)
1058 const int rc = x11_shadow_subsystem_process_message(subsystem, &message);
1064 if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
1067 XLockDisplay(subsystem->display);
1069 if (XEventsQueued(subsystem->display, QueuedAlready))
1071 XNextEvent(subsystem->display, &xevent);
1072 rc = x11_shadow_handle_xevent(subsystem, &xevent);
1075 XUnlockDisplay(subsystem->display);
1080 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
1082 const int rc1 = x11_shadow_check_resize(subsystem);
1085 const int rc2 = x11_shadow_screen_grab(subsystem);
1088 const int rc3 = x11_shadow_query_cursor(subsystem, FALSE);
1091 dwInterval = 1000 / subsystem->common.captureFrameRate;
1092 frameTime += dwInterval;
1101static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
1103 if (subsystem->display)
1107 if (!getenv(
"DISPLAY"))
1110 setenv(
"DISPLAY",
":0", 1);
1113 if (!XInitThreads())
1116 subsystem->display = XOpenDisplay(
nullptr);
1118 if (!subsystem->display)
1120 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(
nullptr));
1124 subsystem->xfds = ConnectionNumber(subsystem->display);
1125 subsystem->number = DefaultScreen(subsystem->display);
1126 subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
1127 subsystem->depth = WINPR_ASSERTING_INT_CAST(UINT32, DefaultDepthOfScreen(subsystem->screen));
1128 subsystem->width = WINPR_ASSERTING_INT_CAST(UINT32, WidthOfScreen(subsystem->screen));
1129 subsystem->height = WINPR_ASSERTING_INT_CAST(UINT32, HeightOfScreen(subsystem->screen));
1130 subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
1135static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
1138 int xfixes_event = 0;
1139 int xfixes_error = 0;
1143 if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
1146 if (!XFixesQueryVersion(subsystem->display, &major, &minor))
1149 subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
1150 XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
1151 XFixesDisplayCursorNotifyMask);
1159static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1162 int xinerama_event = 0;
1163 int xinerama_error = 0;
1165 const int rc = x11_shadow_subsystem_base_init(subsystem);
1169 if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1172#if defined(WITH_XDAMAGE)
1175 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1179 if (!XineramaIsActive(subsystem->display))
1189static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1194 int damage_event = 0;
1195 int damage_error = 0;
1197 if (!subsystem->use_xfixes)
1200 if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1203 if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1209 subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1210 subsystem->xdamage =
1211 XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1213 if (!subsystem->xdamage)
1217 subsystem->xdamage_region = XFixesCreateRegion(subsystem->display,
nullptr, 0);
1219 if (!subsystem->xdamage_region)
1230static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1237 if (!XShmQueryExtension(subsystem->display))
1240 if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1246 subsystem->fb_shm_info.shmid = -1;
1247 subsystem->fb_shm_info.shmaddr = (
char*)-1;
1248 subsystem->fb_shm_info.readOnly = False;
1249 subsystem->fb_image =
1250 XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap,
nullptr,
1251 &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1253 if (!subsystem->fb_image)
1255 WLog_ERR(TAG,
"XShmCreateImage failed");
1259 subsystem->fb_shm_info.shmid =
1261 1ull * WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->bytes_per_line) *
1262 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1265 if (subsystem->fb_shm_info.shmid == -1)
1267 WLog_ERR(TAG,
"shmget failed");
1271 subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid,
nullptr, 0);
1272 subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1274 if (subsystem->fb_shm_info.shmaddr == ((
char*)-1))
1276 WLog_ERR(TAG,
"shmat failed");
1280 if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1283 XSync(subsystem->display, False);
1284 shmctl(subsystem->fb_shm_info.shmid, IPC_RMID,
nullptr);
1285 subsystem->fb_pixmap = XShmCreatePixmap(
1286 subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1287 &(subsystem->fb_shm_info), WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->width),
1288 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->height),
1289 WINPR_ASSERTING_INT_CAST(uint32_t, subsystem->fb_image->depth));
1290 XSync(subsystem->display, False);
1292 if (!subsystem->fb_pixmap)
1295 values.subwindow_mode = IncludeInferiors;
1296 values.graphics_exposures = False;
1297#if defined(WITH_XDAMAGE)
1298 subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1299 GCSubwindowMode | GCGraphicsExposures, &values);
1300 XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1302 XSync(subsystem->display, False);
1306UINT32 x11_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
1308 Display* display =
nullptr;
1309 int displayWidth = 0;
1310 int displayHeight = 0;
1311 int numMonitors = 0;
1314 if (!getenv(
"DISPLAY"))
1317 setenv(
"DISPLAY",
":0", 1);
1320 display = XOpenDisplay(
nullptr);
1324 WLog_ERR(TAG,
"failed to open display: %s", XDisplayName(
nullptr));
1328 displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1329 displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1332#if defined(WITH_XDAMAGE)
1336 int xinerama_event = 0;
1337 int xinerama_error = 0;
1338 XineramaScreenInfo* screens =
nullptr;
1340 const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
1342#if defined(WITH_XDAMAGE)
1343 XDamageQueryVersion(display, &major, &minor);
1348 if (xinerama && damage && XineramaIsActive(display))
1350 screens = XineramaQueryScreens(display, &numMonitors);
1352 if (numMonitors > (INT64)maxMonitors)
1353 numMonitors = (int)maxMonitors;
1355 if (screens && (numMonitors > 0))
1357 for (
int index = 0; index < numMonitors; index++)
1360 const XineramaScreenInfo* screen = &screens[index];
1362 monitor->left = screen->x_org;
1363 monitor->top = screen->y_org;
1364 monitor->right = monitor->left + screen->width - 1;
1365 monitor->bottom = monitor->top + screen->height - 1;
1366 monitor->flags = (index == 0) ? 1 : 0;
1374 XCloseDisplay(display);
1376 if (numMonitors < 1)
1383 monitor->right = displayWidth - 1;
1384 monitor->bottom = displayHeight - 1;
1389 return WINPR_ASSERTING_INT_CAST(uint32_t, numMonitors);
1393static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1397 int nextensions = 0;
1398 XVisualInfo xtemplate = WINPR_C_ARRAY_INIT;
1399 XPixmapFormatValues* pf =
nullptr;
1401 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1406 subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1407 const int rc = x11_shadow_subsystem_base_init(subsystem);
1411 subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRA32
1412 : PIXEL_FORMAT_ARGB32;
1414 if ((subsystem->depth != 24) && (subsystem->depth != 32))
1416 WLog_ERR(TAG,
"unsupported X11 server color depth: %" PRIu32, subsystem->depth);
1421 char** extensions = XListExtensions(subsystem->display, &nextensions);
1423 if (!extensions || (nextensions < 0))
1426 for (
int i = 0; i < nextensions; i++)
1428 if (strcmp(extensions[i],
"Composite") == 0)
1429 subsystem->composite = TRUE;
1432 XFreeExtensionList(extensions);
1435 if (subsystem->composite)
1436 subsystem->use_xdamage = FALSE;
1439 XPixmapFormatValues* pfs = XListPixmapFormats(subsystem->display, &pf_count);
1443 WLog_ERR(TAG,
"XListPixmapFormats failed");
1447 for (
int i = 0; i < pf_count; i++)
1451 if (pf->depth == (INT64)subsystem->depth)
1453 subsystem->bpp = WINPR_ASSERTING_INT_CAST(uint32_t, pf->bits_per_pixel);
1454 subsystem->scanline_pad = WINPR_ASSERTING_INT_CAST(uint32_t, pf->scanline_pad);
1462 xtemplate.class = TrueColor;
1463 xtemplate.screen = subsystem->number;
1466 XVisualInfo* vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask,
1467 &xtemplate, &vi_count);
1471 WLog_ERR(TAG,
"XGetVisualInfo failed");
1475 for (
int i = 0; i < vi_count; i++)
1477 XVisualInfo* vi = vis + i;
1479 if (vi->depth == (INT64)subsystem->depth)
1481 subsystem->visual = vi->visual;
1489 XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1490 subsystem->cursorMaxWidth = 256;
1491 subsystem->cursorMaxHeight = 256;
1492 subsystem->cursorPixels =
1493 winpr_aligned_malloc(4ULL * subsystem->cursorMaxWidth * subsystem->cursorMaxHeight, 16);
1495 if (!subsystem->cursorPixels)
1498 const int rc1 = x11_shadow_query_cursor(subsystem, TRUE);
1502 if (subsystem->use_xfixes)
1504 if (x11_shadow_xfixes_init(subsystem) < 0)
1505 subsystem->use_xfixes = FALSE;
1508 if (subsystem->use_xinerama)
1510 if (x11_shadow_xinerama_init(subsystem) < 0)
1511 subsystem->use_xinerama = FALSE;
1514 if (subsystem->use_xshm)
1516 if (x11_shadow_xshm_init(subsystem) < 0)
1517 subsystem->use_xshm = FALSE;
1520 if (subsystem->use_xdamage)
1522 if (x11_shadow_xdamage_init(subsystem) < 0)
1523 subsystem->use_xdamage = FALSE;
1526 if (!(subsystem->common.event =
1527 CreateFileDescriptorEvent(
nullptr, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1531 MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
1532 virtualScreen->left = 0;
1533 virtualScreen->top = 0;
1534 WINPR_ASSERT(subsystem->width <= INT32_MAX);
1535 WINPR_ASSERT(subsystem->height <= INT32_MAX);
1536 virtualScreen->right = (INT32)subsystem->width - 1;
1537 virtualScreen->bottom = (INT32)subsystem->height - 1;
1538 virtualScreen->flags = 1;
1540 "X11 Extensions: XFixes: %" PRId32
" Xinerama: %" PRId32
" XDamage: %" PRId32
1541 " XShm: %" PRId32
"",
1542 subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1543 subsystem->use_xshm);
1549static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1551 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1556 if (subsystem->display)
1558 XCloseDisplay(subsystem->display);
1559 subsystem->display =
nullptr;
1562 if (subsystem->common.event)
1564 (void)CloseHandle(subsystem->common.event);
1565 subsystem->common.event =
nullptr;
1568 if (subsystem->cursorPixels)
1570 winpr_aligned_free(subsystem->cursorPixels);
1571 subsystem->cursorPixels =
nullptr;
1578static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1580 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1585 if (!(subsystem->thread =
1586 CreateThread(
nullptr, 0, x11_shadow_subsystem_thread, (
void*)subsystem, 0,
nullptr)))
1588 WLog_ERR(TAG,
"Failed to create thread");
1596static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1598 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1603 if (subsystem->thread)
1605 if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1606 (
void)WaitForSingleObject(subsystem->thread, INFINITE);
1608 (void)CloseHandle(subsystem->thread);
1609 subsystem->thread =
nullptr;
1616static rdpShadowSubsystem* x11_shadow_subsystem_new(
void)
1618 x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)calloc(1,
sizeof(x11ShadowSubsystem));
1624 subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1626 subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1627 subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1628 subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1629 subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1630 subsystem->common.RelMouseEvent = x11_shadow_input_rel_mouse_event;
1631 subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1632 subsystem->composite = FALSE;
1633 subsystem->use_xshm = FALSE;
1634 subsystem->use_xfixes = TRUE;
1635 subsystem->use_xdamage = FALSE;
1636 subsystem->use_xinerama = TRUE;
1637 return &subsystem->common;
1640static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1645 x11_shadow_subsystem_uninit(subsystem);
1649FREERDP_ENTRY_POINT(FREERDP_API WINPR_ATTR_NODISCARD
const char* ShadowSubsystemName(
void))
1654FREERDP_ENTRY_POINT(FREERDP_API WINPR_ATTR_NODISCARD
int ShadowSubsystemEntry(
1655 RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
1660 pEntryPoints->New = x11_shadow_subsystem_new;
1661 pEntryPoints->Free = x11_shadow_subsystem_free;
1662 pEntryPoints->Init = x11_shadow_subsystem_init;
1663 pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1664 pEntryPoints->Start = x11_shadow_subsystem_start;
1665 pEntryPoints->Stop = x11_shadow_subsystem_stop;
1666 pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;