FreeRDP
xf_client.c
1 
25 #include <freerdp/config.h>
26 
27 #include <math.h>
28 #include <winpr/cast.h>
29 #include <winpr/assert.h>
30 #include <winpr/sspicli.h>
31 
32 #include <float.h>
33 
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 #include <X11/Xatom.h>
37 
38 #ifdef WITH_XRENDER
39 #include <X11/extensions/Xrender.h>
40 #endif
41 
42 #ifdef WITH_XI
43 #include <X11/extensions/XInput.h>
44 #include <X11/extensions/XInput2.h>
45 #endif
46 
47 #ifdef WITH_XCURSOR
48 #include <X11/Xcursor/Xcursor.h>
49 #endif
50 
51 #ifdef WITH_XINERAMA
52 #include <X11/extensions/Xinerama.h>
53 #endif
54 
55 #include <X11/XKBlib.h>
56 
57 #include <errno.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <locale.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <termios.h>
64 #include <pthread.h>
65 #include <sys/wait.h>
66 #include <sys/types.h>
67 #include <sys/select.h>
68 #include <fcntl.h>
69 #include <sys/stat.h>
70 
71 #include <freerdp/freerdp.h>
72 #include <freerdp/constants.h>
73 #include <freerdp/codec/nsc.h>
74 #include <freerdp/codec/rfx.h>
75 #include <freerdp/codec/color.h>
76 #include <freerdp/codec/bitmap.h>
77 
78 #include <freerdp/utils/signal.h>
79 #include <freerdp/utils/passphrase.h>
80 #include <freerdp/client/cliprdr.h>
81 #include <freerdp/client/channels.h>
82 
83 #include <freerdp/client/file.h>
84 #include <freerdp/client/cmdline.h>
85 
86 #include <winpr/crt.h>
87 #include <winpr/synch.h>
88 #include <winpr/file.h>
89 #include <winpr/print.h>
90 #include <winpr/sysinfo.h>
91 
92 #include "xf_rail.h"
93 #if defined(CHANNEL_TSMF_CLIENT)
94 #include "xf_tsmf.h"
95 #endif
96 #include "xf_event.h"
97 #include "xf_input.h"
98 #include "xf_cliprdr.h"
99 #include "xf_disp.h"
100 #include "xf_video.h"
101 #include "xf_monitor.h"
102 #include "xf_graphics.h"
103 #include "xf_keyboard.h"
104 #include "xf_channels.h"
105 #include "xf_client.h"
106 #include "xfreerdp.h"
107 #include "xf_utils.h"
108 
109 #include <freerdp/log.h>
110 #define TAG CLIENT_TAG("x11")
111 
112 #define MIN_PIXEL_DIFF 0.001
113 
114 struct xf_exit_code_map_t
115 {
116  DWORD error;
117  int rc;
118 };
119 static const struct xf_exit_code_map_t xf_exit_code_map[] = {
120  { FREERDP_ERROR_AUTHENTICATION_FAILED, XF_EXIT_AUTH_FAILURE },
121  { FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED, XF_EXIT_NEGO_FAILURE },
122  { FREERDP_ERROR_CONNECT_LOGON_FAILURE, XF_EXIT_LOGON_FAILURE },
123  { FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT, XF_EXIT_ACCOUNT_LOCKED_OUT },
124  { FREERDP_ERROR_PRE_CONNECT_FAILED, XF_EXIT_PRE_CONNECT_FAILED },
125  { FREERDP_ERROR_CONNECT_UNDEFINED, XF_EXIT_CONNECT_UNDEFINED },
126  { FREERDP_ERROR_POST_CONNECT_FAILED, XF_EXIT_POST_CONNECT_FAILED },
127  { FREERDP_ERROR_DNS_ERROR, XF_EXIT_DNS_ERROR },
128  { FREERDP_ERROR_DNS_NAME_NOT_FOUND, XF_EXIT_DNS_NAME_NOT_FOUND },
129  { FREERDP_ERROR_CONNECT_FAILED, XF_EXIT_CONNECT_FAILED },
130  { FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR, XF_EXIT_MCS_CONNECT_INITIAL_ERROR },
131  { FREERDP_ERROR_TLS_CONNECT_FAILED, XF_EXIT_TLS_CONNECT_FAILED },
132  { FREERDP_ERROR_INSUFFICIENT_PRIVILEGES, XF_EXIT_INSUFFICIENT_PRIVILEGES },
133  { FREERDP_ERROR_CONNECT_CANCELLED, XF_EXIT_CONNECT_CANCELLED },
134  { FREERDP_ERROR_CONNECT_TRANSPORT_FAILED, XF_EXIT_CONNECT_TRANSPORT_FAILED },
135  { FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED, XF_EXIT_CONNECT_PASSWORD_EXPIRED },
136  { FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE, XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE },
137  { FREERDP_ERROR_CONNECT_KDC_UNREACHABLE, XF_EXIT_CONNECT_KDC_UNREACHABLE },
138  { FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED, XF_EXIT_CONNECT_ACCOUNT_DISABLED },
139  { FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED,
140  XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED },
141  { FREERDP_ERROR_CONNECT_CLIENT_REVOKED, XF_EXIT_CONNECT_CLIENT_REVOKED },
142  { FREERDP_ERROR_CONNECT_WRONG_PASSWORD, XF_EXIT_CONNECT_WRONG_PASSWORD },
143  { FREERDP_ERROR_CONNECT_ACCESS_DENIED, XF_EXIT_CONNECT_ACCESS_DENIED },
144  { FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION, XF_EXIT_CONNECT_ACCOUNT_RESTRICTION },
145  { FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED, XF_EXIT_CONNECT_ACCOUNT_EXPIRED },
146  { FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED, XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED },
147  { FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS, XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS }
148 };
149 
150 static BOOL xf_setup_x11(xfContext* xfc);
151 static void xf_teardown_x11(xfContext* xfc);
152 
153 static int xf_map_error_to_exit_code(DWORD error)
154 {
155  for (size_t x = 0; x < ARRAYSIZE(xf_exit_code_map); x++)
156  {
157  const struct xf_exit_code_map_t* cur = &xf_exit_code_map[x];
158  if (cur->error == error)
159  return cur->rc;
160  }
161 
162  return XF_EXIT_CONN_FAILED;
163 }
164 static int (*def_error_handler)(Display*, XErrorEvent*);
165 static int xf_error_handler_ex(Display* d, XErrorEvent* ev);
166 static void xf_check_extensions(xfContext* context);
167 static void xf_window_free(xfContext* xfc);
168 static BOOL xf_get_pixmap_info(xfContext* xfc);
169 
170 #ifdef WITH_XRENDER
171 static void xf_draw_screen_scaled(xfContext* xfc, int x, int y, int w, int h)
172 {
173  XTransform transform = { 0 };
174  Picture windowPicture = 0;
175  Picture primaryPicture = 0;
176  XRenderPictureAttributes pa;
177  XRenderPictFormat* picFormat = NULL;
178  int x2 = 0;
179  int y2 = 0;
180  const char* filter = NULL;
181  WINPR_ASSERT(xfc);
182 
183  rdpSettings* settings = xfc->common.context.settings;
184  WINPR_ASSERT(settings);
185 
186  if (xfc->scaledWidth <= 0 || xfc->scaledHeight <= 0)
187  {
188  WLog_ERR(TAG, "the current window dimensions are invalid");
189  return;
190  }
191 
192  if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) <= 0 ||
193  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) <= 0)
194  {
195  WLog_ERR(TAG, "the window dimensions are invalid");
196  return;
197  }
198 
199  const double xScalingFactor = 1.0 *
200  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) /
201  (double)xfc->scaledWidth;
202  const double yScalingFactor = 1.0 *
203  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) /
204  (double)xfc->scaledHeight;
205  XSetFillStyle(xfc->display, xfc->gc, FillSolid);
206  XSetForeground(xfc->display, xfc->gc, 0);
207  /* Black out possible space between desktop and window borders */
208  {
209  XRectangle box1 = { 0, 0, WINPR_ASSERTING_INT_CAST(UINT16, xfc->window->width),
210  WINPR_ASSERTING_INT_CAST(UINT16, xfc->window->height) };
211  XRectangle box2 = { WINPR_ASSERTING_INT_CAST(INT16, xfc->offset_x),
212  WINPR_ASSERTING_INT_CAST(INT16, xfc->offset_y),
213  WINPR_ASSERTING_INT_CAST(UINT16, xfc->scaledWidth),
214  WINPR_ASSERTING_INT_CAST(UINT16, xfc->scaledHeight) };
215  Region reg1 = XCreateRegion();
216  Region reg2 = XCreateRegion();
217  XUnionRectWithRegion(&box1, reg1, reg1);
218  XUnionRectWithRegion(&box2, reg2, reg2);
219 
220  if (XSubtractRegion(reg1, reg2, reg1) && !XEmptyRegion(reg1))
221  {
222  XSetRegion(xfc->display, xfc->gc, reg1);
223  XFillRectangle(xfc->display, xfc->window->handle, xfc->gc, 0, 0,
224  WINPR_ASSERTING_INT_CAST(UINT16, xfc->window->width),
225  WINPR_ASSERTING_INT_CAST(UINT16, xfc->window->height));
226  XSetClipMask(xfc->display, xfc->gc, None);
227  }
228 
229  XDestroyRegion(reg1);
230  XDestroyRegion(reg2);
231  }
232  picFormat = XRenderFindVisualFormat(xfc->display, xfc->visual);
233  pa.subwindow_mode = IncludeInferiors;
234  primaryPicture =
235  XRenderCreatePicture(xfc->display, xfc->primary, picFormat, CPSubwindowMode, &pa);
236  windowPicture =
237  XRenderCreatePicture(xfc->display, xfc->window->handle, picFormat, CPSubwindowMode, &pa);
238  /* avoid blurry filter when scaling factor is 2x, 3x, etc
239  * useful when the client has high-dpi monitor */
240  filter = FilterBilinear;
241  if (fabs(xScalingFactor - yScalingFactor) < MIN_PIXEL_DIFF)
242  {
243  const double inverseX = 1.0 / xScalingFactor;
244  const double inverseRoundedX = round(inverseX);
245  const double absInverse = fabs(inverseX - inverseRoundedX);
246 
247  if (absInverse < MIN_PIXEL_DIFF)
248  filter = FilterNearest;
249  }
250  XRenderSetPictureFilter(xfc->display, primaryPicture, filter, 0, 0);
251  transform.matrix[0][0] = XDoubleToFixed(xScalingFactor);
252  transform.matrix[0][1] = XDoubleToFixed(0.0);
253  transform.matrix[0][2] = XDoubleToFixed(0.0);
254  transform.matrix[1][0] = XDoubleToFixed(0.0);
255  transform.matrix[1][1] = XDoubleToFixed(yScalingFactor);
256  transform.matrix[1][2] = XDoubleToFixed(0.0);
257  transform.matrix[2][0] = XDoubleToFixed(0.0);
258  transform.matrix[2][1] = XDoubleToFixed(0.0);
259  transform.matrix[2][2] = XDoubleToFixed(1.0);
260  /* calculate and fix up scaled coordinates */
261  x2 = x + w;
262  y2 = y + h;
263 
264  const double dx1 = floor(x / xScalingFactor);
265  const double dy1 = floor(y / yScalingFactor);
266  const double dx2 = ceil(x2 / xScalingFactor);
267  const double dy2 = ceil(y2 / yScalingFactor);
268  x = ((int)dx1) - 1;
269  y = ((int)dy1) - 1;
270  w = ((int)dx2) + 1 - x;
271  h = ((int)dy2) + 1 - y;
272  XRenderSetPictureTransform(xfc->display, primaryPicture, &transform);
273  XRenderComposite(xfc->display, PictOpSrc, primaryPicture, 0, windowPicture, x, y, 0, 0,
274  xfc->offset_x + x, xfc->offset_y + y, WINPR_ASSERTING_INT_CAST(uint32_t, w),
275  WINPR_ASSERTING_INT_CAST(uint32_t, h));
276  XRenderFreePicture(xfc->display, primaryPicture);
277  XRenderFreePicture(xfc->display, windowPicture);
278 }
279 
280 BOOL xf_picture_transform_required(xfContext* xfc)
281 {
282  rdpSettings* settings = NULL;
283 
284  WINPR_ASSERT(xfc);
285 
286  settings = xfc->common.context.settings;
287  WINPR_ASSERT(settings);
288 
289  if ((xfc->offset_x != 0) || (xfc->offset_y != 0) ||
290  (xfc->scaledWidth != (INT64)freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)) ||
291  (xfc->scaledHeight != (INT64)freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)))
292  {
293  return TRUE;
294  }
295 
296  return FALSE;
297 }
298 #endif /* WITH_XRENDER defined */
299 
300 void xf_draw_screen_(xfContext* xfc, int x, int y, int w, int h, const char* fkt, const char* file,
301  int line)
302 {
303  if (!xfc)
304  {
305  WLog_DBG(TAG, "called from [%s] xfc=%p", fkt, xfc);
306  return;
307  }
308 
309  if (w == 0 || h == 0)
310  {
311  WLog_WARN(TAG, "invalid width and/or height specified: w=%d h=%d", w, h);
312  return;
313  }
314 
315  if (!xfc->window)
316  {
317  WLog_WARN(TAG, "invalid xfc->window=%p", xfc->window);
318  return;
319  }
320 
321 #ifdef WITH_XRENDER
322 
323  if (xf_picture_transform_required(xfc))
324  {
325  xf_draw_screen_scaled(xfc, x, y, w, h);
326  return;
327  }
328 
329 #endif
330  XCopyArea(xfc->display, xfc->primary, xfc->window->handle, xfc->gc, x, y,
331  WINPR_ASSERTING_INT_CAST(uint32_t, w), WINPR_ASSERTING_INT_CAST(uint32_t, h), x, y);
332 }
333 
334 static BOOL xf_desktop_resize(rdpContext* context)
335 {
336  rdpSettings* settings = NULL;
337  xfContext* xfc = (xfContext*)context;
338 
339  WINPR_ASSERT(xfc);
340 
341  settings = context->settings;
342  WINPR_ASSERT(settings);
343 
344  if (xfc->primary)
345  {
346  BOOL same = (xfc->primary == xfc->drawing) ? TRUE : FALSE;
347  XFreePixmap(xfc->display, xfc->primary);
348 
349  WINPR_ASSERT(xfc->depth != 0);
350  if (!(xfc->primary =
351  XCreatePixmap(xfc->display, xfc->drawable,
352  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
353  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
354  WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth))))
355  return FALSE;
356 
357  if (same)
358  xfc->drawing = xfc->primary;
359  }
360 
361 #ifdef WITH_XRENDER
362 
363  if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
364  {
365  xfc->scaledWidth = WINPR_ASSERTING_INT_CAST(
366  int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
367  xfc->scaledHeight = WINPR_ASSERTING_INT_CAST(
368  int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
369  }
370 
371 #endif
372 
373  if (!xfc->fullscreen)
374  {
375  xf_ResizeDesktopWindow(
376  xfc, xfc->window,
377  WINPR_ASSERTING_INT_CAST(int,
378  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)),
379  WINPR_ASSERTING_INT_CAST(int,
380  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)));
381  }
382  else
383  {
384 #ifdef WITH_XRENDER
385 
386  if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
387 #endif
388  {
389  /* Update the saved width and height values the window will be
390  * resized to when toggling out of fullscreen */
391  xfc->savedWidth = WINPR_ASSERTING_INT_CAST(
392  int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
393  xfc->savedHeight = WINPR_ASSERTING_INT_CAST(
394  int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
395  }
396 
397  XSetFunction(xfc->display, xfc->gc, GXcopy);
398  XSetFillStyle(xfc->display, xfc->gc, FillSolid);
399  XSetForeground(xfc->display, xfc->gc, 0);
400  XFillRectangle(xfc->display, xfc->drawable, xfc->gc, 0, 0,
401  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
402  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
403  }
404 
405  return TRUE;
406 }
407 
408 static BOOL xf_paint(xfContext* xfc, const GDI_RGN* region)
409 {
410  WINPR_ASSERT(xfc);
411  WINPR_ASSERT(region);
412 
413  if (xfc->remote_app)
414  {
415  const RECTANGLE_16 rect = { .left = WINPR_ASSERTING_INT_CAST(UINT16, region->x),
416  .top = WINPR_ASSERTING_INT_CAST(UINT16, region->y),
417  .right =
418  WINPR_ASSERTING_INT_CAST(UINT16, region->x + region->w),
419  .bottom =
420  WINPR_ASSERTING_INT_CAST(UINT16, region->y + region->h) };
421  xf_rail_paint(xfc, &rect);
422  }
423  else
424  {
425  XPutImage(xfc->display, xfc->primary, xfc->gc, xfc->image, region->x, region->y, region->x,
426  region->y, WINPR_ASSERTING_INT_CAST(UINT16, region->w),
427  WINPR_ASSERTING_INT_CAST(UINT16, region->h));
428  xf_draw_screen(xfc, region->x, region->y, region->w, region->h);
429  }
430  return TRUE;
431 }
432 
433 static BOOL xf_end_paint(rdpContext* context)
434 {
435  xfContext* xfc = (xfContext*)context;
436  rdpGdi* gdi = context->gdi;
437 
438  if (gdi->suppressOutput)
439  return TRUE;
440 
441  HGDI_DC hdc = gdi->primary->hdc;
442 
443  if (!xfc->complex_regions)
444  {
445  const GDI_RGN* rgn = hdc->hwnd->invalid;
446  if (rgn->null)
447  return TRUE;
448  xf_lock_x11(xfc);
449  if (!xf_paint(xfc, rgn))
450  return FALSE;
451  xf_unlock_x11(xfc);
452  }
453  else
454  {
455  const INT32 ninvalid = hdc->hwnd->ninvalid;
456  const GDI_RGN* cinvalid = hdc->hwnd->cinvalid;
457 
458  if (hdc->hwnd->ninvalid < 1)
459  return TRUE;
460 
461  xf_lock_x11(xfc);
462 
463  for (INT32 i = 0; i < ninvalid; i++)
464  {
465  const GDI_RGN* rgn = &cinvalid[i];
466  if (!xf_paint(xfc, rgn))
467  return FALSE;
468  }
469 
470  XFlush(xfc->display);
471  xf_unlock_x11(xfc);
472  }
473 
474  hdc->hwnd->invalid->null = TRUE;
475  hdc->hwnd->ninvalid = 0;
476  return TRUE;
477 }
478 
479 static BOOL xf_sw_desktop_resize(rdpContext* context)
480 {
481  rdpGdi* gdi = context->gdi;
482  xfContext* xfc = (xfContext*)context;
483  rdpSettings* settings = context->settings;
484  BOOL ret = FALSE;
485 
486  if (!gdi_resize(gdi, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
487  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)))
488  return FALSE;
489 
490  /* Do not lock during gdi_resize, there might still be drawing operations in progress.
491  * locking will deadlock. */
492  xf_lock_x11(xfc);
493  if (xfc->image)
494  {
495  xfc->image->data = NULL;
496  XDestroyImage(xfc->image);
497  }
498 
499  WINPR_ASSERT(xfc->depth != 0);
500  if (!(xfc->image = XCreateImage(
501  xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth), ZPixmap, 0,
502  (char*)gdi->primary_buffer, WINPR_ASSERTING_INT_CAST(uint32_t, gdi->width),
503  WINPR_ASSERTING_INT_CAST(uint32_t, gdi->height), xfc->scanline_pad,
504  WINPR_ASSERTING_INT_CAST(int, gdi->stride))))
505  {
506  goto out;
507  }
508 
509  xfc->image->byte_order = LSBFirst;
510  xfc->image->bitmap_bit_order = LSBFirst;
511  ret = xf_desktop_resize(context);
512 out:
513  xf_unlock_x11(xfc);
514  return ret;
515 }
516 
517 static BOOL xf_process_x_events(freerdp* instance)
518 {
519  BOOL status = TRUE;
520  int pending_status = 1;
521  xfContext* xfc = (xfContext*)instance->context;
522 
523  while (pending_status)
524  {
525  xf_lock_x11(xfc);
526  pending_status = XPending(xfc->display);
527 
528  if (pending_status)
529  {
530  XEvent xevent = { 0 };
531 
532  XNextEvent(xfc->display, &xevent);
533  status = xf_event_process(instance, &xevent);
534  }
535  xf_unlock_x11(xfc);
536  if (!status)
537  break;
538  }
539 
540  return status;
541 }
542 
543 static char* xf_window_get_title(rdpSettings* settings)
544 {
545  BOOL port = 0;
546  char* windowTitle = NULL;
547  size_t size = 0;
548  const char* prefix = "FreeRDP:";
549 
550  if (!settings)
551  return NULL;
552 
553  const char* name = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
554  const char* title = freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
555 
556  if (title)
557  return _strdup(title);
558 
559  port = (freerdp_settings_get_uint32(settings, FreeRDP_ServerPort) != 3389);
560  /* Just assume a window title is never longer than a filename... */
561  size = strnlen(name, MAX_PATH) + 16;
562  windowTitle = calloc(size, sizeof(char));
563 
564  if (!windowTitle)
565  return NULL;
566 
567  if (!port)
568  (void)sprintf_s(windowTitle, size, "%s %s", prefix, name);
569  else
570  (void)sprintf_s(windowTitle, size, "%s %s:%" PRIu32, prefix, name,
571  freerdp_settings_get_uint32(settings, FreeRDP_ServerPort));
572 
573  return windowTitle;
574 }
575 
576 BOOL xf_create_window(xfContext* xfc)
577 {
578  XGCValues gcv = { 0 };
579  XEvent xevent = { 0 };
580  char* windowTitle = NULL;
581 
582  WINPR_ASSERT(xfc);
583  rdpSettings* settings = xfc->common.context.settings;
584  WINPR_ASSERT(settings);
585 
586  int width =
587  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
588  int height =
589  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
590 
591  const XSetWindowAttributes empty = { 0 };
592  xfc->attribs = empty;
593 
594  if (xfc->remote_app)
595  xfc->depth = 32;
596  else
597  xfc->depth = DefaultDepthOfScreen(xfc->screen);
598 
599  XVisualInfo vinfo = { 0 };
600  if (XMatchVisualInfo(xfc->display, xfc->screen_number, xfc->depth, TrueColor, &vinfo))
601  {
602  Window root = XDefaultRootWindow(xfc->display);
603  xfc->visual = vinfo.visual;
604  xfc->attribs.colormap = xfc->colormap =
605  XCreateColormap(xfc->display, root, vinfo.visual, AllocNone);
606  }
607  else
608  {
609  if (xfc->remote_app)
610  {
611  WLog_WARN(TAG, "running in remote app mode, but XServer does not support transparency");
612  WLog_WARN(TAG, "display of remote applications might be distorted (black frames, ...)");
613  }
614  xfc->depth = DefaultDepthOfScreen(xfc->screen);
615  xfc->visual = DefaultVisual(xfc->display, xfc->screen_number);
616  xfc->attribs.colormap = xfc->colormap = DefaultColormap(xfc->display, xfc->screen_number);
617  }
618 
619  /*
620  * Detect if the server visual has an inverted colormap
621  * (BGR vs RGB, or red being the least significant byte)
622  */
623  if (vinfo.red_mask & 0xFF)
624  {
625  xfc->invert = FALSE;
626  }
627 
628  if (!xfc->remote_app)
629  {
630  xfc->attribs.background_pixel = BlackPixelOfScreen(xfc->screen);
631  xfc->attribs.border_pixel = WhitePixelOfScreen(xfc->screen);
632  xfc->attribs.backing_store = xfc->primary ? NotUseful : Always;
633  xfc->attribs.override_redirect = False;
634 
635  xfc->attribs.bit_gravity = NorthWestGravity;
636  xfc->attribs.win_gravity = NorthWestGravity;
637  xfc->attribs_mask = CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
638  CWBorderPixel | CWWinGravity | CWBitGravity;
639 
640 #ifdef WITH_XRENDER
641  xfc->offset_x = 0;
642  xfc->offset_y = 0;
643 #endif
644  windowTitle = xf_window_get_title(settings);
645 
646  if (!windowTitle)
647  return FALSE;
648 
649 #ifdef WITH_XRENDER
650 
651  if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) && !xfc->fullscreen)
652  {
653  if (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth) > 0)
654  width = WINPR_ASSERTING_INT_CAST(
655  int, freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth));
656 
657  if (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight) > 0)
658  height = WINPR_ASSERTING_INT_CAST(
659  int, freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight));
660 
661  xfc->scaledWidth = width;
662  xfc->scaledHeight = height;
663  }
664 
665 #endif
666  xfc->window = xf_CreateDesktopWindow(xfc, windowTitle, width, height);
667  free(windowTitle);
668 
669  if (xfc->fullscreen)
670  xf_SetWindowFullscreen(xfc, xfc->window, xfc->fullscreen);
671 
672  xfc->unobscured = (xevent.xvisibility.state == VisibilityUnobscured);
673  XSetWMProtocols(xfc->display, xfc->window->handle, &(xfc->WM_DELETE_WINDOW), 1);
674  xfc->drawable = xfc->window->handle;
675  }
676  else
677  {
678  xfc->attribs.border_pixel = 0;
679  xfc->attribs.background_pixel = 0;
680  xfc->attribs.backing_store = xfc->primary ? NotUseful : Always;
681  xfc->attribs.override_redirect = False;
682 
683  xfc->attribs.bit_gravity = NorthWestGravity;
684  xfc->attribs.win_gravity = NorthWestGravity;
685  xfc->attribs_mask = CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
686  CWBorderPixel | CWWinGravity | CWBitGravity;
687 
688  xfc->drawable = xf_CreateDummyWindow(xfc);
689  }
690 
691  if (!xfc->gc)
692  xfc->gc = XCreateGC(xfc->display, xfc->drawable, GCGraphicsExposures, &gcv);
693 
694  WINPR_ASSERT(xfc->depth != 0);
695  if (!xfc->primary)
696  xfc->primary = XCreatePixmap(xfc->display, xfc->drawable,
697  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
698  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
699  WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth));
700 
701  xfc->drawing = xfc->primary;
702 
703  if (!xfc->bitmap_mono)
704  xfc->bitmap_mono = XCreatePixmap(xfc->display, xfc->drawable, 8, 8, 1);
705 
706  if (!xfc->gc_mono)
707  xfc->gc_mono = XCreateGC(xfc->display, xfc->bitmap_mono, GCGraphicsExposures, &gcv);
708 
709  XSetFunction(xfc->display, xfc->gc, GXcopy);
710  XSetFillStyle(xfc->display, xfc->gc, FillSolid);
711  XSetForeground(xfc->display, xfc->gc, BlackPixelOfScreen(xfc->screen));
712  XFillRectangle(xfc->display, xfc->primary, xfc->gc, 0, 0,
713  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
714  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
715  XFlush(xfc->display);
716 
717  return TRUE;
718 }
719 
720 BOOL xf_create_image(xfContext* xfc)
721 {
722  WINPR_ASSERT(xfc);
723  if (!xfc->image)
724  {
725  const rdpSettings* settings = xfc->common.context.settings;
726  rdpGdi* cgdi = xfc->common.context.gdi;
727  WINPR_ASSERT(cgdi);
728 
729  WINPR_ASSERT(xfc->depth != 0);
730  xfc->image =
731  XCreateImage(xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth),
732  ZPixmap, 0, (char*)cgdi->primary_buffer,
733  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
734  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
735  xfc->scanline_pad, WINPR_ASSERTING_INT_CAST(int, cgdi->stride));
736  xfc->image->byte_order = LSBFirst;
737  xfc->image->bitmap_bit_order = LSBFirst;
738  }
739  return TRUE;
740 }
741 
742 static void xf_window_free(xfContext* xfc)
743 {
744  if (xfc->window)
745  {
746  xf_DestroyDesktopWindow(xfc, xfc->window);
747  xfc->window = NULL;
748  }
749 
750 #if defined(CHANNEL_TSMF_CLIENT)
751  if (xfc->xv_context)
752  {
753  xf_tsmf_uninit(xfc, NULL);
754  xfc->xv_context = NULL;
755  }
756 #endif
757 
758  if (xfc->image)
759  {
760  xfc->image->data = NULL;
761  XDestroyImage(xfc->image);
762  xfc->image = NULL;
763  }
764 
765  if (xfc->bitmap_mono)
766  {
767  XFreePixmap(xfc->display, xfc->bitmap_mono);
768  xfc->bitmap_mono = 0;
769  }
770 
771  if (xfc->gc_mono)
772  {
773  XFreeGC(xfc->display, xfc->gc_mono);
774  xfc->gc_mono = 0;
775  }
776 
777  if (xfc->primary)
778  {
779  XFreePixmap(xfc->display, xfc->primary);
780  xfc->primary = 0;
781  }
782 
783  if (xfc->gc)
784  {
785  XFreeGC(xfc->display, xfc->gc);
786  xfc->gc = 0;
787  }
788 }
789 
790 void xf_toggle_fullscreen(xfContext* xfc)
791 {
792  WindowStateChangeEventArgs e = { 0 };
793  rdpContext* context = (rdpContext*)xfc;
794  rdpSettings* settings = context->settings;
795 
796  /*
797  when debugging, ungrab keyboard when toggling fullscreen
798  to allow keyboard usage on the debugger
799  */
800  if (xfc->debug)
801  xf_ungrab(xfc);
802 
803  xfc->fullscreen = (xfc->fullscreen) ? FALSE : TRUE;
804  xfc->decorations =
805  (xfc->fullscreen) ? FALSE : freerdp_settings_get_bool(settings, FreeRDP_Decorations);
806  xf_SetWindowFullscreen(xfc, xfc->window, xfc->fullscreen);
807  EventArgsInit(&e, "xfreerdp");
808  e.state = xfc->fullscreen ? FREERDP_WINDOW_STATE_FULLSCREEN : 0;
809  PubSub_OnWindowStateChange(context->pubSub, context, &e);
810 }
811 
812 void xf_minimize(xfContext* xfc)
813 {
814  WindowStateChangeEventArgs e = { 0 };
815  rdpContext* context = (rdpContext*)xfc;
816  WINPR_ASSERT(context);
817 
818  /*
819  when debugging, ungrab keyboard when toggling fullscreen
820  to allow keyboard usage on the debugger
821  */
822  if (xfc->debug)
823  xf_ungrab(xfc);
824 
825  xf_SetWindowMinimized(xfc, xfc->window);
826 
827  e.state = xfc->fullscreen ? FREERDP_WINDOW_STATE_FULLSCREEN : 0;
828  PubSub_OnWindowStateChange(context->pubSub, context, &e);
829 }
830 
831 void xf_lock_x11_(xfContext* xfc, const char* fkt)
832 {
833 
834  if (!xfc->UseXThreads)
835  (void)WaitForSingleObject(xfc->mutex, INFINITE);
836  else
837  XLockDisplay(xfc->display);
838 
839  xfc->locked++;
840 #ifdef WITH_DEBUG_X11
841  WLog_VRB(TAG, "[%" PRIu32 "] from %s", xfc->locked, fkt);
842 #endif
843 }
844 
845 void xf_unlock_x11_(xfContext* xfc, const char* fkt)
846 {
847  if (xfc->locked == 0)
848  WLog_WARN(TAG, "X11: trying to unlock although not locked!");
849  else
850  xfc->locked--;
851 
852 #ifdef WITH_DEBUG_X11
853  WLog_VRB(TAG, "[%" PRIu32 "] from %s", xfc->locked, fkt);
854 #endif
855 
856  if (!xfc->UseXThreads)
857  (void)ReleaseMutex(xfc->mutex);
858  else
859  XUnlockDisplay(xfc->display);
860 }
861 
862 static BOOL xf_get_pixmap_info(xfContext* xfc)
863 {
864  int pf_count = 0;
865  XPixmapFormatValues* pfs = NULL;
866 
867  WINPR_ASSERT(xfc->display);
868  pfs = XListPixmapFormats(xfc->display, &pf_count);
869 
870  if (!pfs)
871  {
872  WLog_ERR(TAG, "XListPixmapFormats failed");
873  return 1;
874  }
875 
876  WINPR_ASSERT(xfc->depth != 0);
877  for (int i = 0; i < pf_count; i++)
878  {
879  const XPixmapFormatValues* pf = &pfs[i];
880 
881  if (pf->depth == xfc->depth)
882  {
883  xfc->scanline_pad = pf->scanline_pad;
884  break;
885  }
886  }
887 
888  XFree(pfs);
889  if ((xfc->visual == NULL) || (xfc->scanline_pad == 0))
890  return FALSE;
891 
892  return TRUE;
893 }
894 
895 static int xf_error_handler(Display* d, XErrorEvent* ev)
896 {
897  char buf[256] = { 0 };
898  XGetErrorText(d, ev->error_code, buf, sizeof(buf));
899  WLog_ERR(TAG, "%s", buf);
900  winpr_log_backtrace(TAG, WLOG_ERROR, 20);
901 
902 #if 0
903  const BOOL do_abort = TRUE;
904  if (do_abort)
905  abort();
906 #endif
907 
908  if (def_error_handler)
909  return def_error_handler(d, ev);
910 
911  return 0;
912 }
913 
914 static int xf_error_handler_ex(Display* d, XErrorEvent* ev)
915 {
916  /*
917  * ungrab the keyboard, in case a debugger is running in
918  * another window. This make xf_error_handler() a potential
919  * debugger breakpoint.
920  */
921 
922  XUngrabKeyboard(d, CurrentTime);
923  XUngrabPointer(d, CurrentTime);
924  return xf_error_handler(d, ev);
925 }
926 
927 static BOOL xf_play_sound(rdpContext* context, const PLAY_SOUND_UPDATE* play_sound)
928 {
929  xfContext* xfc = (xfContext*)context;
930  WINPR_UNUSED(play_sound);
931  XkbBell(xfc->display, None, 100, 0);
932  return TRUE;
933 }
934 
935 static void xf_check_extensions(xfContext* context)
936 {
937  int xkb_opcode = 0;
938  int xkb_event = 0;
939  int xkb_error = 0;
940  int xkb_major = XkbMajorVersion;
941  int xkb_minor = XkbMinorVersion;
942 
943  if (XkbLibraryVersion(&xkb_major, &xkb_minor) &&
944  XkbQueryExtension(context->display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major,
945  &xkb_minor))
946  {
947  context->xkbAvailable = TRUE;
948  }
949 
950 #ifdef WITH_XRENDER
951  {
952  int xrender_event_base = 0;
953  int xrender_error_base = 0;
954 
955  if (XRenderQueryExtension(context->display, &xrender_event_base, &xrender_error_base))
956  {
957  context->xrenderAvailable = TRUE;
958  }
959  }
960 #endif
961 }
962 
963 #ifdef WITH_XI
964 /* Input device which does NOT have the correct mapping. We must disregard */
965 /* this device when trying to find the input device which is the pointer. */
966 static const char TEST_PTR_STR[] = "Virtual core XTEST pointer";
967 static const size_t TEST_PTR_LEN = sizeof(TEST_PTR_STR) / sizeof(char);
968 #endif /* WITH_XI */
969 
970 static void xf_get_x11_button_map(xfContext* xfc, unsigned char* x11_map)
971 {
972 #ifdef WITH_XI
973  int opcode = 0;
974  int event = 0;
975  int error = 0;
976  XDevice* ptr_dev = NULL;
977  XExtensionVersion* version = NULL;
978  XDeviceInfo* devices1 = NULL;
979  XIDeviceInfo* devices2 = NULL;
980  int num_devices = 0;
981 
982  if (XQueryExtension(xfc->display, "XInputExtension", &opcode, &event, &error))
983  {
984  WLog_DBG(TAG, "Searching for XInput pointer device");
985  ptr_dev = NULL;
986  /* loop through every device, looking for a pointer */
987  version = XGetExtensionVersion(xfc->display, INAME);
988 
989  if (version->major_version >= 2)
990  {
991  /* XID of pointer device using XInput version 2 */
992  devices2 = XIQueryDevice(xfc->display, XIAllDevices, &num_devices);
993 
994  if (devices2)
995  {
996  for (int i = 0; i < num_devices; ++i)
997  {
998  XIDeviceInfo* dev = &devices2[i];
999  if ((dev->use == XISlavePointer) &&
1000  (strncmp(dev->name, TEST_PTR_STR, TEST_PTR_LEN) != 0))
1001  {
1002  ptr_dev = XOpenDevice(xfc->display,
1003  WINPR_ASSERTING_INT_CAST(uint32_t, dev->deviceid));
1004  if (ptr_dev)
1005  break;
1006  }
1007  }
1008 
1009  XIFreeDeviceInfo(devices2);
1010  }
1011  }
1012  else
1013  {
1014  /* XID of pointer device using XInput version 1 */
1015  devices1 = XListInputDevices(xfc->display, &num_devices);
1016 
1017  if (devices1)
1018  {
1019  for (int i = 0; i < num_devices; ++i)
1020  {
1021  if ((devices1[i].use == IsXExtensionPointer) &&
1022  (strncmp(devices1[i].name, TEST_PTR_STR, TEST_PTR_LEN) != 0))
1023  {
1024  ptr_dev = XOpenDevice(xfc->display, devices1[i].id);
1025  if (ptr_dev)
1026  break;
1027  }
1028  }
1029 
1030  XFreeDeviceList(devices1);
1031  }
1032  }
1033 
1034  XFree(version);
1035 
1036  /* get button mapping from input extension if there is a pointer device; */
1037  /* otherwise leave unchanged. */
1038  if (ptr_dev)
1039  {
1040  WLog_DBG(TAG, "Pointer device: %d", ptr_dev->device_id);
1041  XGetDeviceButtonMapping(xfc->display, ptr_dev, x11_map, NUM_BUTTONS_MAPPED);
1042  XCloseDevice(xfc->display, ptr_dev);
1043  }
1044  else
1045  {
1046  WLog_DBG(TAG, "No pointer device found!");
1047  }
1048  }
1049  else
1050 #endif /* WITH_XI */
1051  {
1052  WLog_DBG(TAG, "Get global pointer mapping (no XInput)");
1053  XGetPointerMapping(xfc->display, x11_map, NUM_BUTTONS_MAPPED);
1054  }
1055 }
1056 
1057 /* Assignment of physical (not logical) mouse buttons to wire flags. */
1058 /* Notice that the middle button is 2 in X11, but 3 in RDP. */
1059 static const button_map xf_button_flags[NUM_BUTTONS_MAPPED] = {
1060  { Button1, PTR_FLAGS_BUTTON1 },
1061  { Button2, PTR_FLAGS_BUTTON3 },
1062  { Button3, PTR_FLAGS_BUTTON2 },
1063  { Button4, PTR_FLAGS_WHEEL | 0x78 },
1064  /* Negative value is 9bit twos complement */
1065  { Button5, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | (0x100 - 0x78) },
1066  { 6, PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE | (0x100 - 0x78) },
1067  { 7, PTR_FLAGS_HWHEEL | 0x78 },
1068  { 8, PTR_XFLAGS_BUTTON1 },
1069  { 9, PTR_XFLAGS_BUTTON2 },
1070  { 97, PTR_XFLAGS_BUTTON1 },
1071  { 112, PTR_XFLAGS_BUTTON2 }
1072 };
1073 
1074 static UINT16 get_flags_for_button(size_t button)
1075 {
1076  for (size_t x = 0; x < ARRAYSIZE(xf_button_flags); x++)
1077  {
1078  const button_map* map = &xf_button_flags[x];
1079 
1080  if (map->button == button)
1081  return map->flags;
1082  }
1083 
1084  return 0;
1085 }
1086 
1087 static void xf_button_map_init(xfContext* xfc)
1088 {
1089  size_t pos = 0;
1090  /* loop counter for array initialization */
1091 
1092  /* logical mouse button which is used for each physical mouse */
1093  /* button (indexed from zero). This is the default map. */
1094  unsigned char x11_map[112] = { 0 };
1095 
1096  WINPR_ASSERT(xfc);
1097  WINPR_ASSERT(xfc->common.context.settings);
1098 
1099  x11_map[0] = Button1;
1100  x11_map[1] = Button2;
1101  x11_map[2] = Button3;
1102  x11_map[3] = Button4;
1103  x11_map[4] = Button5;
1104  x11_map[5] = 6;
1105  x11_map[6] = 7;
1106  x11_map[7] = 8;
1107  x11_map[8] = 9;
1108  x11_map[96] = 97;
1109  x11_map[111] = 112;
1110 
1111  /* query system for actual remapping */
1112  if (freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_UnmapButtons))
1113  {
1114  xf_get_x11_button_map(xfc, x11_map);
1115  }
1116 
1117  /* iterate over all (mapped) physical buttons; for each of them */
1118  /* find the logical button in X11, and assign to this the */
1119  /* appropriate value to send over the RDP wire. */
1120  for (size_t physical = 0; physical < ARRAYSIZE(x11_map); ++physical)
1121  {
1122  const unsigned char logical = x11_map[physical];
1123  const UINT16 flags = get_flags_for_button(logical);
1124 
1125  if ((logical != 0) && (flags != 0))
1126  {
1127  if (pos >= NUM_BUTTONS_MAPPED)
1128  {
1129  WLog_ERR(TAG, "Failed to map mouse button to RDP button, no space");
1130  }
1131  else
1132  {
1133  button_map* map = &xfc->button_map[pos++];
1134  map->button = logical;
1135  map->flags = get_flags_for_button(physical + Button1);
1136  }
1137  }
1138  }
1139 }
1140 
1152 static BOOL xf_pre_connect(freerdp* instance)
1153 {
1154  rdpChannels* channels = NULL;
1155  rdpSettings* settings = NULL;
1156  rdpContext* context = NULL;
1157  xfContext* xfc = NULL;
1158  UINT32 maxWidth = 0;
1159  UINT32 maxHeight = 0;
1160 
1161  WINPR_ASSERT(instance);
1162 
1163  context = instance->context;
1164  xfc = (xfContext*)instance->context;
1165  WINPR_ASSERT(context);
1166 
1167  settings = context->settings;
1168  WINPR_ASSERT(settings);
1169 
1170  if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
1171  return FALSE;
1172 
1173  if (!freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1174  {
1175  if (!xf_setup_x11(xfc))
1176  return FALSE;
1177  }
1178 
1179  channels = context->channels;
1180  WINPR_ASSERT(channels);
1181 
1182  if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX))
1183  return FALSE;
1184  if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_NATIVE_XSERVER))
1185  return FALSE;
1186  PubSub_SubscribeChannelConnected(context->pubSub, xf_OnChannelConnectedEventHandler);
1187  PubSub_SubscribeChannelDisconnected(context->pubSub, xf_OnChannelDisconnectedEventHandler);
1188 
1189  if (!freerdp_settings_get_string(settings, FreeRDP_Username) &&
1190  !freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin) &&
1191  !freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon))
1192  {
1193  char login_name[MAX_PATH] = { 0 };
1194  ULONG size = sizeof(login_name) - 1;
1195 
1196  if (GetUserNameExA(NameSamCompatible, login_name, &size))
1197  {
1198  if (!freerdp_settings_set_string(settings, FreeRDP_Username, login_name))
1199  return FALSE;
1200 
1201  WLog_INFO(TAG, "No user name set. - Using login name: %s",
1202  freerdp_settings_get_string(settings, FreeRDP_Username));
1203  }
1204  }
1205 
1206  if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1207  {
1208  /* Check +auth-only has a username and password. */
1209  if (!freerdp_settings_get_string(settings, FreeRDP_Password))
1210  {
1211  WLog_INFO(TAG, "auth-only, but no password set. Please provide one.");
1212  return FALSE;
1213  }
1214 
1215  WLog_INFO(TAG, "Authentication only. Don't connect to X.");
1216  }
1217 
1218  if (!freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1219  {
1220  if (!xf_keyboard_init(xfc))
1221  return FALSE;
1222  if (!xf_detect_monitors(xfc, &maxWidth, &maxHeight))
1223  return FALSE;
1224  }
1225 
1226  if (maxWidth && maxHeight && !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
1227  {
1228  if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, maxWidth))
1229  return FALSE;
1230  if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, maxHeight))
1231  return FALSE;
1232  }
1233 
1234 #ifdef WITH_XRENDER
1235 
1240  if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
1241  freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) &&
1242  (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth) > 0) &&
1243  (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight) > 0))
1244  {
1246  settings, FreeRDP_DesktopWidth,
1247  freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth)))
1248  return FALSE;
1250  settings, FreeRDP_DesktopHeight,
1251  freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight)))
1252  return FALSE;
1253  }
1254 
1255 #endif
1256  xfc->fullscreen = freerdp_settings_get_bool(settings, FreeRDP_Fullscreen);
1257  xfc->decorations = freerdp_settings_get_bool(settings, FreeRDP_Decorations);
1258  xfc->grab_keyboard = freerdp_settings_get_bool(settings, FreeRDP_GrabKeyboard);
1259  xfc->fullscreen_toggle = freerdp_settings_get_bool(settings, FreeRDP_ToggleFullscreen);
1260  if (!freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1261  xf_button_map_init(xfc);
1262  return TRUE;
1263 }
1264 
1265 static BOOL xf_inject_keypress(rdpContext* context, const char* buffer, size_t size)
1266 {
1267  WCHAR wbuffer[64] = { 0 };
1268  const SSIZE_T len = ConvertUtf8NToWChar(buffer, size, wbuffer, ARRAYSIZE(wbuffer));
1269  if (len < 0)
1270  return FALSE;
1271 
1272  rdpInput* input = context->input;
1273  WINPR_ASSERT(input);
1274 
1275  for (SSIZE_T x = 0; x < len; x++)
1276  {
1277  const WCHAR code = wbuffer[x];
1278  freerdp_input_send_unicode_keyboard_event(input, 0, code);
1279  Sleep(5);
1280  freerdp_input_send_unicode_keyboard_event(input, KBD_FLAGS_RELEASE, code);
1281  Sleep(5);
1282  }
1283  return TRUE;
1284 }
1285 
1286 static BOOL xf_process_pipe(rdpContext* context, const char* pipe)
1287 {
1288  int fd = open(pipe, O_NONBLOCK | O_RDONLY);
1289  if (fd < 0)
1290  {
1291  char ebuffer[256] = { 0 };
1292  WLog_ERR(TAG, "pipe '%s' open returned %s [%d]", pipe,
1293  winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1294  return FALSE;
1295  }
1296  while (!freerdp_shall_disconnect_context(context))
1297  {
1298  char buffer[64] = { 0 };
1299  ssize_t rd = read(fd, buffer, sizeof(buffer) - 1);
1300  if (rd == 0)
1301  {
1302  char ebuffer[256] = { 0 };
1303  if ((errno == EAGAIN) || (errno == 0))
1304  {
1305  Sleep(100);
1306  continue;
1307  }
1308 
1309  // EOF, abort reading.
1310  WLog_ERR(TAG, "pipe '%s' read returned %s [%d]", pipe,
1311  winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1312  break;
1313  }
1314  else if (rd < 0)
1315  {
1316  char ebuffer[256] = { 0 };
1317  WLog_ERR(TAG, "pipe '%s' read returned %s [%d]", pipe,
1318  winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1319  break;
1320  }
1321  else
1322  {
1323  if (!xf_inject_keypress(context, buffer, WINPR_ASSERTING_INT_CAST(size_t, rd)))
1324  break;
1325  }
1326  }
1327  close(fd);
1328  return TRUE;
1329 }
1330 
1331 static void cleanup_pipe(int signum, const char* signame, void* context)
1332 {
1333  const char* pipe = context;
1334  if (!pipe)
1335  return;
1336  unlink(pipe);
1337 }
1338 
1339 static DWORD WINAPI xf_handle_pipe(void* arg)
1340 {
1341  xfContext* xfc = arg;
1342  WINPR_ASSERT(xfc);
1343 
1344  rdpContext* context = &xfc->common.context;
1345  WINPR_ASSERT(context);
1346 
1347  rdpSettings* settings = context->settings;
1348  WINPR_ASSERT(settings);
1349 
1350  const char* pipe = freerdp_settings_get_string(settings, FreeRDP_KeyboardPipeName);
1351  WINPR_ASSERT(pipe);
1352 
1353  const int rc = mkfifo(pipe, S_IWUSR | S_IRUSR);
1354  if (rc != 0)
1355  {
1356  char ebuffer[256] = { 0 };
1357  WLog_ERR(TAG, "Failed to create named pipe '%s': %s [%d]", pipe,
1358  winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1359  return 0;
1360  }
1361 
1362  void* ctx = WINPR_CAST_CONST_PTR_AWAY(pipe, void*);
1363  freerdp_add_signal_cleanup_handler(ctx, cleanup_pipe);
1364 
1365  xf_process_pipe(context, pipe);
1366 
1367  freerdp_del_signal_cleanup_handler(ctx, cleanup_pipe);
1368 
1369  unlink(pipe);
1370  return 0;
1371 }
1372 
1378 static BOOL xf_post_connect(freerdp* instance)
1379 {
1380  ResizeWindowEventArgs e = { 0 };
1381 
1382  WINPR_ASSERT(instance);
1383  xfContext* xfc = (xfContext*)instance->context;
1384  rdpContext* context = instance->context;
1385  WINPR_ASSERT(context);
1386 
1387  rdpSettings* settings = context->settings;
1388  WINPR_ASSERT(settings);
1389 
1390  rdpUpdate* update = context->update;
1391  WINPR_ASSERT(update);
1392 
1393  if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode))
1394  xfc->remote_app = TRUE;
1395 
1396  if (!xf_create_window(xfc))
1397  return FALSE;
1398 
1399  if (!xf_get_pixmap_info(xfc))
1400  return FALSE;
1401 
1402  if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
1403  return FALSE;
1404 
1405  if (!xf_create_image(xfc))
1406  return FALSE;
1407 
1408  if (!xf_register_pointer(context->graphics))
1409  return FALSE;
1410 
1411 #ifdef WITH_XRENDER
1412  xfc->scaledWidth =
1413  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
1414  xfc->scaledHeight =
1415  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
1416  xfc->offset_x = 0;
1417  xfc->offset_y = 0;
1418 #endif
1419 
1420  if (!xfc->xrenderAvailable)
1421  {
1422  if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
1423  {
1424  WLog_ERR(TAG, "XRender not available: disabling smart-sizing");
1425  if (!freerdp_settings_set_bool(settings, FreeRDP_SmartSizing, FALSE))
1426  return FALSE;
1427  }
1428 
1429  if (freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
1430  {
1431  WLog_ERR(TAG, "XRender not available: disabling local multi-touch gestures");
1432  if (!freerdp_settings_set_bool(settings, FreeRDP_MultiTouchGestures, FALSE))
1433  return FALSE;
1434  }
1435  }
1436 
1437  update->DesktopResize = xf_sw_desktop_resize;
1438  update->EndPaint = xf_end_paint;
1439  update->PlaySound = xf_play_sound;
1440  update->SetKeyboardIndicators = xf_keyboard_set_indicators;
1441  update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
1442 
1443  const BOOL serverIsWindowsPlatform =
1444  (freerdp_settings_get_uint32(settings, FreeRDP_OsMajorType) == OSMAJORTYPE_WINDOWS);
1445  if (freerdp_settings_get_bool(settings, FreeRDP_RedirectClipboard) &&
1446  !(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform)))
1447  return FALSE;
1448 
1449  if (!(xfc->xfDisp = xf_disp_new(xfc)))
1450  return FALSE;
1451 
1452  const char* pipe = freerdp_settings_get_string(settings, FreeRDP_KeyboardPipeName);
1453  if (pipe)
1454  {
1455  xfc->pipethread = CreateThread(NULL, 0, xf_handle_pipe, xfc, 0, NULL);
1456  if (!xfc->pipethread)
1457  return FALSE;
1458  }
1459 
1460  EventArgsInit(&e, "xfreerdp");
1461  e.width =
1462  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
1463  e.height =
1464  WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
1465  PubSub_OnResizeWindow(context->pubSub, xfc, &e);
1466  return TRUE;
1467 }
1468 
1469 static void xf_post_disconnect(freerdp* instance)
1470 {
1471  xfContext* xfc = NULL;
1472  rdpContext* context = NULL;
1473 
1474  if (!instance || !instance->context)
1475  return;
1476 
1477  context = instance->context;
1478  xfc = (xfContext*)context;
1479  PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
1480  xf_OnChannelConnectedEventHandler);
1481  PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
1482  xf_OnChannelDisconnectedEventHandler);
1483  gdi_free(instance);
1484 
1485  if (xfc->pipethread)
1486  {
1487  (void)WaitForSingleObject(xfc->pipethread, INFINITE);
1488  (void)CloseHandle(xfc->pipethread);
1489  xfc->pipethread = NULL;
1490  }
1491  if (xfc->clipboard)
1492  {
1493  xf_clipboard_free(xfc->clipboard);
1494  xfc->clipboard = NULL;
1495  }
1496 
1497  if (xfc->xfDisp)
1498  {
1499  xf_disp_free(xfc->xfDisp);
1500  xfc->xfDisp = NULL;
1501  }
1502 
1503  if ((xfc->window != NULL) && (xfc->drawable == xfc->window->handle))
1504  xfc->drawable = 0;
1505  else
1506  xf_DestroyDummyWindow(xfc, xfc->drawable);
1507 
1508  xf_window_free(xfc);
1509 }
1510 
1511 static void xf_post_final_disconnect(freerdp* instance)
1512 {
1513  xfContext* xfc = NULL;
1514  rdpContext* context = NULL;
1515 
1516  if (!instance || !instance->context)
1517  return;
1518 
1519  context = instance->context;
1520  xfc = (xfContext*)context;
1521 
1522  xf_keyboard_free(xfc);
1523  xf_teardown_x11(xfc);
1524 }
1525 
1526 static int xf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
1527 {
1528  xfContext* xfc = (xfContext*)instance->context;
1529  const char* str_data = freerdp_get_logon_error_info_data(data);
1530  const char* str_type = freerdp_get_logon_error_info_type(type);
1531  WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
1532  if (type != LOGON_MSG_SESSION_CONTINUE)
1533  {
1534  xf_rail_disable_remoteapp_mode(xfc);
1535  }
1536  return 1;
1537 }
1538 
1539 static BOOL handle_window_events(freerdp* instance)
1540 {
1541  if (!xf_process_x_events(instance))
1542  {
1543  WLog_DBG(TAG, "Closed from X11");
1544  return FALSE;
1545  }
1546 
1547  return TRUE;
1548 }
1549 
1558 static DWORD WINAPI xf_client_thread(LPVOID param)
1559 {
1560  DWORD exit_code = 0;
1561  DWORD waitStatus = 0;
1562  HANDLE inputEvent = NULL;
1563  HANDLE timer = NULL;
1564  LARGE_INTEGER due = { 0 };
1565  TimerEventArgs timerEvent = { 0 };
1566 
1567  EventArgsInit(&timerEvent, "xfreerdp");
1568  freerdp* instance = (freerdp*)param;
1569  WINPR_ASSERT(instance);
1570 
1571  const BOOL status = freerdp_connect(instance);
1572  rdpContext* context = instance->context;
1573  WINPR_ASSERT(context);
1574  xfContext* xfc = (xfContext*)instance->context;
1575  WINPR_ASSERT(xfc);
1576 
1577  rdpSettings* settings = context->settings;
1578  WINPR_ASSERT(settings);
1579 
1580  if (!status)
1581  {
1582  UINT32 error = freerdp_get_last_error(instance->context);
1583  exit_code = (uint32_t)xf_map_error_to_exit_code(error);
1584  }
1585  else
1586  exit_code = XF_EXIT_SUCCESS;
1587 
1588  if (!status)
1589  goto end;
1590 
1591  /* --authonly ? */
1592  if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1593  {
1594  WLog_ERR(TAG, "Authentication only, exit status %" PRId32 "", !status);
1595  goto disconnect;
1596  }
1597 
1598  if (!status)
1599  {
1600  WLog_ERR(TAG, "Freerdp connect error exit status %" PRId32 "", !status);
1601  exit_code = freerdp_error_info(instance);
1602 
1603  if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_AUTHENTICATION_FAILED)
1604  exit_code = XF_EXIT_AUTH_FAILURE;
1605  else if (exit_code == ERRINFO_SUCCESS)
1606  exit_code = XF_EXIT_CONN_FAILED;
1607 
1608  goto disconnect;
1609  }
1610 
1611  timer = CreateWaitableTimerA(NULL, FALSE, "mainloop-periodic-timer");
1612 
1613  if (!timer)
1614  {
1615  WLog_ERR(TAG, "failed to create timer");
1616  goto disconnect;
1617  }
1618 
1619  due.QuadPart = 0;
1620 
1621  if (!SetWaitableTimer(timer, &due, 20, NULL, NULL, FALSE))
1622  {
1623  goto disconnect;
1624  }
1625  inputEvent = xfc->x11event;
1626 
1627  while (!freerdp_shall_disconnect_context(instance->context))
1628  {
1629  HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
1630  DWORD nCount = 0;
1631  handles[nCount++] = timer;
1632  handles[nCount++] = inputEvent;
1633 
1634  /*
1635  * win8 and server 2k12 seem to have some timing issue/race condition
1636  * when a initial sync request is send to sync the keyboard indicators
1637  * sending the sync event twice fixed this problem
1638  */
1639  if (freerdp_focus_required(instance))
1640  {
1641  xf_keyboard_focus_in(xfc);
1642  xf_keyboard_focus_in(xfc);
1643  }
1644 
1645  {
1646  DWORD tmp =
1647  freerdp_get_event_handles(context, &handles[nCount], ARRAYSIZE(handles) - nCount);
1648 
1649  if (tmp == 0)
1650  {
1651  WLog_ERR(TAG, "freerdp_get_event_handles failed");
1652  break;
1653  }
1654 
1655  nCount += tmp;
1656  }
1657 
1658  if (xfc->window)
1659  xf_floatbar_hide_and_show(xfc->window->floatbar);
1660 
1661  waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
1662 
1663  if (waitStatus == WAIT_FAILED)
1664  break;
1665 
1666  {
1667  if (!freerdp_check_event_handles(context))
1668  {
1669  if (client_auto_reconnect_ex(instance, handle_window_events))
1670  continue;
1671  else
1672  {
1673  /*
1674  * Indicate an unsuccessful connection attempt if reconnect
1675  * did not succeed and no other error was specified.
1676  */
1677  const UINT32 error = freerdp_get_last_error(instance->context);
1678 
1679  if (freerdp_error_info(instance) == 0)
1680  exit_code = (uint32_t)xf_map_error_to_exit_code(error);
1681  }
1682 
1683  if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS)
1684  WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
1685 
1686  break;
1687  }
1688  }
1689 
1690  if (!handle_window_events(instance))
1691  break;
1692 
1693  if ((waitStatus != WAIT_TIMEOUT) && (waitStatus == WAIT_OBJECT_0))
1694  {
1695  timerEvent.now = GetTickCount64();
1696  PubSub_OnTimer(context->pubSub, context, &timerEvent);
1697  }
1698  }
1699 
1700  if (!exit_code)
1701  {
1702  exit_code = freerdp_error_info(instance);
1703 
1704  if (exit_code == XF_EXIT_DISCONNECT &&
1705  freerdp_get_disconnect_ultimatum(context) == Disconnect_Ultimatum_user_requested)
1706  {
1707  /* This situation might be limited to Windows XP. */
1708  WLog_INFO(TAG, "Error info says user did not initiate but disconnect ultimatum says "
1709  "they did; treat this as a user logoff");
1710  exit_code = XF_EXIT_LOGOFF;
1711  }
1712  }
1713 
1714 disconnect:
1715 
1716  if (timer)
1717  (void)CloseHandle(timer);
1718 
1719  freerdp_disconnect(instance);
1720 end:
1721  ExitThread(exit_code);
1722  return exit_code;
1723 }
1724 
1725 int xf_exit_code_from_disconnect_reason(DWORD reason)
1726 {
1727  if (reason == 0 ||
1728  (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS))
1729  return WINPR_ASSERTING_INT_CAST(int, reason);
1730  /* License error set */
1731  else if (reason >= 0x100 && reason <= 0x10A)
1732  reason -= 0x100 + XF_EXIT_LICENSE_INTERNAL;
1733  /* RDP protocol error set */
1734  else if (reason >= 0x10c9 && reason <= 0x1193)
1735  reason = XF_EXIT_RDP;
1736  /* There's no need to test protocol-independent codes: they match */
1737  else if (!(reason <= 0xC))
1738  reason = XF_EXIT_UNKNOWN;
1739 
1740  return WINPR_ASSERTING_INT_CAST(int, reason);
1741 }
1742 
1743 static void xf_TerminateEventHandler(void* context, const TerminateEventArgs* e)
1744 {
1745  rdpContext* ctx = (rdpContext*)context;
1746  WINPR_UNUSED(e);
1747  freerdp_abort_connect_context(ctx);
1748 }
1749 
1750 #ifdef WITH_XRENDER
1751 static void xf_ZoomingChangeEventHandler(void* context, const ZoomingChangeEventArgs* e)
1752 {
1753  int w = 0;
1754  int h = 0;
1755  rdpSettings* settings = NULL;
1756  xfContext* xfc = (xfContext*)context;
1757 
1758  WINPR_ASSERT(xfc);
1759 
1760  settings = xfc->common.context.settings;
1761  WINPR_ASSERT(settings);
1762 
1763  w = xfc->scaledWidth + e->dx;
1764  h = xfc->scaledHeight + e->dy;
1765 
1766  if (e->dx == 0 && e->dy == 0)
1767  return;
1768 
1769  if (w < 10)
1770  w = 10;
1771 
1772  if (h < 10)
1773  h = 10;
1774 
1775  if (w == xfc->scaledWidth && h == xfc->scaledHeight)
1776  return;
1777 
1778  xfc->scaledWidth = w;
1779  xfc->scaledHeight = h;
1780  xf_draw_screen(xfc, 0, 0,
1781  WINPR_ASSERTING_INT_CAST(
1782  int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)),
1783  WINPR_ASSERTING_INT_CAST(
1784  int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)));
1785 }
1786 
1787 static void xf_PanningChangeEventHandler(void* context, const PanningChangeEventArgs* e)
1788 {
1789  xfContext* xfc = (xfContext*)context;
1790  rdpSettings* settings = NULL;
1791 
1792  WINPR_ASSERT(xfc);
1793  WINPR_ASSERT(e);
1794 
1795  settings = xfc->common.context.settings;
1796  WINPR_ASSERT(settings);
1797 
1798  if (e->dx == 0 && e->dy == 0)
1799  return;
1800 
1801  xfc->offset_x += e->dx;
1802  xfc->offset_y += e->dy;
1803  xf_draw_screen(xfc, 0, 0,
1804  WINPR_ASSERTING_INT_CAST(
1805  int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)),
1806  WINPR_ASSERTING_INT_CAST(
1807  int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)));
1808 }
1809 #endif
1810 
1815 static BOOL xfreerdp_client_global_init(void)
1816 {
1817  (void)setlocale(LC_ALL, "");
1818 
1819  if (freerdp_handle_signals() != 0)
1820  return FALSE;
1821 
1822  return TRUE;
1823 }
1824 
1825 static void xfreerdp_client_global_uninit(void)
1826 {
1827 }
1828 
1829 static int xfreerdp_client_start(rdpContext* context)
1830 {
1831  xfContext* xfc = (xfContext*)context;
1832  rdpSettings* settings = context->settings;
1833 
1834  if (!freerdp_settings_get_string(settings, FreeRDP_ServerHostname))
1835  {
1836  WLog_ERR(TAG, "error: server hostname was not specified with /v:<server>[:port]");
1837  return -1;
1838  }
1839 
1840  if (!(xfc->common.thread = CreateThread(NULL, 0, xf_client_thread, context->instance, 0, NULL)))
1841  {
1842  WLog_ERR(TAG, "failed to create client thread");
1843  return -1;
1844  }
1845 
1846  return 0;
1847 }
1848 
1849 static Atom get_supported_atom(xfContext* xfc, const char* atomName)
1850 {
1851  const Atom atom = Logging_XInternAtom(xfc->log, xfc->display, atomName, False);
1852 
1853  for (unsigned long i = 0; i < xfc->supportedAtomCount; i++)
1854  {
1855  if (xfc->supportedAtoms[i] == atom)
1856  return atom;
1857  }
1858 
1859  return None;
1860 }
1861 
1862 void xf_teardown_x11(xfContext* xfc)
1863 {
1864  WINPR_ASSERT(xfc);
1865 
1866  if (xfc->display)
1867  {
1868  XCloseDisplay(xfc->display);
1869  xfc->display = NULL;
1870  }
1871 
1872  if (xfc->x11event)
1873  {
1874  (void)CloseHandle(xfc->x11event);
1875  xfc->x11event = NULL;
1876  }
1877 
1878  if (xfc->mutex)
1879  {
1880  (void)CloseHandle(xfc->mutex);
1881  xfc->mutex = NULL;
1882  }
1883 
1884  if (xfc->vscreen.monitors)
1885  {
1886  free(xfc->vscreen.monitors);
1887  xfc->vscreen.monitors = NULL;
1888  }
1889  xfc->vscreen.nmonitors = 0;
1890 
1891  free(xfc->supportedAtoms);
1892  xfc->supportedAtoms = NULL;
1893  xfc->supportedAtomCount = 0;
1894 }
1895 
1896 BOOL xf_setup_x11(xfContext* xfc)
1897 {
1898 
1899  WINPR_ASSERT(xfc);
1900  xfc->UseXThreads = TRUE;
1901 
1902 #if !defined(NDEBUG)
1903  /* uncomment below if debugging to prevent keyboard grab */
1904  xfc->debug = TRUE;
1905 #endif
1906 
1907  if (xfc->UseXThreads)
1908  {
1909  if (!XInitThreads())
1910  {
1911  WLog_WARN(TAG, "XInitThreads() failure");
1912  xfc->UseXThreads = FALSE;
1913  }
1914  }
1915 
1916  xfc->display = XOpenDisplay(NULL);
1917 
1918  if (!xfc->display)
1919  {
1920  WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
1921  WLog_ERR(TAG, "Please check that the $DISPLAY environment variable is properly set.");
1922  goto fail;
1923  }
1924  if (xfc->debug)
1925  {
1926  WLog_INFO(TAG, "Enabling X11 debug mode.");
1927  XSynchronize(xfc->display, TRUE);
1928  }
1929  def_error_handler = XSetErrorHandler(xf_error_handler_ex);
1930 
1931  xfc->mutex = CreateMutex(NULL, FALSE, NULL);
1932 
1933  if (!xfc->mutex)
1934  {
1935  WLog_ERR(TAG, "Could not create mutex!");
1936  goto fail;
1937  }
1938 
1939  xfc->xfds = ConnectionNumber(xfc->display);
1940  xfc->screen_number = DefaultScreen(xfc->display);
1941  xfc->screen = ScreenOfDisplay(xfc->display, xfc->screen_number);
1942  xfc->big_endian = (ImageByteOrder(xfc->display) == MSBFirst);
1943  xfc->invert = TRUE;
1944  xfc->complex_regions = TRUE;
1945  xfc->_NET_SUPPORTED = Logging_XInternAtom(xfc->log, xfc->display, "_NET_SUPPORTED", True);
1946  xfc->_NET_SUPPORTING_WM_CHECK =
1947  Logging_XInternAtom(xfc->log, xfc->display, "_NET_SUPPORTING_WM_CHECK", True);
1948 
1949  if ((xfc->_NET_SUPPORTED != None) && (xfc->_NET_SUPPORTING_WM_CHECK != None))
1950  {
1951  Atom actual_type = 0;
1952  int actual_format = 0;
1953  unsigned long nitems = 0;
1954  unsigned long after = 0;
1955  unsigned char* data = NULL;
1956  int status = LogTagAndXGetWindowProperty(
1957  TAG, xfc->display, RootWindowOfScreen(xfc->screen), xfc->_NET_SUPPORTED, 0, 1024, False,
1958  XA_ATOM, &actual_type, &actual_format, &nitems, &after, &data);
1959 
1960  if ((status == Success) && (actual_type == XA_ATOM) && (actual_format == 32))
1961  {
1962  xfc->supportedAtomCount = nitems;
1963  xfc->supportedAtoms = calloc(xfc->supportedAtomCount, sizeof(Atom));
1964  WINPR_ASSERT(xfc->supportedAtoms);
1965  memcpy(xfc->supportedAtoms, data, nitems * sizeof(Atom));
1966  }
1967 
1968  if (data)
1969  XFree(data);
1970  }
1971 
1972  xfc->_XWAYLAND_MAY_GRAB_KEYBOARD =
1973  Logging_XInternAtom(xfc->log, xfc->display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False);
1974  xfc->_NET_WM_ICON = Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ICON", False);
1975  xfc->_MOTIF_WM_HINTS = Logging_XInternAtom(xfc->log, xfc->display, "_MOTIF_WM_HINTS", False);
1976  xfc->_NET_NUMBER_OF_DESKTOPS =
1977  Logging_XInternAtom(xfc->log, xfc->display, "_NET_NUMBER_OF_DESKTOPS", False);
1978  xfc->_NET_CURRENT_DESKTOP =
1979  Logging_XInternAtom(xfc->log, xfc->display, "_NET_CURRENT_DESKTOP", False);
1980  xfc->_NET_WORKAREA = Logging_XInternAtom(xfc->log, xfc->display, "_NET_WORKAREA", False);
1981  xfc->_NET_WM_STATE = get_supported_atom(xfc, "_NET_WM_STATE");
1982  xfc->_NET_WM_STATE_MODAL = get_supported_atom(xfc, "_NET_WM_STATE_MODAL");
1983  xfc->_NET_WM_STATE_STICKY = get_supported_atom(xfc, "_NET_WM_STATE_STICKY");
1984  xfc->_NET_WM_STATE_MAXIMIZED_HORZ =
1985  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
1986  xfc->_NET_WM_STATE_MAXIMIZED_VERT =
1987  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
1988  xfc->_NET_WM_STATE_SHADED = get_supported_atom(xfc, "_NET_WM_STATE_SHADED");
1989  xfc->_NET_WM_STATE_SKIP_TASKBAR = get_supported_atom(xfc, "_NET_WM_STATE_SKIP_TASKBAR");
1990  xfc->_NET_WM_STATE_SKIP_PAGER = get_supported_atom(xfc, "_NET_WM_STATE_SKIP_PAGER");
1991  xfc->_NET_WM_STATE_HIDDEN = get_supported_atom(xfc, "_NET_WM_STATE_HIDDEN");
1992  xfc->_NET_WM_STATE_FULLSCREEN = get_supported_atom(xfc, "_NET_WM_STATE_FULLSCREEN");
1993  xfc->_NET_WM_STATE_ABOVE = get_supported_atom(xfc, "_NET_WM_STATE_ABOVE");
1994  xfc->_NET_WM_STATE_BELOW = get_supported_atom(xfc, "_NET_WM_STATE_BELOW");
1995  xfc->_NET_WM_STATE_DEMANDS_ATTENTION =
1996  get_supported_atom(xfc, "_NET_WM_STATE_DEMANDS_ATTENTION");
1997  xfc->_NET_WM_FULLSCREEN_MONITORS = get_supported_atom(xfc, "_NET_WM_FULLSCREEN_MONITORS");
1998  xfc->_NET_WM_NAME = Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_NAME", False);
1999  xfc->_NET_WM_PID = Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_PID", False);
2000  xfc->_NET_WM_WINDOW_TYPE =
2001  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE", False);
2002  xfc->_NET_WM_WINDOW_TYPE_NORMAL =
2003  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
2004  xfc->_NET_WM_WINDOW_TYPE_DIALOG =
2005  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
2006  xfc->_NET_WM_WINDOW_TYPE_POPUP =
2007  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_POPUP", False);
2008  xfc->_NET_WM_WINDOW_TYPE_POPUP_MENU =
2009  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False);
2010  xfc->_NET_WM_WINDOW_TYPE_UTILITY =
2011  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
2012  xfc->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU =
2013  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False);
2014  xfc->_NET_WM_STATE_SKIP_TASKBAR =
2015  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_SKIP_TASKBAR", False);
2016  xfc->_NET_WM_STATE_SKIP_PAGER =
2017  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_SKIP_PAGER", False);
2018  xfc->_NET_WM_MOVERESIZE =
2019  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_MOVERESIZE", False);
2020  xfc->_NET_MOVERESIZE_WINDOW =
2021  Logging_XInternAtom(xfc->log, xfc->display, "_NET_MOVERESIZE_WINDOW", False);
2022  xfc->UTF8_STRING = Logging_XInternAtom(xfc->log, xfc->display, "UTF8_STRING", FALSE);
2023  xfc->WM_PROTOCOLS = Logging_XInternAtom(xfc->log, xfc->display, "WM_PROTOCOLS", False);
2024  xfc->WM_DELETE_WINDOW = Logging_XInternAtom(xfc->log, xfc->display, "WM_DELETE_WINDOW", False);
2025  xfc->WM_STATE = Logging_XInternAtom(xfc->log, xfc->display, "WM_STATE", False);
2026  xfc->x11event = CreateFileDescriptorEvent(NULL, FALSE, FALSE, xfc->xfds, WINPR_FD_READ);
2027 
2028  xfc->NET_WM_ALLOWED_ACTIONS =
2029  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ALLOWED_ACTIONS", False);
2030 
2031  xfc->NET_WM_ACTION_CLOSE =
2032  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_CLOSE", False);
2033  xfc->NET_WM_ACTION_MINIMIZE =
2034  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_MINIMIZE", False);
2035  xfc->NET_WM_ACTION_MOVE =
2036  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_MOVE", False);
2037  xfc->NET_WM_ACTION_RESIZE =
2038  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_RESIZE", False);
2039  xfc->NET_WM_ACTION_MAXIMIZE_HORZ =
2040  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
2041  xfc->NET_WM_ACTION_MAXIMIZE_VERT =
2042  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
2043  xfc->NET_WM_ACTION_FULLSCREEN =
2044  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_FULLSCREEN", False);
2045  xfc->NET_WM_ACTION_CHANGE_DESKTOP =
2046  Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_ACTION_CHANGE_DESKTOP", False);
2047 
2048  if (!xfc->x11event)
2049  {
2050  WLog_ERR(TAG, "Could not create xfds event");
2051  goto fail;
2052  }
2053 
2054  xf_check_extensions(xfc);
2055 
2056  xfc->vscreen.monitors = calloc(16, sizeof(MONITOR_INFO));
2057 
2058  if (!xfc->vscreen.monitors)
2059  goto fail;
2060  return TRUE;
2061 
2062 fail:
2063  xf_teardown_x11(xfc);
2064  return FALSE;
2065 }
2066 
2067 static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
2068 {
2069  xfContext* xfc = (xfContext*)instance->context;
2070  WINPR_ASSERT(context);
2071  WINPR_ASSERT(xfc);
2072  WINPR_ASSERT(!xfc->display);
2073  WINPR_ASSERT(!xfc->mutex);
2074  WINPR_ASSERT(!xfc->x11event);
2075  instance->PreConnect = xf_pre_connect;
2076  instance->PostConnect = xf_post_connect;
2077  instance->PostDisconnect = xf_post_disconnect;
2078  instance->PostFinalDisconnect = xf_post_final_disconnect;
2079  instance->LogonErrorInfo = xf_logon_error_info;
2080  instance->GetAccessToken = client_cli_get_access_token;
2081  PubSub_SubscribeTerminate(context->pubSub, xf_TerminateEventHandler);
2082 #ifdef WITH_XRENDER
2083  PubSub_SubscribeZoomingChange(context->pubSub, xf_ZoomingChangeEventHandler);
2084  PubSub_SubscribePanningChange(context->pubSub, xf_PanningChangeEventHandler);
2085 #endif
2086  xfc->log = WLog_Get(TAG);
2087 
2088  return TRUE;
2089 }
2090 
2091 static void xfreerdp_client_free(freerdp* instance, rdpContext* context)
2092 {
2093  if (!context)
2094  return;
2095 
2096  if (context->pubSub)
2097  {
2098  PubSub_UnsubscribeTerminate(context->pubSub, xf_TerminateEventHandler);
2099 #ifdef WITH_XRENDER
2100  PubSub_UnsubscribeZoomingChange(context->pubSub, xf_ZoomingChangeEventHandler);
2101  PubSub_UnsubscribePanningChange(context->pubSub, xf_PanningChangeEventHandler);
2102 #endif
2103  }
2104 }
2105 
2106 int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
2107 {
2108  pEntryPoints->Version = 1;
2109  pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
2110  pEntryPoints->GlobalInit = xfreerdp_client_global_init;
2111  pEntryPoints->GlobalUninit = xfreerdp_client_global_uninit;
2112  pEntryPoints->ContextSize = sizeof(xfContext);
2113  pEntryPoints->ClientNew = xfreerdp_client_new;
2114  pEntryPoints->ClientFree = xfreerdp_client_free;
2115  pEntryPoints->ClientStart = xfreerdp_client_start;
2116  pEntryPoints->ClientStop = freerdp_client_common_stop;
2117  return 0;
2118 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.