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