FreeRDP
xf_rail.c
1 
20 #include <freerdp/config.h>
21 
22 #include <X11/Xlib.h>
23 #include <X11/Xatom.h>
24 #include <X11/Xutil.h>
25 
26 #include <winpr/assert.h>
27 #include <winpr/wlog.h>
28 #include <winpr/print.h>
29 
30 #include <freerdp/client/rail.h>
31 
32 #include "xf_window.h"
33 #include "xf_rail.h"
34 #include "xf_utils.h"
35 
36 #include <freerdp/log.h>
37 #define TAG CLIENT_TAG("x11")
38 
39 static const char* error_code_names[] = { "RAIL_EXEC_S_OK",
40  "RAIL_EXEC_E_HOOK_NOT_LOADED",
41  "RAIL_EXEC_E_DECODE_FAILED",
42  "RAIL_EXEC_E_NOT_IN_ALLOWLIST",
43  "RAIL_EXEC_E_FILE_NOT_FOUND",
44  "RAIL_EXEC_E_FAIL",
45  "RAIL_EXEC_E_SESSION_LOCKED" };
46 
47 #ifdef WITH_DEBUG_RAIL
48 static const char* movetype_names[] = {
49  "(invalid)", "RAIL_WMSZ_LEFT", "RAIL_WMSZ_RIGHT",
50  "RAIL_WMSZ_TOP", "RAIL_WMSZ_TOPLEFT", "RAIL_WMSZ_TOPRIGHT",
51  "RAIL_WMSZ_BOTTOM", "RAIL_WMSZ_BOTTOMLEFT", "RAIL_WMSZ_BOTTOMRIGHT",
52  "RAIL_WMSZ_MOVE", "RAIL_WMSZ_KEYMOVE", "RAIL_WMSZ_KEYSIZE"
53 };
54 #endif
55 
56 struct xf_rail_icon
57 {
58  long* data;
59  int length;
60 };
61 typedef struct xf_rail_icon xfRailIcon;
62 
63 struct xf_rail_icon_cache
64 {
65  xfRailIcon* entries;
66  UINT32 numCaches;
67  UINT32 numCacheEntries;
68  xfRailIcon scratch;
69 };
70 
71 typedef struct
72 {
73  xfContext* xfc;
74  const RECTANGLE_16* rect;
75 } rail_paint_fn_arg_t;
76 
77 void xf_rail_enable_remoteapp_mode(xfContext* xfc)
78 {
79  if (!xfc->remote_app)
80  {
81  xfc->remote_app = TRUE;
82  xfc->drawable = xf_CreateDummyWindow(xfc);
83  xf_DestroyDesktopWindow(xfc, xfc->window);
84  xfc->window = NULL;
85  }
86 }
87 
88 void xf_rail_disable_remoteapp_mode(xfContext* xfc)
89 {
90  if (xfc->remote_app)
91  {
92  xfc->remote_app = FALSE;
93  xf_DestroyDummyWindow(xfc, xfc->drawable);
94  xf_create_window(xfc);
95  xf_create_image(xfc);
96  }
97 }
98 
99 void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
100 {
101  RAIL_ACTIVATE_ORDER activate = { 0 };
102  xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, xwindow);
103 
104  if (!appWindow)
105  return;
106 
107  if (enabled)
108  xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
109 
110  WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
111  activate.windowId = (UINT32)appWindow->windowId;
112  activate.enabled = enabled;
113  xfc->rail->ClientActivate(xfc->rail, &activate);
114 }
115 
116 BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command)
117 {
118  WINPR_ASSERT(xfc);
119  WINPR_ASSERT(xfc->rail);
120  WINPR_ASSERT(xfc->rail->ClientSystemCommand);
121  if (windowId > UINT32_MAX)
122  return FALSE;
123 
124  const RAIL_SYSCOMMAND_ORDER syscommand = { .windowId = (UINT32)windowId, .command = command };
125  const UINT rc = xfc->rail->ClientSystemCommand(xfc->rail, &syscommand);
126  return rc == CHANNEL_RC_OK;
127 }
128 
135 void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
136 {
137  RAIL_WINDOW_MOVE_ORDER windowMove = { 0 };
138 
139  if (!appWindow->is_mapped || appWindow->local_move.state != LMS_NOT_ACTIVE)
140  return;
141 
142  /* If current window position disagrees with RDP window position, send update to RDP server */
143  if (appWindow->x != appWindow->windowOffsetX || appWindow->y != appWindow->windowOffsetY ||
144  appWindow->width != (INT64)appWindow->windowWidth ||
145  appWindow->height != (INT64)appWindow->windowHeight)
146  {
147  WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
148  windowMove.windowId = (UINT32)appWindow->windowId;
149  /*
150  * Calculate new size/position for the rail window(new values for
151  * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
152  */
153  windowMove.left = appWindow->x - appWindow->resizeMarginLeft;
154  windowMove.top = appWindow->y - appWindow->resizeMarginTop;
155  windowMove.right = appWindow->x + appWindow->width + appWindow->resizeMarginRight;
156  windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom;
157  xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
158  }
159 }
160 
161 void xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow)
162 {
163  int x = 0;
164  int y = 0;
165  int child_x = 0;
166  int child_y = 0;
167  unsigned int mask = 0;
168  Window root_window = 0;
169  Window child_window = 0;
170  rdpInput* input = NULL;
171 
172  WINPR_ASSERT(xfc);
173 
174  input = xfc->common.context.input;
175  WINPR_ASSERT(input);
176 
177  if ((appWindow->local_move.direction == _NET_WM_MOVERESIZE_MOVE_KEYBOARD) ||
178  (appWindow->local_move.direction == _NET_WM_MOVERESIZE_SIZE_KEYBOARD))
179  {
180  RAIL_WINDOW_MOVE_ORDER windowMove = { 0 };
181 
182  /*
183  * For keyboard moves send and explicit update to RDP server
184  */
185  WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
186  windowMove.windowId = (UINT32)appWindow->windowId;
187  /*
188  * Calculate new size/position for the rail window(new values for
189  * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
190  *
191  */
192  windowMove.left = appWindow->x - appWindow->resizeMarginLeft;
193  windowMove.top = appWindow->y - appWindow->resizeMarginTop;
194  windowMove.right =
195  appWindow->x + appWindow->width +
196  appWindow
197  ->resizeMarginRight; /* In the update to RDP the position is one past the window */
198  windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom;
199  xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
200  }
201 
202  /*
203  * Simulate button up at new position to end the local move (per RDP spec)
204  */
205  XQueryPointer(xfc->display, appWindow->handle, &root_window, &child_window, &x, &y, &child_x,
206  &child_y, &mask);
207 
208  /* only send the mouse coordinates if not a keyboard move or size */
209  if ((appWindow->local_move.direction != _NET_WM_MOVERESIZE_MOVE_KEYBOARD) &&
210  (appWindow->local_move.direction != _NET_WM_MOVERESIZE_SIZE_KEYBOARD))
211  {
212  freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_BUTTON1, x, y);
213  }
214 
215  /*
216  * Proactively update the RAIL window dimensions. There is a race condition where
217  * we can start to receive GDI orders for the new window dimensions before we
218  * receive the RAIL ORDER for the new window size. This avoids that race condition.
219  */
220  appWindow->windowOffsetX = appWindow->x;
221  appWindow->windowOffsetY = appWindow->y;
222  appWindow->windowWidth = appWindow->width;
223  appWindow->windowHeight = appWindow->height;
224  appWindow->local_move.state = LMS_TERMINATING;
225 }
226 
227 BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* rect)
228 {
229  xfAppWindow* appWindow = xf_rail_get_window(xfc, windowId);
230 
231  WINPR_ASSERT(rect);
232 
233  if (!appWindow)
234  return FALSE;
235 
236  const RECTANGLE_16 windowRect = { .left = MAX(appWindow->x, 0),
237  .top = MAX(appWindow->y, 0),
238  .right = MAX(appWindow->x + appWindow->width, 0),
239  .bottom = MAX(appWindow->y + appWindow->height, 0) };
240 
241  REGION16 windowInvalidRegion = { 0 };
242  region16_init(&windowInvalidRegion);
243  region16_union_rect(&windowInvalidRegion, &windowInvalidRegion, &windowRect);
244  region16_intersect_rect(&windowInvalidRegion, &windowInvalidRegion, rect);
245 
246  if (!region16_is_empty(&windowInvalidRegion))
247  {
248  const RECTANGLE_16* extents = region16_extents(&windowInvalidRegion);
249  const RECTANGLE_16 updateRect = { .left = extents->left - appWindow->x,
250  .top = extents->top - appWindow->y,
251  .right = extents->right - appWindow->x,
252  .bottom = extents->bottom - appWindow->y };
253 
254  xf_UpdateWindowArea(xfc, appWindow, updateRect.left, updateRect.top,
255  updateRect.right - updateRect.left, updateRect.bottom - updateRect.top);
256  }
257  region16_uninit(&windowInvalidRegion);
258  return TRUE;
259 }
260 
261 static BOOL rail_paint_fn(const void* pvkey, void* value, void* pvarg)
262 {
263  rail_paint_fn_arg_t* arg = pvarg;
264  WINPR_ASSERT(pvkey);
265  WINPR_ASSERT(arg);
266 
267  const UINT64 key = *(const UINT64*)pvkey;
268  return xf_rail_paint_surface(arg->xfc, key, arg->rect);
269 }
270 
271 BOOL xf_rail_paint(xfContext* xfc, const RECTANGLE_16* rect)
272 {
273  rail_paint_fn_arg_t arg = { .xfc = xfc, .rect = rect };
274 
275  WINPR_ASSERT(xfc);
276  WINPR_ASSERT(rect);
277 
278  if (!xfc->railWindows)
279  return TRUE;
280 
281  return HashTable_Foreach(xfc->railWindows, rail_paint_fn, &arg);
282 }
283 
284 #define window_state_log_style(log, windowState) \
285  window_state_log_style_int((log), (windowState), __FILE__, __func__, __LINE__)
286 static void window_state_log_style_int(wLog* log, const WINDOW_STATE_ORDER* windowState,
287  const char* file, const char* fkt, size_t line)
288 {
289  const DWORD log_level = WLOG_DEBUG;
290 
291  WINPR_ASSERT(log);
292  WINPR_ASSERT(windowState);
293  if (WLog_IsLevelActive(log, log_level))
294  {
295  char buffer1[128] = { 0 };
296  char buffer2[128] = { 0 };
297 
298  window_styles_to_string(windowState->style, buffer1, sizeof(buffer1));
299  window_styles_ex_to_string(windowState->extendedStyle, buffer2, sizeof(buffer2));
300  WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
301  "windowStyle={%s, %s}", buffer1, buffer2);
302  }
303 }
304 
305 /* RemoteApp Core Protocol Extension */
306 
307 static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
308  const WINDOW_STATE_ORDER* windowState)
309 {
310  xfAppWindow* appWindow = NULL;
311  xfContext* xfc = (xfContext*)context;
312  UINT32 fieldFlags = orderInfo->fieldFlags;
313  BOOL position_or_size_updated = FALSE;
314  appWindow = xf_rail_get_window(xfc, orderInfo->windowId);
315 
316  if (fieldFlags & WINDOW_ORDER_STATE_NEW)
317  {
318  if (!appWindow)
319  appWindow = xf_rail_add_window(xfc, orderInfo->windowId, windowState->windowOffsetX,
320  windowState->windowOffsetY, windowState->windowWidth,
321  windowState->windowHeight, 0xFFFFFFFF);
322 
323  if (!appWindow)
324  return FALSE;
325 
326  appWindow->dwStyle = windowState->style;
327  appWindow->dwExStyle = windowState->extendedStyle;
328  window_state_log_style(xfc->log, windowState);
329 
330  /* Ensure window always gets a window title */
331  if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
332  {
333  union
334  {
335  WCHAR* wc;
336  BYTE* b;
337  } cnv;
338  char* title = NULL;
339 
340  cnv.b = windowState->titleInfo.string;
341  if (windowState->titleInfo.length == 0)
342  {
343  if (!(title = _strdup("")))
344  {
345  WLog_ERR(TAG, "failed to duplicate empty window title string");
346  /* error handled below */
347  }
348  }
349  else if (!(title = ConvertWCharNToUtf8Alloc(
350  cnv.wc, windowState->titleInfo.length / sizeof(WCHAR), NULL)))
351  {
352  WLog_ERR(TAG, "failed to convert window title");
353  /* error handled below */
354  }
355 
356  appWindow->title = title;
357  }
358  else
359  {
360  if (!(appWindow->title = _strdup("RdpRailWindow")))
361  WLog_ERR(TAG, "failed to duplicate default window title string");
362  }
363 
364  if (!appWindow->title)
365  {
366  free(appWindow);
367  return FALSE;
368  }
369 
370  xf_AppWindowInit(xfc, appWindow);
371  }
372 
373  if (!appWindow)
374  return FALSE;
375 
376  /* Keep track of any position/size update so that we can force a refresh of the window */
377  if ((fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) ||
378  (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE) ||
379  (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET) ||
380  (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE) ||
381  (fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA) ||
382  (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET) ||
383  (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY))
384  {
385  position_or_size_updated = TRUE;
386  }
387 
388  /* Update Parameters */
389 
390  if (fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET)
391  {
392  appWindow->windowOffsetX = windowState->windowOffsetX;
393  appWindow->windowOffsetY = windowState->windowOffsetY;
394  }
395 
396  if (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE)
397  {
398  appWindow->windowWidth = windowState->windowWidth;
399  appWindow->windowHeight = windowState->windowHeight;
400  }
401 
402  if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X)
403  {
404  appWindow->resizeMarginLeft = windowState->resizeMarginLeft;
405  appWindow->resizeMarginRight = windowState->resizeMarginRight;
406  }
407 
408  if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y)
409  {
410  appWindow->resizeMarginTop = windowState->resizeMarginTop;
411  appWindow->resizeMarginBottom = windowState->resizeMarginBottom;
412  }
413 
414  if (fieldFlags & WINDOW_ORDER_FIELD_OWNER)
415  {
416  appWindow->ownerWindowId = windowState->ownerWindowId;
417  }
418 
419  if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
420  {
421  appWindow->dwStyle = windowState->style;
422  appWindow->dwExStyle = windowState->extendedStyle;
423  window_state_log_style(xfc->log, windowState);
424  }
425 
426  if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
427  {
428  appWindow->showState = windowState->showState;
429  }
430 
431  if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
432  {
433  char* title = NULL;
434  union
435  {
436  WCHAR* wc;
437  BYTE* b;
438  } cnv;
439 
440  cnv.b = windowState->titleInfo.string;
441  if (windowState->titleInfo.length == 0)
442  {
443  if (!(title = _strdup("")))
444  {
445  WLog_ERR(TAG, "failed to duplicate empty window title string");
446  return FALSE;
447  }
448  }
449  else if (!(title = ConvertWCharNToUtf8Alloc(
450  cnv.wc, windowState->titleInfo.length / sizeof(WCHAR), NULL)))
451  {
452  WLog_ERR(TAG, "failed to convert window title");
453  return FALSE;
454  }
455 
456  free(appWindow->title);
457  appWindow->title = title;
458  }
459 
460  if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET)
461  {
462  appWindow->clientOffsetX = windowState->clientOffsetX;
463  appWindow->clientOffsetY = windowState->clientOffsetY;
464  }
465 
466  if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE)
467  {
468  appWindow->clientAreaWidth = windowState->clientAreaWidth;
469  appWindow->clientAreaHeight = windowState->clientAreaHeight;
470  }
471 
472  if (fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA)
473  {
474  appWindow->windowClientDeltaX = windowState->windowClientDeltaX;
475  appWindow->windowClientDeltaY = windowState->windowClientDeltaY;
476  }
477 
478  if (fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS)
479  {
480  if (appWindow->windowRects)
481  {
482  free(appWindow->windowRects);
483  appWindow->windowRects = NULL;
484  }
485 
486  appWindow->numWindowRects = windowState->numWindowRects;
487 
488  if (appWindow->numWindowRects)
489  {
490  appWindow->windowRects =
491  (RECTANGLE_16*)calloc(appWindow->numWindowRects, sizeof(RECTANGLE_16));
492 
493  if (!appWindow->windowRects)
494  return FALSE;
495 
496  CopyMemory(appWindow->windowRects, windowState->windowRects,
497  appWindow->numWindowRects * sizeof(RECTANGLE_16));
498  }
499  }
500 
501  if (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET)
502  {
503  appWindow->visibleOffsetX = windowState->visibleOffsetX;
504  appWindow->visibleOffsetY = windowState->visibleOffsetY;
505  }
506 
507  if (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY)
508  {
509  if (appWindow->visibilityRects)
510  {
511  free(appWindow->visibilityRects);
512  appWindow->visibilityRects = NULL;
513  }
514 
515  appWindow->numVisibilityRects = windowState->numVisibilityRects;
516 
517  if (appWindow->numVisibilityRects)
518  {
519  appWindow->visibilityRects =
520  (RECTANGLE_16*)calloc(appWindow->numVisibilityRects, sizeof(RECTANGLE_16));
521 
522  if (!appWindow->visibilityRects)
523  return FALSE;
524 
525  CopyMemory(appWindow->visibilityRects, windowState->visibilityRects,
526  appWindow->numVisibilityRects * sizeof(RECTANGLE_16));
527  }
528  }
529 
530  /* Update Window */
531 
532  if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
533  {
534  }
535 
536  if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
537  {
538  xf_ShowWindow(xfc, appWindow, appWindow->showState);
539  }
540 
541  if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
542  {
543  if (appWindow->title)
544  xf_SetWindowText(xfc, appWindow, appWindow->title);
545  }
546 
547  if (position_or_size_updated)
548  {
549  UINT32 visibilityRectsOffsetX =
550  (appWindow->visibleOffsetX -
551  (appWindow->clientOffsetX - appWindow->windowClientDeltaX));
552  UINT32 visibilityRectsOffsetY =
553  (appWindow->visibleOffsetY -
554  (appWindow->clientOffsetY - appWindow->windowClientDeltaY));
555 
556  /*
557  * The rail server like to set the window to a small size when it is minimized even though
558  * it is hidden in some cases this can cause the window not to restore back to its original
559  * size. Therefore we don't update our local window when that rail window state is minimized
560  */
561  if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
562  {
563  /* Redraw window area if already in the correct position */
564  if (appWindow->x == (INT64)appWindow->windowOffsetX &&
565  appWindow->y == (INT64)appWindow->windowOffsetY &&
566  appWindow->width == (INT64)appWindow->windowWidth &&
567  appWindow->height == (INT64)appWindow->windowHeight)
568  {
569  xf_UpdateWindowArea(xfc, appWindow, 0, 0, appWindow->windowWidth,
570  appWindow->windowHeight);
571  }
572  else
573  {
574  xf_MoveWindow(xfc, appWindow, appWindow->windowOffsetX, appWindow->windowOffsetY,
575  appWindow->windowWidth, appWindow->windowHeight);
576  }
577 
578  xf_SetWindowVisibilityRects(xfc, appWindow, visibilityRectsOffsetX,
579  visibilityRectsOffsetY, appWindow->visibilityRects,
580  appWindow->numVisibilityRects);
581  }
582 
583  if (appWindow->rail_state == WINDOW_SHOW_MAXIMIZED)
584  {
585  xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, _NET_WM_STATE_ADD,
586  xfc->_NET_WM_STATE_MAXIMIZED_VERT, xfc->_NET_WM_STATE_MAXIMIZED_HORZ,
587  0, 0);
588  }
589  }
590 
591  if ((fieldFlags & WINDOW_ORDER_STATE_NEW) == 0)
592  xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
593 
594  /* We should only be using the visibility rects for shaping the window */
595  /*if (fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS)
596  {
597  xf_SetWindowRects(xfc, appWindow, appWindow->windowRects, appWindow->numWindowRects);
598  }*/
599  return TRUE;
600 }
601 
602 static BOOL xf_rail_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
603 {
604  xfContext* xfc = (xfContext*)context;
605  return xf_rail_del_window(xfc, orderInfo->windowId);
606 }
607 
608 static xfRailIconCache* RailIconCache_New(rdpSettings* settings)
609 {
610  xfRailIconCache* cache = NULL;
611  cache = calloc(1, sizeof(xfRailIconCache));
612 
613  if (!cache)
614  return NULL;
615 
616  cache->numCaches = freerdp_settings_get_uint32(settings, FreeRDP_RemoteAppNumIconCaches);
617  cache->numCacheEntries =
618  freerdp_settings_get_uint32(settings, FreeRDP_RemoteAppNumIconCacheEntries);
619  cache->entries = calloc(1ull * cache->numCaches * cache->numCacheEntries, sizeof(xfRailIcon));
620 
621  if (!cache->entries)
622  {
623  WLog_ERR(TAG, "failed to allocate icon cache %" PRIu32 " x %" PRIu32 " entries",
624  cache->numCaches, cache->numCacheEntries);
625  free(cache);
626  return NULL;
627  }
628 
629  return cache;
630 }
631 
632 static void RailIconCache_Free(xfRailIconCache* cache)
633 {
634  if (cache)
635  {
636  for (UINT32 i = 0; i < cache->numCaches * cache->numCacheEntries; i++)
637  {
638  free(cache->entries[i].data);
639  }
640 
641  free(cache->scratch.data);
642  free(cache->entries);
643  free(cache);
644  }
645 }
646 
647 static xfRailIcon* RailIconCache_Lookup(xfRailIconCache* cache, UINT8 cacheId, UINT16 cacheEntry)
648 {
649  /*
650  * MS-RDPERP 2.2.1.2.3 Icon Info (TS_ICON_INFO)
651  *
652  * CacheId (1 byte):
653  * If the value is 0xFFFF, the icon SHOULD NOT be cached.
654  *
655  * Yes, the spec says "0xFFFF" in the 2018-03-16 revision,
656  * but the actual protocol field is 1-byte wide.
657  */
658  if (cacheId == 0xFF)
659  return &cache->scratch;
660 
661  if (cacheId >= cache->numCaches)
662  return NULL;
663 
664  if (cacheEntry >= cache->numCacheEntries)
665  return NULL;
666 
667  return &cache->entries[cache->numCacheEntries * cacheId + cacheEntry];
668 }
669 
670 /*
671  * _NET_WM_ICON format is defined as "array of CARDINAL" values which for
672  * Xlib must be represented with an array of C's "long" values. Note that
673  * "long" != "INT32" on 64-bit systems. Therefore we can't simply cast
674  * the bitmap data as (unsigned char*), we have to copy all the pixels.
675  *
676  * The first two values are width and height followed by actual color data
677  * in ARGB format (e.g., 0xFFFF0000L is opaque red), pixels are in normal,
678  * left-to-right top-down order.
679  */
680 static BOOL convert_rail_icon(const ICON_INFO* iconInfo, xfRailIcon* railIcon)
681 {
682  BYTE* argbPixels = NULL;
683  BYTE* nextPixel = NULL;
684  long* pixels = NULL;
685  int nelements = 0;
686  argbPixels = calloc(1ull * iconInfo->width * iconInfo->height, 4);
687 
688  if (!argbPixels)
689  goto error;
690 
691  if (!freerdp_image_copy_from_icon_data(
692  argbPixels, PIXEL_FORMAT_ARGB32, 0, 0, 0, iconInfo->width, iconInfo->height,
693  iconInfo->bitsColor, iconInfo->cbBitsColor, iconInfo->bitsMask, iconInfo->cbBitsMask,
694  iconInfo->colorTable, iconInfo->cbColorTable, iconInfo->bpp))
695  goto error;
696 
697  nelements = 2 + iconInfo->width * iconInfo->height;
698  pixels = realloc(railIcon->data, nelements * sizeof(long));
699 
700  if (!pixels)
701  goto error;
702 
703  railIcon->data = pixels;
704  railIcon->length = nelements;
705  pixels[0] = iconInfo->width;
706  pixels[1] = iconInfo->height;
707  nextPixel = argbPixels;
708 
709  for (int i = 2; i < nelements; i++)
710  {
711  pixels[i] = FreeRDPReadColor(nextPixel, PIXEL_FORMAT_BGRA32);
712  nextPixel += 4;
713  }
714 
715  free(argbPixels);
716  return TRUE;
717 error:
718  free(argbPixels);
719  return FALSE;
720 }
721 
722 static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfRailIcon* icon,
723  BOOL replace)
724 {
725  WINPR_ASSERT(xfc);
726 
727  LogTagAndXChangeProperty(TAG, xfc->display, railWindow->handle, xfc->_NET_WM_ICON, XA_CARDINAL,
728  32, replace ? PropModeReplace : PropModeAppend,
729  (unsigned char*)icon->data, icon->length);
730  XFlush(xfc->display);
731 }
732 
733 static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
734  const WINDOW_ICON_ORDER* windowIcon)
735 {
736  xfContext* xfc = (xfContext*)context;
737  xfAppWindow* railWindow = NULL;
738  xfRailIcon* icon = NULL;
739  BOOL replaceIcon = 0;
740  railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
741 
742  if (!railWindow)
743  return TRUE;
744 
745  icon = RailIconCache_Lookup(xfc->railIconCache, windowIcon->iconInfo->cacheId,
746  windowIcon->iconInfo->cacheEntry);
747 
748  if (!icon)
749  {
750  WLog_WARN(TAG, "failed to get icon from cache %02X:%04X", windowIcon->iconInfo->cacheId,
751  windowIcon->iconInfo->cacheEntry);
752  return FALSE;
753  }
754 
755  if (!convert_rail_icon(windowIcon->iconInfo, icon))
756  {
757  WLog_WARN(TAG, "failed to convert icon for window %08X", orderInfo->windowId);
758  return FALSE;
759  }
760 
761  replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
762  xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
763  return TRUE;
764 }
765 
766 static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
767  const WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
768 {
769  xfContext* xfc = (xfContext*)context;
770  xfAppWindow* railWindow = NULL;
771  xfRailIcon* icon = NULL;
772  BOOL replaceIcon = 0;
773  railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
774 
775  if (!railWindow)
776  return TRUE;
777 
778  icon = RailIconCache_Lookup(xfc->railIconCache, windowCachedIcon->cachedIcon.cacheId,
779  windowCachedIcon->cachedIcon.cacheEntry);
780 
781  if (!icon)
782  {
783  WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
784  windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry);
785  return FALSE;
786  }
787 
788  replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
789  xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
790  return TRUE;
791 }
792 
793 static BOOL xf_rail_notify_icon_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
794  const NOTIFY_ICON_STATE_ORDER* notifyIconState)
795 {
796  if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION)
797  {
798  }
799 
800  if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_TIP)
801  {
802  }
803 
804  if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_INFO_TIP)
805  {
806  }
807 
808  if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_STATE)
809  {
810  }
811 
812  if (orderInfo->fieldFlags & WINDOW_ORDER_ICON)
813  {
814  }
815 
816  if (orderInfo->fieldFlags & WINDOW_ORDER_CACHED_ICON)
817  {
818  }
819 
820  return TRUE;
821 }
822 
823 static BOOL xf_rail_notify_icon_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
824  const NOTIFY_ICON_STATE_ORDER* notifyIconState)
825 {
826  return xf_rail_notify_icon_common(context, orderInfo, notifyIconState);
827 }
828 
829 static BOOL xf_rail_notify_icon_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
830  const NOTIFY_ICON_STATE_ORDER* notifyIconState)
831 {
832  return xf_rail_notify_icon_common(context, orderInfo, notifyIconState);
833 }
834 
835 static BOOL xf_rail_notify_icon_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
836 {
837  return TRUE;
838 }
839 
840 static BOOL xf_rail_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
841  const MONITORED_DESKTOP_ORDER* monitoredDesktop)
842 {
843  return TRUE;
844 }
845 
846 static BOOL xf_rail_non_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
847 {
848  xfContext* xfc = (xfContext*)context;
849  xf_rail_disable_remoteapp_mode(xfc);
850  return TRUE;
851 }
852 
853 static void xf_rail_register_update_callbacks(rdpUpdate* update)
854 {
855  rdpWindowUpdate* window = update->window;
856  window->WindowCreate = xf_rail_window_common;
857  window->WindowUpdate = xf_rail_window_common;
858  window->WindowDelete = xf_rail_window_delete;
859  window->WindowIcon = xf_rail_window_icon;
860  window->WindowCachedIcon = xf_rail_window_cached_icon;
861  window->NotifyIconCreate = xf_rail_notify_icon_create;
862  window->NotifyIconUpdate = xf_rail_notify_icon_update;
863  window->NotifyIconDelete = xf_rail_notify_icon_delete;
864  window->MonitoredDesktop = xf_rail_monitored_desktop;
865  window->NonMonitoredDesktop = xf_rail_non_monitored_desktop;
866 }
867 
868 /* RemoteApp Virtual Channel Extension */
869 
875 static UINT xf_rail_server_execute_result(RailClientContext* context,
876  const RAIL_EXEC_RESULT_ORDER* execResult)
877 {
878  xfContext* xfc = NULL;
879 
880  WINPR_ASSERT(context);
881  WINPR_ASSERT(execResult);
882 
883  xfc = (xfContext*)context->custom;
884  WINPR_ASSERT(xfc);
885 
886  if (execResult->execResult != RAIL_EXEC_S_OK)
887  {
888  WLog_ERR(TAG, "RAIL exec error: execResult=%s NtError=0x%X\n",
889  error_code_names[execResult->execResult], execResult->rawResult);
890  freerdp_abort_connect_context(&xfc->common.context);
891  }
892  else
893  {
894  xf_rail_enable_remoteapp_mode(xfc);
895  }
896 
897  return CHANNEL_RC_OK;
898 }
899 
905 static UINT xf_rail_server_system_param(RailClientContext* context,
906  const RAIL_SYSPARAM_ORDER* sysparam)
907 {
908  // TODO: Actually apply param
909  return CHANNEL_RC_OK;
910 }
911 
917 static UINT xf_rail_server_handshake(RailClientContext* context,
918  const RAIL_HANDSHAKE_ORDER* handshake)
919 {
920  return client_rail_server_start_cmd(context);
921 }
922 
928 static UINT xf_rail_server_handshake_ex(RailClientContext* context,
929  const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
930 {
931  return client_rail_server_start_cmd(context);
932 }
933 
939 static UINT xf_rail_server_local_move_size(RailClientContext* context,
940  const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
941 {
942  int x = 0;
943  int y = 0;
944  int direction = 0;
945  Window child_window = 0;
946  xfContext* xfc = (xfContext*)context->custom;
947  xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId);
948 
949  if (!appWindow)
950  return ERROR_INTERNAL_ERROR;
951 
952  switch (localMoveSize->moveSizeType)
953  {
954  case RAIL_WMSZ_LEFT:
955  direction = _NET_WM_MOVERESIZE_SIZE_LEFT;
956  x = localMoveSize->posX;
957  y = localMoveSize->posY;
958  break;
959 
960  case RAIL_WMSZ_RIGHT:
961  direction = _NET_WM_MOVERESIZE_SIZE_RIGHT;
962  x = localMoveSize->posX;
963  y = localMoveSize->posY;
964  break;
965 
966  case RAIL_WMSZ_TOP:
967  direction = _NET_WM_MOVERESIZE_SIZE_TOP;
968  x = localMoveSize->posX;
969  y = localMoveSize->posY;
970  break;
971 
972  case RAIL_WMSZ_TOPLEFT:
973  direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
974  x = localMoveSize->posX;
975  y = localMoveSize->posY;
976  break;
977 
978  case RAIL_WMSZ_TOPRIGHT:
979  direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
980  x = localMoveSize->posX;
981  y = localMoveSize->posY;
982  break;
983 
984  case RAIL_WMSZ_BOTTOM:
985  direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
986  x = localMoveSize->posX;
987  y = localMoveSize->posY;
988  break;
989 
990  case RAIL_WMSZ_BOTTOMLEFT:
991  direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
992  x = localMoveSize->posX;
993  y = localMoveSize->posY;
994  break;
995 
996  case RAIL_WMSZ_BOTTOMRIGHT:
997  direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
998  x = localMoveSize->posX;
999  y = localMoveSize->posY;
1000  break;
1001 
1002  case RAIL_WMSZ_MOVE:
1003  direction = _NET_WM_MOVERESIZE_MOVE;
1004  XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
1005  localMoveSize->posX, localMoveSize->posY, &x, &y, &child_window);
1006  break;
1007 
1008  case RAIL_WMSZ_KEYMOVE:
1009  direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
1010  x = localMoveSize->posX;
1011  y = localMoveSize->posY;
1012  /* FIXME: local keyboard moves not working */
1013  return CHANNEL_RC_OK;
1014 
1015  case RAIL_WMSZ_KEYSIZE:
1016  direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
1017  x = localMoveSize->posX;
1018  y = localMoveSize->posY;
1019  /* FIXME: local keyboard moves not working */
1020  return CHANNEL_RC_OK;
1021  default:
1022  break;
1023  }
1024 
1025  if (localMoveSize->isMoveSizeStart)
1026  xf_StartLocalMoveSize(xfc, appWindow, direction, x, y);
1027  else
1028  xf_EndLocalMoveSize(xfc, appWindow);
1029 
1030  return CHANNEL_RC_OK;
1031 }
1032 
1038 static UINT xf_rail_server_min_max_info(RailClientContext* context,
1039  const RAIL_MINMAXINFO_ORDER* minMaxInfo)
1040 {
1041  xfContext* xfc = (xfContext*)context->custom;
1042  xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId);
1043 
1044  if (appWindow)
1045  {
1046  xf_SetWindowMinMaxInfo(xfc, appWindow, minMaxInfo->maxWidth, minMaxInfo->maxHeight,
1047  minMaxInfo->maxPosX, minMaxInfo->maxPosY, minMaxInfo->minTrackWidth,
1048  minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
1049  minMaxInfo->maxTrackHeight);
1050  }
1051 
1052  return CHANNEL_RC_OK;
1053 }
1054 
1060 static UINT xf_rail_server_language_bar_info(RailClientContext* context,
1061  const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
1062 {
1063  return CHANNEL_RC_OK;
1064 }
1065 
1071 static UINT xf_rail_server_get_appid_response(RailClientContext* context,
1072  const RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
1073 {
1074  return CHANNEL_RC_OK;
1075 }
1076 
1077 static BOOL rail_window_key_equals(const void* key1, const void* key2)
1078 {
1079  const UINT64* k1 = (const UINT64*)key1;
1080  const UINT64* k2 = (const UINT64*)key2;
1081 
1082  if (!k1 || !k2)
1083  return FALSE;
1084 
1085  return *k1 == *k2;
1086 }
1087 
1088 static UINT32 rail_window_key_hash(const void* key)
1089 {
1090  const UINT64* k1 = (const UINT64*)key;
1091  return (UINT32)*k1;
1092 }
1093 
1094 static void rail_window_free(void* value)
1095 {
1096  xfAppWindow* appWindow = (xfAppWindow*)value;
1097 
1098  if (!appWindow)
1099  return;
1100 
1101  xf_DestroyWindow(appWindow->xfc, appWindow);
1102 }
1103 
1104 int xf_rail_init(xfContext* xfc, RailClientContext* rail)
1105 {
1106  rdpContext* context = (rdpContext*)xfc;
1107 
1108  if (!xfc || !rail)
1109  return 0;
1110 
1111  xfc->rail = rail;
1112  xf_rail_register_update_callbacks(context->update);
1113  rail->custom = (void*)xfc;
1114  rail->ServerExecuteResult = xf_rail_server_execute_result;
1115  rail->ServerSystemParam = xf_rail_server_system_param;
1116  rail->ServerHandshake = xf_rail_server_handshake;
1117  rail->ServerHandshakeEx = xf_rail_server_handshake_ex;
1118  rail->ServerLocalMoveSize = xf_rail_server_local_move_size;
1119  rail->ServerMinMaxInfo = xf_rail_server_min_max_info;
1120  rail->ServerLanguageBarInfo = xf_rail_server_language_bar_info;
1121  rail->ServerGetAppIdResponse = xf_rail_server_get_appid_response;
1122  xfc->railWindows = HashTable_New(TRUE);
1123 
1124  if (!xfc->railWindows)
1125  return 0;
1126 
1127  if (!HashTable_SetHashFunction(xfc->railWindows, rail_window_key_hash))
1128  goto fail;
1129  {
1130  wObject* obj = HashTable_KeyObject(xfc->railWindows);
1131  obj->fnObjectEquals = rail_window_key_equals;
1132  }
1133  {
1134  wObject* obj = HashTable_ValueObject(xfc->railWindows);
1135  obj->fnObjectFree = rail_window_free;
1136  }
1137  xfc->railIconCache = RailIconCache_New(xfc->common.context.settings);
1138 
1139  if (!xfc->railIconCache)
1140  {
1141  }
1142 
1143  return 1;
1144 fail:
1145  HashTable_Free(xfc->railWindows);
1146  return 0;
1147 }
1148 
1149 int xf_rail_uninit(xfContext* xfc, RailClientContext* rail)
1150 {
1151  WINPR_UNUSED(rail);
1152 
1153  if (xfc->rail)
1154  {
1155  xfc->rail->custom = NULL;
1156  xfc->rail = NULL;
1157  }
1158 
1159  if (xfc->railWindows)
1160  {
1161  HashTable_Free(xfc->railWindows);
1162  xfc->railWindows = NULL;
1163  }
1164 
1165  if (xfc->railIconCache)
1166  {
1167  RailIconCache_Free(xfc->railIconCache);
1168  xfc->railIconCache = NULL;
1169  }
1170 
1171  return 1;
1172 }
1173 
1174 xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
1175  UINT32 height, UINT32 surfaceId)
1176 {
1177  xfAppWindow* appWindow = NULL;
1178 
1179  if (!xfc)
1180  return NULL;
1181 
1182  appWindow = (xfAppWindow*)calloc(1, sizeof(xfAppWindow));
1183 
1184  if (!appWindow)
1185  return NULL;
1186 
1187  appWindow->xfc = xfc;
1188  appWindow->windowId = id;
1189  appWindow->surfaceId = surfaceId;
1190  appWindow->x = x;
1191  appWindow->y = y;
1192  appWindow->width = width;
1193  appWindow->height = height;
1194 
1195  if (!xf_AppWindowCreate(xfc, appWindow))
1196  goto fail;
1197  if (!HashTable_Insert(xfc->railWindows, &appWindow->windowId, (void*)appWindow))
1198  goto fail;
1199  return appWindow;
1200 fail:
1201  rail_window_free(appWindow);
1202  return NULL;
1203 }
1204 
1205 BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
1206 {
1207  if (!xfc)
1208  return FALSE;
1209 
1210  if (!xfc->railWindows)
1211  return FALSE;
1212 
1213  return HashTable_Remove(xfc->railWindows, &id);
1214 }
1215 
1216 xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id)
1217 {
1218  if (!xfc)
1219  return NULL;
1220 
1221  if (!xfc->railWindows)
1222  return FALSE;
1223 
1224  return HashTable_GetItemValue(xfc->railWindows, &id);
1225 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57