20#include <freerdp/config.h>
26#include <winpr/cast.h>
27#include <winpr/assert.h>
28#include <winpr/wlog.h>
29#include <winpr/print.h>
31#include <freerdp/client/rail.h>
37#include <freerdp/log.h>
40static const char* error_code2str(UINT32 code)
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);
55 return "RAIL_EXEC_E_UNKNOWN";
61static const char* movetype2str(UINT32 code)
70 EVCASE(RAIL_WMSZ_LEFT);
71 EVCASE(RAIL_WMSZ_RIGHT);
72 EVCASE(RAIL_WMSZ_TOP);
73 EVCASE(RAIL_WMSZ_TOPLEFT);
74 EVCASE(RAIL_WMSZ_TOPRIGHT);
75 EVCASE(RAIL_WMSZ_BOTTOM);
76 EVCASE(RAIL_WMSZ_BOTTOMLEFT);
77 EVCASE(RAIL_WMSZ_BOTTOMRIGHT);
78 EVCASE(RAIL_WMSZ_MOVE);
79 EVCASE(RAIL_WMSZ_KEYMOVE);
80 EVCASE(RAIL_WMSZ_KEYSIZE);
82 return "RAIL_WMSZ_INVALID";
92typedef struct xf_rail_icon xfRailIcon;
94struct xf_rail_icon_cache
98 UINT32 numCacheEntries;
106} rail_paint_fn_arg_t;
108BOOL xf_rail_enable_remoteapp_mode(xfContext* xfc)
111 if (!xfc->remote_app)
113 rdpGdi* gdi = xfc->common.context.gdi;
116 const BOOL old = gdi->suppressOutput;
117 gdi->suppressOutput = TRUE;
118 xfc->remote_app = TRUE;
119 xfc->drawable = xf_CreateDummyWindow(xfc);
120 xf_DestroyDesktopWindow(xfc, xfc->window);
121 xfc->window =
nullptr;
123 gdi->suppressOutput = old;
128BOOL xf_rail_disable_remoteapp_mode(xfContext* xfc)
133 rdpGdi* gdi = xfc->common.context.gdi;
136 const BOOL old = gdi->suppressOutput;
137 gdi->suppressOutput = TRUE;
139 xfc->remote_app = FALSE;
140 xf_DestroyDummyWindow(xfc, xfc->drawable);
141 xf_destroy_window(xfc);
142 xf_create_window(xfc);
143 xf_create_image(xfc);
145 gdi->suppressOutput = old;
151static BOOL xf_rail_select_workarea_events(xfContext* xfc)
155 XWindowAttributes attributes = WINPR_C_ARRAY_INIT;
156 const Window root = DefaultRootWindow(xfc->display);
157 const int rc = LogDynAndXGetWindowAttributes(xfc->log, xfc->display, root, &attributes);
161 LogDynAndXSelectInput(xfc->log, xfc->display, root,
162 attributes.your_event_mask | PropertyChangeMask);
167static BOOL xf_rail_get_workarea(xfContext* xfc, xfWorkArea* workArea)
170 WINPR_ASSERT(workArea);
173 const xfWorkArea saved = xfc->workArea;
174 const BOOL rc = xf_GetWorkArea(xfc);
175 *workArea = xfc->workArea;
176 xfc->workArea = saved;
182static UINT xf_rail_send_workarea(xfContext* xfc)
186 if (!xfc->remote_app || !xfc->rail)
187 return CHANNEL_RC_OK;
189 xfWorkArea current = WINPR_C_ARRAY_INIT;
190 if (!xf_rail_get_workarea(xfc, ¤t))
191 return CHANNEL_RC_OK;
193 if ((current.x == xfc->railWorkArea.x) && (current.y == xfc->railWorkArea.y) &&
194 (current.width == xfc->railWorkArea.width) && (current.height == xfc->railWorkArea.height))
196 WLog_Print(xfc->log, WLOG_DEBUG,
197 "Suppressing work area %" PRId32
",%" PRId32
" %" PRIu32
"x%" PRIu32
199 current.x, current.y, current.width, current.height);
200 return CHANNEL_RC_OK;
203 const INT64 right = (INT64)current.x + current.width;
204 const INT64 bottom = (INT64)current.y + current.height;
207 WINPR_CXX_COMPAT_CAST(UINT16, current.x),
208 .workArea.top = WINPR_CXX_COMPAT_CAST(UINT16, current.y),
209 .workArea.right = WINPR_CXX_COMPAT_CAST(UINT16, right),
211 WINPR_CXX_COMPAT_CAST(UINT16, bottom) };
213 WLog_Print(xfc->log, WLOG_DEBUG,
214 "Sending work area %" PRId32
",%" PRId32
" %" PRIu32
"x%" PRIu32, current.x,
215 current.y, current.width, current.height);
216 const UINT rc = xfc->rail->ClientSystemParam(xfc->rail, &sysparam);
217 if (rc == CHANNEL_RC_OK)
218 xfc->railWorkArea = current;
223BOOL xf_rail_schedule_workarea(xfContext* xfc)
227 xfWorkArea current = WINPR_C_ARRAY_INIT;
228 if (!xf_rail_get_workarea(xfc, ¤t))
230 WLog_Print(xfc->log, WLOG_DEBUG,
"Suppressing work area update: current value unavailable");
234 return xf_rail_send_workarea(xfc) == CHANNEL_RC_OK;
237BOOL xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
240 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, xwindow);
244 WLog_Print(xfc->log, WLOG_DEBUG,
"xf_rail_send_activate: ignoring unknown window 0x%08lx",
250 xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
252 WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
253 activate.windowId = (UINT32)appWindow->windowId;
254 xf_rail_return_window(appWindow, FALSE);
256 activate.enabled = enabled;
257 const UINT rc = xfc->rail->ClientActivate(xfc->rail, &activate);
258 return rc == CHANNEL_RC_OK;
261BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command)
264 WINPR_ASSERT(xfc->rail);
265 WINPR_ASSERT(xfc->rail->ClientSystemCommand);
266 if (windowId > UINT32_MAX)
270 const UINT rc = xfc->rail->ClientSystemCommand(xfc->rail, &syscommand);
271 return rc == CHANNEL_RC_OK;
280BOOL xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
285 WINPR_ASSERT(appWindow);
286 if (!appWindow->is_mapped || appWindow->local_move.state != LMS_NOT_ACTIVE)
290 if (appWindow->x != appWindow->windowOffsetX || appWindow->y != appWindow->windowOffsetY ||
291 appWindow->width != (INT64)appWindow->windowWidth ||
292 appWindow->height != (INT64)appWindow->windowHeight)
294 WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
295 windowMove.windowId = (UINT32)appWindow->windowId;
300 const INT16 left = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginLeft);
301 const INT16 right = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginRight);
302 const INT16 top = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginTop);
303 const INT16 bottom = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginBottom);
304 windowMove.left = WINPR_ASSERTING_INT_CAST(INT16, appWindow->x - left);
305 windowMove.top = WINPR_ASSERTING_INT_CAST(INT16, appWindow->y - top);
306 windowMove.right = WINPR_ASSERTING_INT_CAST(INT16, appWindow->x + appWindow->width + right);
308 WINPR_ASSERTING_INT_CAST(INT16, appWindow->y + appWindow->height + bottom);
309 const UINT rc = xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
310 return rc == CHANNEL_RC_OK;
315BOOL xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow)
321 unsigned int mask = 0;
322 Window root_window = 0;
323 Window child_window = 0;
326 WINPR_ASSERT(appWindow);
328 if ((appWindow->local_move.direction == NET_WM_MOVERESIZE_MOVE_KEYBOARD) ||
329 (appWindow->local_move.direction == NET_WM_MOVERESIZE_SIZE_KEYBOARD))
336 WINPR_ASSERT(appWindow->windowId <= UINT32_MAX);
337 windowMove.windowId = (UINT32)appWindow->windowId;
343 const INT16 left = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginLeft);
344 const INT16 right = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginRight);
345 const INT16 top = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginTop);
346 const INT16 bottom = WINPR_ASSERTING_INT_CAST(INT16, appWindow->resizeMarginBottom);
347 const INT16 w = WINPR_ASSERTING_INT_CAST(INT16, appWindow->width + right);
348 const INT16 h = WINPR_ASSERTING_INT_CAST(INT16, appWindow->height + bottom);
349 windowMove.left = WINPR_ASSERTING_INT_CAST(INT16, appWindow->x - left);
350 windowMove.top = WINPR_ASSERTING_INT_CAST(INT16, appWindow->y - top);
351 windowMove.right = WINPR_ASSERTING_INT_CAST(INT16, appWindow->x + w);
353 windowMove.bottom = WINPR_ASSERTING_INT_CAST(INT16, appWindow->y + h);
354 const UINT rc = xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
355 if (rc != CHANNEL_RC_OK)
362 XQueryPointer(xfc->display, appWindow->handle, &root_window, &child_window, &x, &y, &child_x,
366 if ((appWindow->local_move.direction != NET_WM_MOVERESIZE_MOVE_KEYBOARD) &&
367 (appWindow->local_move.direction != NET_WM_MOVERESIZE_SIZE_KEYBOARD))
369 if (!freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_BUTTON1, x, y))
378 appWindow->windowOffsetX = appWindow->x;
379 appWindow->windowOffsetY = appWindow->y;
380 appWindow->windowWidth = WINPR_ASSERTING_INT_CAST(uint32_t, appWindow->width);
381 appWindow->windowHeight = WINPR_ASSERTING_INT_CAST(uint32_t, appWindow->height);
382 appWindow->local_move.state = LMS_TERMINATING;
386BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId,
const RECTANGLE_16* rect)
388 xfAppWindow* appWindow = xf_rail_get_window(xfc, windowId, FALSE);
396 .left = WINPR_ASSERTING_INT_CAST(UINT16, MAX(appWindow->x, 0)),
397 .top = WINPR_ASSERTING_INT_CAST(UINT16, MAX(appWindow->y, 0)),
398 .right = WINPR_ASSERTING_INT_CAST(UINT16, MAX(appWindow->x + appWindow->width, 0)),
399 .bottom = WINPR_ASSERTING_INT_CAST(UINT16, MAX(appWindow->y + appWindow->height, 0))
402 REGION16 windowInvalidRegion = WINPR_C_ARRAY_INIT;
403 region16_init(&windowInvalidRegion);
404 if (!region16_union_rect(&windowInvalidRegion, &windowInvalidRegion, &windowRect))
406 if (!region16_intersect_rect(&windowInvalidRegion, &windowInvalidRegion, rect))
409 if (!region16_is_empty(&windowInvalidRegion))
411 const RECTANGLE_16* extents = region16_extents(&windowInvalidRegion);
414 .left = WINPR_ASSERTING_INT_CAST(UINT16, extents->left - appWindow->x),
415 .top = WINPR_ASSERTING_INT_CAST(UINT16, extents->top - appWindow->y),
416 .right = WINPR_ASSERTING_INT_CAST(UINT16, extents->right - appWindow->x),
417 .bottom = WINPR_ASSERTING_INT_CAST(UINT16, extents->bottom - appWindow->y)
420 xf_UpdateWindowArea(xfc, appWindow, updateRect.left, updateRect.top,
421 updateRect.right - updateRect.left, updateRect.bottom - updateRect.top);
423 region16_uninit(&windowInvalidRegion);
424 xf_rail_return_window(appWindow, FALSE);
429static BOOL rail_paint_fn(
const void* pvkey, WINPR_ATTR_UNUSED
void* value,
void* pvarg)
431 rail_paint_fn_arg_t* arg = pvarg;
435 const UINT64 key = *(
const UINT64*)pvkey;
436 return xf_rail_paint_surface(arg->xfc, key, arg->rect);
439BOOL xf_rail_paint(xfContext* xfc,
const RECTANGLE_16* rect)
441 rail_paint_fn_arg_t arg = { .xfc = xfc, .rect = rect };
446 if (!xfc->railWindows)
449 return HashTable_Foreach(xfc->railWindows, rail_paint_fn, &arg);
452#define window_state_log_style(log, windowState) \
453 window_state_log_style_int((log), (windowState), __FILE__, __func__, __LINE__)
454static void window_state_log_style_int(wLog* log,
const WINDOW_STATE_ORDER* windowState,
455 const char* file,
const char* fkt,
size_t line)
457 const DWORD log_level = WLOG_DEBUG;
460 WINPR_ASSERT(windowState);
461 if (WLog_IsLevelActive(log, log_level))
463 char buffer1[128] = WINPR_C_ARRAY_INIT;
464 char buffer2[128] = WINPR_C_ARRAY_INIT;
466 window_styles_to_string(windowState->style, buffer1,
sizeof(buffer1));
467 window_styles_ex_to_string(windowState->extendedStyle, buffer2,
sizeof(buffer2));
468 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"windowStyle={%s, %s}", buffer1,
475static BOOL xf_rail_window_common(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
479 xfContext* xfc = (xfContext*)context;
482 WINPR_ASSERT(orderInfo);
483 WINPR_ASSERT(windowState);
485 UINT32 fieldFlags = orderInfo->fieldFlags;
486 BOOL position_or_size_updated = FALSE;
487 xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
489 if (fieldFlags & WINDOW_ORDER_STATE_NEW)
492 appWindow = xf_rail_add_window(xfc, orderInfo->windowId, windowState->windowOffsetX,
493 windowState->windowOffsetY, windowState->windowWidth,
494 windowState->windowHeight, 0xFFFFFFFF);
499 appWindow->dwStyle = windowState->style;
500 appWindow->dwExStyle = windowState->extendedStyle;
501 window_state_log_style(xfc->log, windowState);
504 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
506 char* title = rail_string_to_utf8_string(&windowState->titleInfo);
509 WLog_Print(xfc->log, WLOG_ERROR,
"failed to duplicate window title string");
513 appWindow->title = title;
517 if (!(appWindow->title = _strdup(
"RdpRailWindow")))
518 WLog_Print(xfc->log, WLOG_ERROR,
"failed to duplicate default window title string");
521 if (!appWindow->title)
524 xf_AppWindowInit(xfc, appWindow);
531 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) ||
532 (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE) ||
533 (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET) ||
534 (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE) ||
535 (fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA) ||
536 (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET) ||
537 (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY))
539 position_or_size_updated = TRUE;
544 if (fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET)
546 appWindow->windowOffsetX = windowState->windowOffsetX;
547 appWindow->windowOffsetY = windowState->windowOffsetY;
550 if (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE)
552 appWindow->windowWidth = windowState->windowWidth;
553 appWindow->windowHeight = windowState->windowHeight;
556 if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X)
558 appWindow->resizeMarginLeft = windowState->resizeMarginLeft;
559 appWindow->resizeMarginRight = windowState->resizeMarginRight;
562 if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y)
564 appWindow->resizeMarginTop = windowState->resizeMarginTop;
565 appWindow->resizeMarginBottom = windowState->resizeMarginBottom;
568 if (fieldFlags & WINDOW_ORDER_FIELD_OWNER)
570 appWindow->ownerWindowId = windowState->ownerWindowId;
573 if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
575 appWindow->dwStyle = windowState->style;
576 appWindow->dwExStyle = windowState->extendedStyle;
577 window_state_log_style(xfc->log, windowState);
580 if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
582 appWindow->showState = windowState->showState;
585 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
587 char* title = rail_string_to_utf8_string(&windowState->titleInfo);
590 WLog_Print(xfc->log, WLOG_ERROR,
"failed to duplicate window title string");
594 free(appWindow->title);
595 appWindow->title = title;
598 if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET)
600 appWindow->clientOffsetX = windowState->clientOffsetX;
601 appWindow->clientOffsetY = windowState->clientOffsetY;
604 if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE)
606 appWindow->clientAreaWidth = windowState->clientAreaWidth;
607 appWindow->clientAreaHeight = windowState->clientAreaHeight;
610 if (fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA)
612 appWindow->windowClientDeltaX = windowState->windowClientDeltaX;
613 appWindow->windowClientDeltaY = windowState->windowClientDeltaY;
616 if (fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS)
618 if (appWindow->windowRects)
620 free(appWindow->windowRects);
621 appWindow->windowRects =
nullptr;
624 appWindow->numWindowRects = windowState->numWindowRects;
626 if (appWindow->numWindowRects)
628 appWindow->windowRects =
631 if (!appWindow->windowRects)
634 CopyMemory(appWindow->windowRects, windowState->windowRects,
639 if (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET)
641 appWindow->visibleOffsetX = windowState->visibleOffsetX;
642 appWindow->visibleOffsetY = windowState->visibleOffsetY;
645 if (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY)
647 if (appWindow->visibilityRects)
649 free(appWindow->visibilityRects);
650 appWindow->visibilityRects =
nullptr;
653 appWindow->numVisibilityRects = windowState->numVisibilityRects;
655 if (appWindow->numVisibilityRects)
657 appWindow->visibilityRects =
660 if (!appWindow->visibilityRects)
663 CopyMemory(appWindow->visibilityRects, windowState->visibilityRects,
670 if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
674 if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
676 xf_ShowWindow(xfc, appWindow, WINPR_ASSERTING_INT_CAST(UINT8, appWindow->showState));
679 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
681 if (appWindow->title)
682 xf_SetWindowText(xfc, appWindow, appWindow->title);
685 if (position_or_size_updated)
687 const INT32 visibilityRectsOffsetX =
688 (appWindow->visibleOffsetX -
689 (appWindow->clientOffsetX - appWindow->windowClientDeltaX));
690 const INT32 visibilityRectsOffsetY =
691 (appWindow->visibleOffsetY -
692 (appWindow->clientOffsetY - appWindow->windowClientDeltaY));
699 if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
701 const BOOL maximized = (appWindow->dwStyle & WS_MAXIMIZE) != 0;
703 if (maximized || (appWindow->x == (INT64)appWindow->windowOffsetX &&
704 appWindow->y == (INT64)appWindow->windowOffsetY &&
705 appWindow->width == (INT64)appWindow->windowWidth &&
706 appWindow->height == (INT64)appWindow->windowHeight))
708 xf_UpdateWindowArea(xfc, appWindow, 0, 0,
709 WINPR_ASSERTING_INT_CAST(
int, appWindow->windowWidth),
710 WINPR_ASSERTING_INT_CAST(
int, appWindow->windowHeight));
714 xf_MoveWindow(xfc, appWindow, appWindow->windowOffsetX, appWindow->windowOffsetY,
715 WINPR_ASSERTING_INT_CAST(
int, appWindow->windowWidth),
716 WINPR_ASSERTING_INT_CAST(
int, appWindow->windowHeight));
721 xf_ClearWindowVisibilityRects(xfc, appWindow);
723 xf_SetWindowVisibilityRects(
724 xfc, appWindow, WINPR_ASSERTING_INT_CAST(uint32_t, visibilityRectsOffsetX),
725 WINPR_ASSERTING_INT_CAST(uint32_t, visibilityRectsOffsetY),
726 appWindow->visibilityRects,
727 WINPR_ASSERTING_INT_CAST(
int, appWindow->numVisibilityRects));
730 if (appWindow->rail_state == WINDOW_SHOW_MAXIMIZED)
732 xf_SendClientEvent(xfc, appWindow->handle, xfc->NET_WM_STATE, 4, NET_WM_STATE_ADD,
733 xfc->NET_WM_STATE_MAXIMIZED_VERT, xfc->NET_WM_STATE_MAXIMIZED_HORZ,
738 if (fieldFlags & (WINDOW_ORDER_STATE_NEW | WINDOW_ORDER_FIELD_STYLE))
739 xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
748 xf_rail_return_window(appWindow, FALSE);
753static BOOL xf_rail_window_delete(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo)
755 xfContext* xfc = (xfContext*)context;
757 return xf_rail_del_window(xfc, orderInfo->windowId);
760static void RailIconCache_Free(xfRailIconCache* cache)
765 for (UINT32 i = 0; i < cache->numCaches * cache->numCacheEntries; i++)
767 xfRailIcon* cur = &cache->entries[i];
771 free(cache->scratch.data);
772 free(cache->entries);
776WINPR_ATTR_MALLOC(RailIconCache_Free, 1)
777static xfRailIconCache* RailIconCache_New(wLog* log, rdpSettings* settings)
779 xfRailIconCache* cache = calloc(1,
sizeof(xfRailIconCache));
785 cache->numCacheEntries =
787 cache->entries = calloc(1ull * cache->numCaches * cache->numCacheEntries,
sizeof(xfRailIcon));
791 WLog_Print(log, WLOG_ERROR,
792 "failed to allocate icon cache %" PRIu32
" x %" PRIu32
" entries",
793 cache->numCaches, cache->numCacheEntries);
802static xfRailIcon* RailIconCache_Lookup(xfRailIconCache* cache, UINT8 cacheId, UINT16 cacheEntry)
815 return &cache->scratch;
817 if (cacheId >= cache->numCaches)
820 if (cacheEntry >= cache->numCacheEntries)
823 return &cache->entries[cache->numCacheEntries * cacheId + cacheEntry];
837static BOOL convert_rail_icon(
const ICON_INFO* iconInfo, xfRailIcon* railIcon)
839 WINPR_ASSERT(iconInfo);
840 WINPR_ASSERT(railIcon);
842 BYTE* nextPixel =
nullptr;
843 long* pixels =
nullptr;
844 BYTE* argbPixels = calloc(1ull * iconInfo->width * iconInfo->height, 4);
849 if (!freerdp_image_copy_from_icon_data(
850 argbPixels, PIXEL_FORMAT_ARGB32, 0, 0, 0,
851 WINPR_ASSERTING_INT_CAST(UINT16, iconInfo->width),
852 WINPR_ASSERTING_INT_CAST(UINT16, iconInfo->height), iconInfo->bitsColor,
853 WINPR_ASSERTING_INT_CAST(UINT16, iconInfo->cbBitsColor), iconInfo->bitsMask,
854 WINPR_ASSERTING_INT_CAST(UINT16, iconInfo->cbBitsMask), iconInfo->colorTable,
855 WINPR_ASSERTING_INT_CAST(UINT16, iconInfo->cbColorTable), iconInfo->bpp))
859 const UINT32 nelements = 2 + iconInfo->width * iconInfo->height;
860 pixels = realloc(railIcon->data, nelements *
sizeof(
long));
865 railIcon->data = pixels;
867 railIcon->length = WINPR_ASSERTING_INT_CAST(
int, nelements);
868 pixels[0] = iconInfo->width;
869 pixels[1] = iconInfo->height;
870 nextPixel = argbPixels;
872 for (UINT32 i = 2; i < nelements; i++)
874 pixels[i] = FreeRDPReadColor(nextPixel, PIXEL_FORMAT_BGRA32);
886static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfRailIcon* icon,
891 LogDynAndXChangeProperty(xfc->log, xfc->display, railWindow->handle, xfc->NET_WM_ICON,
892 XA_CARDINAL, 32, replace ? PropModeReplace : PropModeAppend,
893 (unsigned char*)icon->data, icon->length);
894 LogDynAndXFlush(xfc->log, xfc->display);
898static BOOL xf_rail_window_icon(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
902 xfContext* xfc = (xfContext*)context;
903 BOOL replaceIcon = 0;
904 xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
909 WINPR_ASSERT(windowIcon);
910 WINPR_ASSERT(windowIcon->iconInfo);
911 xfRailIcon* icon = RailIconCache_Lookup(
912 xfc->railIconCache, WINPR_ASSERTING_INT_CAST(UINT8, windowIcon->iconInfo->cacheId),
913 WINPR_ASSERTING_INT_CAST(UINT16, windowIcon->iconInfo->cacheEntry));
917 WLog_Print(xfc->log, WLOG_WARN,
"failed to get icon from cache %02X:%04X",
918 windowIcon->iconInfo->cacheId, windowIcon->iconInfo->cacheEntry);
920 else if (!convert_rail_icon(windowIcon->iconInfo, icon))
922 WLog_Print(xfc->log, WLOG_WARN,
"failed to convert icon for window %08X",
923 orderInfo->windowId);
927 replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
928 xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
931 xf_rail_return_window(railWindow, FALSE);
936static BOOL xf_rail_window_cached_icon(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
940 xfContext* xfc = (xfContext*)context;
941 WINPR_ASSERT(orderInfo);
943 BOOL replaceIcon = 0;
944 xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
949 WINPR_ASSERT(windowCachedIcon);
951 xfRailIcon* icon = RailIconCache_Lookup(
952 xfc->railIconCache, WINPR_ASSERTING_INT_CAST(UINT8, windowCachedIcon->cachedIcon.cacheId),
953 WINPR_ASSERTING_INT_CAST(UINT16, windowCachedIcon->cachedIcon.cacheEntry));
957 WLog_Print(xfc->log, WLOG_WARN,
"failed to get icon from cache %02X:%04X",
958 windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry);
962 replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
963 xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
966 xf_rail_return_window(railWindow, FALSE);
972xf_rail_notify_icon_common(WINPR_ATTR_UNUSED rdpContext* context,
976 xfContext* xfc = (xfContext*)context;
979 WLog_Print(xfc->log, WLOG_ERROR,
"TODO: implement");
980 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION)
984 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_TIP)
988 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_INFO_TIP)
992 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_STATE)
996 if (orderInfo->fieldFlags & WINDOW_ORDER_ICON)
1000 if (orderInfo->fieldFlags & WINDOW_ORDER_CACHED_ICON)
1008static BOOL xf_rail_notify_icon_create(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1011 return xf_rail_notify_icon_common(context, orderInfo, notifyIconState);
1015static BOOL xf_rail_notify_icon_update(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1018 return xf_rail_notify_icon_common(context, orderInfo, notifyIconState);
1022static BOOL xf_rail_notify_icon_delete(WINPR_ATTR_UNUSED rdpContext* context,
1025 xfContext* xfc = (xfContext*)context;
1028 WLog_Print(xfc->log, WLOG_ERROR,
"TODO: implement");
1034xf_rail_monitored_desktop(WINPR_ATTR_UNUSED rdpContext* context,
1038 const UINT32 mask = WINDOW_ORDER_TYPE_DESKTOP | WINDOW_ORDER_FIELD_DESKTOP_HOOKED |
1039 WINDOW_ORDER_FIELD_DESKTOP_ARC_BEGAN |
1040 WINDOW_ORDER_FIELD_DESKTOP_ARC_COMPLETED |
1041 WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND | WINDOW_ORDER_FIELD_DESKTOP_ZORDER;
1042 xfContext* xfc = (xfContext*)context;
1044 if (!context || !orderInfo || !monitoredDesktop)
1047 if ((orderInfo->fieldFlags & WINDOW_ORDER_TYPE_DESKTOP) == 0)
1049 WLog_Print(xfc->log, WLOG_WARN,
"WINDOW_ORDER_TYPE_DESKTOP flag missing!");
1053 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ARC_BEGAN) &&
1054 (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_HOOKED))
1057 WLog_Print(xfc->log, WLOG_WARN,
1058 "TODO: implement WINDOW_ORDER_FIELD_DESKTOP_ARC_BEGAN && "
1059 "WINDOW_ORDER_FIELD_DESKTOP_HOOKED");
1061 else if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_HOOKED)
1063 WLog_Print(xfc->log, WLOG_WARN,
"TODO: implement WINDOW_ORDER_FIELD_DESKTOP_HOOKED");
1065 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ARC_COMPLETED)
1067 WLog_Print(xfc->log, WLOG_DEBUG,
1068 "WINDOW_ORDER_FIELD_DESKTOP_ARC_COMPLETED -> switch to RAILS mode");
1069 if (!xf_rail_enable_remoteapp_mode(xfc))
1072 if (!xf_rail_select_workarea_events(xfc))
1077 if ((app !=
nullptr) && (strnlen(app, 1) > 0))
1079 if (client_rail_server_start_cmd(xfc->rail) != CHANNEL_RC_OK)
1081 if (xf_rail_send_workarea(xfc) != CHANNEL_RC_OK)
1085 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND)
1087 WLog_Print(xfc->log, WLOG_WARN,
"TODO: implement WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND");
1089 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ZORDER)
1091 WLog_Print(xfc->log, WLOG_WARN,
"TODO: implement WINDOW_ORDER_FIELD_DESKTOP_ZORDER");
1093 if (orderInfo->fieldFlags & ~mask)
1095 WLog_Print(xfc->log, WLOG_WARN,
"unknown flags 0x%08" PRIx32
"!", orderInfo->fieldFlags);
1101static BOOL xf_rail_non_monitored_desktop(rdpContext* context,
1104 xfContext* xfc = (xfContext*)context;
1105 const UINT32 mask = WINDOW_ORDER_TYPE_DESKTOP | WINDOW_ORDER_FIELD_DESKTOP_NONE;
1107 if (!context || !orderInfo)
1110 if ((orderInfo->fieldFlags & WINDOW_ORDER_TYPE_DESKTOP) == 0)
1112 WLog_Print(xfc->log, WLOG_WARN,
"TODO: implement WINDOW_ORDER_TYPE_DESKTOP");
1115 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_NONE)
1117 WLog_Print(xfc->log, WLOG_WARN,
"TODO: implement WINDOW_ORDER_FIELD_DESKTOP_NONE");
1119 if (orderInfo->fieldFlags & ~mask)
1121 WLog_Print(xfc->log, WLOG_WARN,
"unknown flags 0x%08" PRIx32
"!", orderInfo->fieldFlags);
1124 return xf_rail_disable_remoteapp_mode(xfc);
1127static void xf_rail_register_update_callbacks(rdpUpdate* update)
1129 WINPR_ASSERT(update);
1131 rdpWindowUpdate* window = update->window;
1132 WINPR_ASSERT(window);
1134 window->WindowCreate = xf_rail_window_common;
1135 window->WindowUpdate = xf_rail_window_common;
1136 window->WindowDelete = xf_rail_window_delete;
1137 window->WindowIcon = xf_rail_window_icon;
1138 window->WindowCachedIcon = xf_rail_window_cached_icon;
1139 window->NotifyIconCreate = xf_rail_notify_icon_create;
1140 window->NotifyIconUpdate = xf_rail_notify_icon_update;
1141 window->NotifyIconDelete = xf_rail_notify_icon_delete;
1142 window->MonitoredDesktop = xf_rail_monitored_desktop;
1143 window->NonMonitoredDesktop = xf_rail_non_monitored_desktop;
1154static UINT xf_rail_server_execute_result(RailClientContext* context,
1157 WINPR_ASSERT(context);
1158 WINPR_ASSERT(execResult);
1160 xfContext* xfc = (xfContext*)context->custom;
1163 if (execResult->execResult != RAIL_EXEC_S_OK)
1166 xfc->log, WLOG_ERROR,
"RAIL exec error: execResult=%s [0x%08" PRIx32
"] NtError=0x%X\n",
1167 error_code2str(execResult->execResult), execResult->execResult, execResult->rawResult);
1168 freerdp_abort_connect_context(&xfc->common.context);
1171 return CHANNEL_RC_OK;
1180static UINT xf_rail_server_system_param(WINPR_ATTR_UNUSED RailClientContext* context,
1183 WINPR_ASSERT(context);
1185 xfContext* xfc = (xfContext*)context->custom;
1189 WLog_Print(xfc->log, WLOG_ERROR,
"TODO: implement");
1190 return CHANNEL_RC_OK;
1199static UINT xf_rail_server_local_move_size(RailClientContext* context,
1205 Window child_window = 0;
1206 WINPR_ASSERT(context);
1207 WINPR_ASSERT(localMoveSize);
1209 xfContext* xfc = (xfContext*)context->custom;
1210 xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId, FALSE);
1213 return ERROR_INTERNAL_ERROR;
1215 WLog_Print(xfc->log, WLOG_TRACE,
"%s [0x%08" PRIx32
"]",
1216 movetype2str(localMoveSize->moveSizeType), localMoveSize->moveSizeType);
1217 switch (localMoveSize->moveSizeType)
1219 case RAIL_WMSZ_LEFT:
1220 direction = NET_WM_MOVERESIZE_SIZE_LEFT;
1221 x = localMoveSize->posX;
1222 y = localMoveSize->posY;
1225 case RAIL_WMSZ_RIGHT:
1226 direction = NET_WM_MOVERESIZE_SIZE_RIGHT;
1227 x = localMoveSize->posX;
1228 y = localMoveSize->posY;
1232 direction = NET_WM_MOVERESIZE_SIZE_TOP;
1233 x = localMoveSize->posX;
1234 y = localMoveSize->posY;
1237 case RAIL_WMSZ_TOPLEFT:
1238 direction = NET_WM_MOVERESIZE_SIZE_TOPLEFT;
1239 x = localMoveSize->posX;
1240 y = localMoveSize->posY;
1243 case RAIL_WMSZ_TOPRIGHT:
1244 direction = NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
1245 x = localMoveSize->posX;
1246 y = localMoveSize->posY;
1249 case RAIL_WMSZ_BOTTOM:
1250 direction = NET_WM_MOVERESIZE_SIZE_BOTTOM;
1251 x = localMoveSize->posX;
1252 y = localMoveSize->posY;
1255 case RAIL_WMSZ_BOTTOMLEFT:
1256 direction = NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
1257 x = localMoveSize->posX;
1258 y = localMoveSize->posY;
1261 case RAIL_WMSZ_BOTTOMRIGHT:
1262 direction = NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
1263 x = localMoveSize->posX;
1264 y = localMoveSize->posY;
1267 case RAIL_WMSZ_MOVE:
1268 direction = NET_WM_MOVERESIZE_MOVE;
1269 XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
1270 localMoveSize->posX, localMoveSize->posY, &x, &y, &child_window);
1273 case RAIL_WMSZ_KEYMOVE:
1274 direction = NET_WM_MOVERESIZE_MOVE_KEYBOARD;
1275 x = localMoveSize->posX;
1276 y = localMoveSize->posY;
1280 case RAIL_WMSZ_KEYSIZE:
1281 direction = NET_WM_MOVERESIZE_SIZE_KEYBOARD;
1282 x = localMoveSize->posX;
1283 y = localMoveSize->posY;
1290 if (localMoveSize->isMoveSizeStart)
1291 xf_StartLocalMoveSize(xfc, appWindow, direction, x, y);
1293 xf_EndLocalMoveSize(xfc, appWindow);
1295 xf_rail_return_window(appWindow, FALSE);
1296 return CHANNEL_RC_OK;
1305static UINT xf_rail_server_min_max_info(RailClientContext* context,
1308 WINPR_ASSERT(context);
1309 WINPR_ASSERT(minMaxInfo);
1311 xfContext* xfc = (xfContext*)context->custom;
1312 xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId, FALSE);
1316 xf_SetWindowMinMaxInfo(xfc, appWindow, minMaxInfo->maxWidth, minMaxInfo->maxHeight,
1317 minMaxInfo->maxPosX, minMaxInfo->maxPosY, minMaxInfo->minTrackWidth,
1318 minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
1319 minMaxInfo->maxTrackHeight);
1321 xf_rail_return_window(appWindow, FALSE);
1323 return CHANNEL_RC_OK;
1333xf_rail_server_language_bar_info(WINPR_ATTR_UNUSED RailClientContext* context,
1336 WINPR_ASSERT(context);
1338 xfContext* xfc = (xfContext*)context->custom;
1341 WLog_Print(xfc->log, WLOG_ERROR,
"TODO: implement");
1342 return CHANNEL_RC_OK;
1352xf_rail_server_get_appid_response(WINPR_ATTR_UNUSED RailClientContext* context,
1355 WINPR_ASSERT(context);
1357 xfContext* xfc = (xfContext*)context->custom;
1360 WLog_Print(xfc->log, WLOG_ERROR,
"TODO: implement");
1361 return CHANNEL_RC_OK;
1365static BOOL rail_window_key_equals(
const void* key1,
const void* key2)
1367 const UINT64* k1 = (
const UINT64*)key1;
1368 const UINT64* k2 = (
const UINT64*)key2;
1377static UINT32 rail_window_key_hash(
const void* key)
1379 const UINT64* k1 = (
const UINT64*)key;
1383static void rail_window_free(
void* value)
1385 xfAppWindow* appWindow = (xfAppWindow*)value;
1390 xf_DestroyWindow(appWindow->xfc, appWindow);
1393int xf_rail_init(xfContext* xfc, RailClientContext* rail)
1395 rdpContext* context = (rdpContext*)xfc;
1401 xf_rail_register_update_callbacks(context->update);
1402 rail->custom = (
void*)xfc;
1403 rail->ServerExecuteResult = xf_rail_server_execute_result;
1404 rail->ServerSystemParam = xf_rail_server_system_param;
1405 rail->ServerLocalMoveSize = xf_rail_server_local_move_size;
1406 rail->ServerMinMaxInfo = xf_rail_server_min_max_info;
1407 rail->ServerLanguageBarInfo = xf_rail_server_language_bar_info;
1408 rail->ServerGetAppIdResponse = xf_rail_server_get_appid_response;
1409 xfc->railWindows = HashTable_New(TRUE);
1411 if (!xfc->railWindows)
1414 if (!HashTable_SetHashFunction(xfc->railWindows, rail_window_key_hash))
1417 wObject* obj = HashTable_KeyObject(xfc->railWindows);
1421 wObject* obj = HashTable_ValueObject(xfc->railWindows);
1424 xfc->railIconCache = RailIconCache_New(xfc->log, xfc->common.context.settings);
1426 if (!xfc->railIconCache)
1432 HashTable_Free(xfc->railWindows);
1436int xf_rail_uninit(xfContext* xfc, RailClientContext* rail)
1442 xfc->rail->custom =
nullptr;
1443 xfc->rail =
nullptr;
1446 if (xfc->railWindows)
1448 HashTable_Free(xfc->railWindows);
1449 xfc->railWindows =
nullptr;
1452 if (xfc->railIconCache)
1454 RailIconCache_Free(xfc->railIconCache);
1455 xfc->railIconCache =
nullptr;
1461xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64
id, INT32 x, INT32 y, UINT32 width,
1462 UINT32 height, UINT32 surfaceId)
1467 xfAppWindow* appWindow = (xfAppWindow*)calloc(1,
sizeof(xfAppWindow));
1472 appWindow->xfc = xfc;
1473 appWindow->windowId = id;
1474 appWindow->surfaceId = surfaceId;
1477 appWindow->width = WINPR_ASSERTING_INT_CAST(
int, width);
1478 appWindow->height = WINPR_ASSERTING_INT_CAST(
int, height);
1480 xf_AppWindowsLock(xfc);
1481 if (!xf_AppWindowCreate(xfc, appWindow))
1484 if (!HashTable_Insert(xfc->railWindows, &appWindow->windowId, (
void*)appWindow))
1488 rail_window_free(appWindow);
1489 xf_AppWindowsUnlock(xfc);
1493BOOL xf_rail_del_window(xfContext* xfc, UINT64
id)
1498 if (!xfc->railWindows)
1502 const BOOL res = HashTable_Remove(xfc->railWindows, &
id);
1507void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked,
const char* file,
1508 const char* fkt,
size_t line)
1516 xfAppWindowsUnlockFrom(window->xfc, file, fkt, line);
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.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals