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