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