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