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