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