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