From 58409406afe7c2a8a71ed2dc8e22075be4f41c0c Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 12:58:42 +0100 Subject: [PATCH 1/8] [client,X11] fix clipboard update Synchronize channel thread with RDP thread when accessing clipboard formats --- client/X11/xf_cliprdr.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index 39b082e2f..bc4cb753f 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -869,7 +869,11 @@ static void xf_clipboard_formats_free(xfClipboard* clipboard) { WINPR_ASSERT(clipboard); + /* Synchronize RDP/X11 thread with channel thread */ + xf_lock_x11(clipboard->xfc); xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); + xf_unlock_x11(clipboard->xfc); + clipboard->lastSentFormats = NULL; clipboard->lastSentNumFormats = 0; } @@ -1867,24 +1871,23 @@ static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard, static UINT xf_cliprdr_monitor_ready(CliprdrClientContext* context, const CLIPRDR_MONITOR_READY* monitorReady) { - UINT ret = 0; - xfClipboard* clipboard = NULL; - WINPR_ASSERT(context); WINPR_ASSERT(monitorReady); - clipboard = cliprdr_file_context_get_context(context->custom); + xfClipboard* clipboard = cliprdr_file_context_get_context(context->custom); WINPR_ASSERT(clipboard); WINPR_UNUSED(monitorReady); - if ((ret = xf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK) + const UINT ret = xf_cliprdr_send_client_capabilities(clipboard); + if (ret != CHANNEL_RC_OK) return ret; xf_clipboard_formats_free(clipboard); - if ((ret = xf_cliprdr_send_client_format_list(clipboard, TRUE)) != CHANNEL_RC_OK) - return ret; + const UINT ret2 = xf_cliprdr_send_client_format_list(clipboard, TRUE); + if (ret2 != CHANNEL_RC_OK) + return ret2; clipboard->sync = TRUE; return CHANNEL_RC_OK; From 2e3b77e28ac6a398897d28ba464dcc5dfab9c9e2 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 13:18:51 +0100 Subject: [PATCH 2/8] [channels,rdpgfx] check available stream length --- channels/rdpgfx/client/rdpgfx_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/channels/rdpgfx/client/rdpgfx_main.c b/channels/rdpgfx/client/rdpgfx_main.c index f2483ad01..74690e822 100644 --- a/channels/rdpgfx/client/rdpgfx_main.c +++ b/channels/rdpgfx/client/rdpgfx_main.c @@ -1306,7 +1306,8 @@ static UINT rdpgfx_recv_wire_to_surface_2_pdu(GENERIC_CHANNEL_CALLBACK* callback Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */ Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */ pdu.bitmapData = Stream_Pointer(s); - Stream_Seek(s, pdu.bitmapDataLength); + if (!Stream_SafeSeek(s, pdu.bitmapDataLength)) + return ERROR_INVALID_DATA; DEBUG_RDPGFX(gfx->log, "RecvWireToSurface2Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 ") " From 9362a0bf8dda04eedbca07d5dfaec1044e67cc6b Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 13:39:28 +0100 Subject: [PATCH 3/8] [client,x11] stringfiy functions for RAILS --- client/X11/xf_rail.c | 66 ++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 7d3869a65..7c7f1a3a5 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -37,22 +37,51 @@ #include #define TAG CLIENT_TAG("x11") -static const char* error_code_names[] = { "RAIL_EXEC_S_OK", - "RAIL_EXEC_E_HOOK_NOT_LOADED", - "RAIL_EXEC_E_DECODE_FAILED", - "RAIL_EXEC_E_NOT_IN_ALLOWLIST", - "RAIL_EXEC_E_FILE_NOT_FOUND", - "RAIL_EXEC_E_FAIL", - "RAIL_EXEC_E_SESSION_LOCKED" }; +static const char* error_code2str(UINT32 code) +{ +#define EVCASE(x) \ + case x: \ + return #x + switch (code) + { + EVCASE(RAIL_EXEC_S_OK); + EVCASE(RAIL_EXEC_E_HOOK_NOT_LOADED); + EVCASE(RAIL_EXEC_E_DECODE_FAILED); + EVCASE(RAIL_EXEC_E_NOT_IN_ALLOWLIST); + EVCASE(RAIL_EXEC_E_FILE_NOT_FOUND); + EVCASE(RAIL_EXEC_E_FAIL); + EVCASE(RAIL_EXEC_E_SESSION_LOCKED); + default: + return "RAIL_EXEC_E_UNKNOWN"; + } +#undef EVCASE +} -#ifdef WITH_DEBUG_RAIL -static const char* movetype_names[] = { - "(invalid)", "RAIL_WMSZ_LEFT", "RAIL_WMSZ_RIGHT", - "RAIL_WMSZ_TOP", "RAIL_WMSZ_TOPLEFT", "RAIL_WMSZ_TOPRIGHT", - "RAIL_WMSZ_BOTTOM", "RAIL_WMSZ_BOTTOMLEFT", "RAIL_WMSZ_BOTTOMRIGHT", - "RAIL_WMSZ_MOVE", "RAIL_WMSZ_KEYMOVE", "RAIL_WMSZ_KEYSIZE" -}; -#endif +static const char* movetype2str(UINT32 code) +{ +#define EVCASE(x) \ + case x: \ + return #x + + switch (code) + { + + EVCASE(RAIL_WMSZ_LEFT); + EVCASE(RAIL_WMSZ_RIGHT); + EVCASE(RAIL_WMSZ_TOP); + EVCASE(RAIL_WMSZ_TOPLEFT); + EVCASE(RAIL_WMSZ_TOPRIGHT); + EVCASE(RAIL_WMSZ_BOTTOM); + EVCASE(RAIL_WMSZ_BOTTOMLEFT); + EVCASE(RAIL_WMSZ_BOTTOMRIGHT); + EVCASE(RAIL_WMSZ_MOVE); + EVCASE(RAIL_WMSZ_KEYMOVE); + EVCASE(RAIL_WMSZ_KEYSIZE); + default: + return "RAIL_WMSZ_INVALID"; + } +#undef EVCASE +} struct xf_rail_icon { @@ -1013,8 +1042,9 @@ static UINT xf_rail_server_execute_result(RailClientContext* context, if (execResult->execResult != RAIL_EXEC_S_OK) { - WLog_ERR(TAG, "RAIL exec error: execResult=%s NtError=0x%X\n", - error_code_names[execResult->execResult], execResult->rawResult); + WLog_ERR(TAG, "RAIL exec error: execResult=%s [0x%08" PRIx32 "] NtError=0x%X\n", + error_code2str(execResult->execResult), execResult->execResult, + execResult->rawResult); freerdp_abort_connect_context(&xfc->common.context); } @@ -1078,6 +1108,8 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, if (!appWindow) return ERROR_INTERNAL_ERROR; + WLog_Print(xfc->log, WLOG_TRACE, "%s [0x%08" PRIx32 "]", + movetype2str(localMoveSize->moveSizeType), localMoveSize->moveSizeType); switch (localMoveSize->moveSizeType) { case RAIL_WMSZ_LEFT: From b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 15:50:19 +0100 Subject: [PATCH 4/8] [client,x11] fix xf_rail_window_common cleanup leave the appWindow for later cleanup. --- client/X11/xf_rail.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 7c7f1a3a5..b09ef2a25 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -368,7 +368,6 @@ static void window_state_log_style_int(wLog* log, const WINDOW_STATE_ORDER* wind static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, const WINDOW_STATE_ORDER* windowState) { - xfAppWindow* appWindow = NULL; xfContext* xfc = (xfContext*)context; WINPR_ASSERT(xfc); @@ -377,7 +376,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* UINT32 fieldFlags = orderInfo->fieldFlags; BOOL position_or_size_updated = FALSE; - appWindow = xf_rail_get_window(xfc, orderInfo->windowId); + xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId); if (fieldFlags & WINDOW_ORDER_STATE_NEW) { @@ -428,10 +427,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* } if (!appWindow->title) - { - free(appWindow); return FALSE; - } xf_AppWindowInit(xfc, appWindow); } From 1994e9844212a6dfe0ff12309fef520e888986b5 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 16:09:51 +0100 Subject: [PATCH 5/8] [client,x11] lock appWindow When using xf_rail_get_window lock the hash talbe until xf_rail_return_window --- client/X11/xf_event.c | 47 +++++++++++++++++++++++------------ client/X11/xf_graphics.c | 10 +++++--- client/X11/xf_rail.c | 53 ++++++++++++++++++++++++++++------------ client/X11/xf_rail.h | 4 +++ client/X11/xf_window.c | 6 +++++ client/X11/xf_window.h | 4 +++ 6 files changed, 89 insertions(+), 35 deletions(-) diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index ceb0874ab..6e2f51398 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -441,7 +441,9 @@ BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL if (app) { /* make sure window exists */ - if (!xf_AppWindowFromX11Window(xfc, window)) + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); + xf_rail_return_window(appWindow); + if (!appWindow) return TRUE; /* Translate to desktop coordinates */ @@ -547,7 +549,9 @@ BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window wi if (app) { /* make sure window exists */ - if (!xf_AppWindowFromX11Window(xfc, window)) + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); + xf_rail_return_window(appWindow); + if (!appWindow) return TRUE; /* Translate to desktop coordinates */ @@ -708,6 +712,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap */ if (appWindow) xf_rail_adjust_position(xfc, appWindow); + xf_rail_return_window(appWindow); } xf_keyboard_focus_in(xfc); @@ -762,12 +767,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev { if (app) { + BOOL rc = TRUE; xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); if (appWindow) - return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); - - return TRUE; + rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); + xf_rail_return_window(appWindow); + return rc; } else { @@ -800,6 +806,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, /* keep track of which window has focus so that we can apply pointer updates */ xfc->appWindow = appWindow; + xf_rail_return_window(appWindow); } return TRUE; @@ -821,6 +828,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, /* keep track of which window has focus so that we can apply pointer updates */ if (xfc->appWindow == appWindow) xfc->appWindow = NULL; + xf_rail_return_window(appWindow); } return TRUE; } @@ -926,6 +934,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even xf_rail_adjust_position(xfc, appWindow); } } + xf_rail_return_window(appWindow); } return xf_pointer_update_scale(xfc); } @@ -949,6 +958,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); appWindow->is_mapped = TRUE; } + xf_rail_return_window(appWindow); } return TRUE; @@ -963,13 +973,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL xf_keyboard_release_all_keypress(xfc); if (!app) - gdi_send_suppress_output(xfc->common.context.gdi, TRUE); - else + return gdi_send_suppress_output(xfc->common.context.gdi, TRUE); + { xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); if (appWindow) appWindow->is_mapped = FALSE; + xf_rail_return_window(appWindow); } return TRUE; @@ -977,6 +988,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) { + BOOL rc = TRUE; WINPR_ASSERT(xfc); WINPR_ASSERT(event); @@ -1001,7 +1013,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, appWindow = xf_AppWindowFromX11Window(xfc, event->window); if (!appWindow) - return TRUE; + goto fail; } if (event->atom == xfc->NET_WM_STATE) @@ -1073,8 +1085,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) { appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; - return xf_rail_send_client_system_command(xfc, appWindow->windowId, - SC_MAXIMIZE); + rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); } } else if (appWindow->minimized) @@ -1082,8 +1093,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) { appWindow->rail_state = WINDOW_SHOW_MINIMIZED; - return xf_rail_send_client_system_command(xfc, appWindow->windowId, - SC_MINIMIZE); + rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); } } else @@ -1091,15 +1101,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) { appWindow->rail_state = WINDOW_SHOW; - return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); } } } else if (minimizedChanged) - gdi_send_suppress_output(xfc->common.context.gdi, minimized); + rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); + + fail: + xf_rail_return_window(appWindow); } - return TRUE; + return rc; } static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event) @@ -1215,7 +1228,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) /* Update "current" window for cursor change orders */ xfc->appWindow = appWindow; - if (xf_event_suppress_events(xfc, appWindow, event)) + const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); + xf_rail_return_window(appWindow); + if (rc) return TRUE; } } diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index c61466b1a..51cbd0e40 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -253,12 +253,14 @@ static Window xf_Pointer_get_window(xfContext* xfc) } if (xfc->remote_app) { + Window w = 0; + HashTable_Lock(xfc->railWindows); if (!xfc->appWindow) - { WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); - return 0; - } - return xfc->appWindow->handle; + else + w = xfc->appWindow->handle; + HashTable_Unlock(xfc->railWindows); + return w; } else { diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index b09ef2a25..ecc81ca33 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -159,6 +159,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) activate.windowId = (UINT32)appWindow->windowId; activate.enabled = enabled; xfc->rail->ClientActivate(xfc->rail, &activate); + xf_rail_return_window(appWindow); } BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) @@ -316,6 +317,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); } region16_uninit(&windowInvalidRegion); + xf_rail_return_window(appWindow); return TRUE; } @@ -808,6 +810,7 @@ static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfR static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, const WINDOW_ICON_ORDER* windowIcon) { + BOOL rc = FALSE; xfContext* xfc = (xfContext*)context; BOOL replaceIcon = 0; xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId); @@ -825,24 +828,26 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or { WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", windowIcon->iconInfo->cacheId, windowIcon->iconInfo->cacheEntry); - return FALSE; } - - if (!convert_rail_icon(windowIcon->iconInfo, icon)) + else if (!convert_rail_icon(windowIcon->iconInfo, icon)) { WLog_Print(xfc->log, WLOG_WARN, "failed to convert icon for window %08X", orderInfo->windowId); - return FALSE; } - - replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); - xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); - return TRUE; + else + { + replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); + xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); + rc = TRUE; + } + xf_rail_return_window(railWindow); + return rc; } static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, const WINDOW_CACHED_ICON_ORDER* windowCachedIcon) { + BOOL rc = FALSE; xfContext* xfc = (xfContext*)context; WINPR_ASSERT(orderInfo); @@ -862,12 +867,15 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I { WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry); - return FALSE; } - - replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); - xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); - return TRUE; + else + { + replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); + xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); + rc = TRUE; + } + xf_rail_return_window(railWindow); + return rc; } static BOOL @@ -1184,6 +1192,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, else xf_EndLocalMoveSize(xfc, appWindow); + xf_rail_return_window(appWindow); return CHANNEL_RC_OK; } @@ -1208,6 +1217,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context, minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth, minMaxInfo->maxTrackHeight); } + xf_rail_return_window(appWindow); return CHANNEL_RC_OK; } @@ -1381,7 +1391,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id) return NULL; if (!xfc->railWindows) - return FALSE; + return NULL; - return HashTable_GetItemValue(xfc->railWindows, &id); + HashTable_Lock(xfc->railWindows); + xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); + if (!window) + HashTable_Unlock(xfc->railWindows); + + return window; +} + +void xf_rail_return_window(xfAppWindow* window) +{ + if (!window) + return; + + HashTable_Unlock(window->xfc->railWindows); } diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h index 8d2fd7044..0867093a2 100644 --- a/client/X11/xf_rail.h +++ b/client/X11/xf_rail.h @@ -37,6 +37,10 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc); xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, INT32 x, INT32 y, UINT32 width, UINT32 height, UINT32 surfaceId); + +void xf_rail_return_window(xfAppWindow* window); + +WINPR_ATTR_MALLOC(xf_rail_return_window, 1) xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id); BOOL xf_rail_del_window(xfContext* xfc, UINT64 id); diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 9bffa66c5..e276aea85 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -1436,6 +1436,7 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) if (!xfc->railWindows) return NULL; + HashTable_Lock(xfc->railWindows); size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); for (size_t index = 0; index < count; index++) @@ -1444,17 +1445,20 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) if (!appWindow) { + HashTable_Unlock(xfc->railWindows); free(pKeys); return NULL; } if (appWindow->handle == wnd) { + HashTable_Unlock(xfc->railWindows); free(pKeys); return appWindow; } } + HashTable_Unlock(xfc->railWindows); free(pKeys); return NULL; } @@ -1535,6 +1539,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) rc = CHANNEL_RC_OK; fail: + xf_rail_return_window(appWindow); LogDynAndXFlush(xfc->log, xfc->display); xf_unlock_x11(xfc); return rc; @@ -1571,4 +1576,5 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) return; (void)LogDynAndXSetTransientForHint(xfc->log, xfc->display, window->handle, parent->handle); + xf_rail_return_window(parent); } diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index 8a41b4e47..e258a9b26 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -202,6 +202,10 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth int maxTrackWidth, int maxTrackHeight); void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); + +void xf_rail_return_window(xfAppWindow* window); + +WINPR_ATTR_MALLOC(xf_rail_return_window, 1) xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); From 169d358734509e82663a0d6a0085ae726d439d8e Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 16:58:20 +0100 Subject: [PATCH 6/8] [client,x11] destroy XImage on window unmap When unmapping rails window destroy the cached XImage of appWindow --- client/X11/xf_gfx.c | 24 ++++++++++++++++++++++++ client/X11/xf_window.c | 2 +- client/X11/xf_window.h | 8 ++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/client/X11/xf_gfx.c b/client/X11/xf_gfx.c index 37d0bb17b..b6de7eb86 100644 --- a/client/X11/xf_gfx.c +++ b/client/X11/xf_gfx.c @@ -30,6 +30,7 @@ #include "xf_gfx.h" #include "xf_rail.h" #include "xf_utils.h" +#include "xf_window.h" #include @@ -444,6 +445,27 @@ static UINT xf_DeleteSurface(RdpgfxClientContext* context, return status; } +static UINT xf_UnmapWindowForSurface(RdpgfxClientContext* context, UINT64 windowID) +{ + WINPR_ASSERT(context); + rdpGdi* gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + + xfContext* xfc = (xfContext*)gdi->context; + WINPR_ASSERT(gdi->context); + + if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode)) + { + xfAppWindow* appWindow = xf_rail_get_window(xfc, windowID); + if (appWindow) + xf_AppWindowDestroyImage(appWindow); + xf_rail_return_window(appWindow); + } + + WLog_WARN(TAG, "function not implemented"); + return CHANNEL_RC_OK; +} + static UINT xf_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface) { WINPR_ASSERT(context); @@ -482,7 +504,9 @@ void xf_graphics_pipeline_init(xfContext* xfc, RdpgfxClientContext* gfx) gfx->CreateSurface = xf_CreateSurface; gfx->DeleteSurface = xf_DeleteSurface; } + gfx->UpdateWindowFromSurface = xf_UpdateWindowFromSurface; + gfx->UnmapWindowForSurface = xf_UnmapWindowForSurface; } void xf_graphics_pipeline_uninit(xfContext* xfc, RdpgfxClientContext* gfx) diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index e276aea85..b1d589f0d 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -1380,7 +1380,7 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i xf_unlock_x11(xfc); } -static void xf_AppWindowDestroyImage(xfAppWindow* appWindow) +void xf_AppWindowDestroyImage(xfAppWindow* appWindow) { WINPR_ASSERT(appWindow); if (appWindow->image) diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index e258a9b26..26e4292ba 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -167,10 +167,13 @@ void xf_SetWindowMinimized(xfContext* xfc, xfWindow* window); void xf_SetWindowDecorations(xfContext* xfc, Window window, BOOL show); void xf_SetWindowUnlisted(xfContext* xfc, Window window); -xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width, int height); -void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width, int height); void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window); +WINPR_ATTR_MALLOC(xf_DestroyDesktopWindow, 2) +xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width, int height); + +void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width, int height); + Window xf_CreateDummyWindow(xfContext* xfc); void xf_DestroyDummyWindow(xfContext* xfc, Window window); @@ -196,6 +199,7 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i int height); UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface); +void xf_AppWindowDestroyImage(xfAppWindow* appWindow); void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow); void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth, int maxHeight, int maxPosX, int maxPosY, int minTrackWidth, int minTrackHeight, From d3e8b3b9365be96a4f11dda149d71b3287227d0a Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 17:52:37 +0100 Subject: [PATCH 7/8] [client,x11] lock cache when providing data --- client/X11/xf_cliprdr.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index bc4cb753f..db097e84a 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -1576,12 +1576,16 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, DEBUG_CLIPRDR("formatId: 0x%08" PRIx32 ", dstFormatId: 0x%08" PRIx32 "", formatId, dstFormatId); + wHashTable* table = clipboard->cachedData; + if (rawTransfer) + table = clipboard->cachedRawData; + + HashTable_Lock(table); + if (!rawTransfer) - cached_data = HashTable_GetItemValue(clipboard->cachedData, - format_to_cache_slot(dstFormatId)); + cached_data = HashTable_GetItemValue(table, format_to_cache_slot(dstFormatId)); else - cached_data = HashTable_GetItemValue(clipboard->cachedRawData, - format_to_cache_slot(formatId)); + cached_data = HashTable_GetItemValue(table, format_to_cache_slot(formatId)); DEBUG_CLIPRDR("hasCachedData: %u, rawTransfer: %d", cached_data ? 1u : 0u, rawTransfer); @@ -1635,6 +1639,7 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, delayRespond = TRUE; xf_cliprdr_send_data_request(clipboard, formatId, cformat); } + HashTable_Unlock(table); } } From 2679cf6bf8af357b977cf806b6c9604c8753b9a0 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 9 Feb 2026 19:01:48 +0100 Subject: [PATCH 8/8] [client,x11] refactor headers * Use a forward definition in xf_types.h for xfContext * Resolve circular dependencies on xfreerdp.h --- client/X11/CMakeLists.txt | 1 + client/X11/xf_cliprdr.h | 10 +++-- client/X11/xf_disp.c | 1 + client/X11/xf_disp.h | 7 ++- client/X11/xf_floatbar.h | 8 +++- client/X11/xf_monitor.h | 19 +------- client/X11/xf_rail.h | 93 ++++++++++++++++++++++++++++++++++++-- client/X11/xf_types.h | 42 +++++++++++++++++ client/X11/xf_video.h | 7 +-- client/X11/xf_window.h | 95 ++------------------------------------- client/X11/xfreerdp.h | 12 ++--- 11 files changed, 168 insertions(+), 127 deletions(-) create mode 100644 client/X11/xf_types.h diff --git a/client/X11/CMakeLists.txt b/client/X11/CMakeLists.txt index 0be9d634a..24bff5ebd 100644 --- a/client/X11/CMakeLists.txt +++ b/client/X11/CMakeLists.txt @@ -40,6 +40,7 @@ include_directories(SYSTEM ${X11_INCLUDE_DIRS}) include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) set(SRCS + xf_types.h xf_utils.h xf_utils.c xf_x11_utils.c diff --git a/client/X11/xf_cliprdr.h b/client/X11/xf_cliprdr.h index b57779132..3d0a15323 100644 --- a/client/X11/xf_cliprdr.h +++ b/client/X11/xf_cliprdr.h @@ -20,11 +20,15 @@ #ifndef FREERDP_CLIENT_X11_CLIPRDR_H #define FREERDP_CLIENT_X11_CLIPRDR_H -#include "xf_client.h" -#include "xfreerdp.h" - +#include #include +#include + +#include "xf_types.h" + +typedef struct xf_clipboard xfClipboard; + void xf_clipboard_free(xfClipboard* clipboard); WINPR_ATTR_MALLOC(xf_clipboard_free, 1) diff --git a/client/X11/xf_disp.c b/client/X11/xf_disp.c index 045a649d7..2de050d43 100644 --- a/client/X11/xf_disp.c +++ b/client/X11/xf_disp.c @@ -35,6 +35,7 @@ #endif +#include "xfreerdp.h" #include "xf_disp.h" #include "xf_monitor.h" diff --git a/client/X11/xf_disp.h b/client/X11/xf_disp.h index 8c971596c..e28e1e246 100644 --- a/client/X11/xf_disp.h +++ b/client/X11/xf_disp.h @@ -22,8 +22,11 @@ #include #include -#include "xf_client.h" -#include "xfreerdp.h" +#include + +#include "xf_types.h" + +typedef struct s_xfDispContext xfDispContext; FREERDP_API BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp); FREERDP_API BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp); diff --git a/client/X11/xf_floatbar.h b/client/X11/xf_floatbar.h index 1e15ae17f..41cac3784 100644 --- a/client/X11/xf_floatbar.h +++ b/client/X11/xf_floatbar.h @@ -18,9 +18,13 @@ #ifndef FREERDP_CLIENT_X11_FLOATBAR_H #define FREERDP_CLIENT_X11_FLOATBAR_H -typedef struct xf_floatbar xfFloatbar; +#include -#include "xfreerdp.h" +#include + +#include "xf_types.h" + +typedef struct xf_floatbar xfFloatbar; void xf_floatbar_free(xfFloatbar* floatbar); diff --git a/client/X11/xf_monitor.h b/client/X11/xf_monitor.h index 6597c1560..d654b0621 100644 --- a/client/X11/xf_monitor.h +++ b/client/X11/xf_monitor.h @@ -22,24 +22,9 @@ #include #include +#include -typedef struct -{ - RECTANGLE_16 area; - RECTANGLE_16 workarea; - BOOL primary; -} MONITOR_INFO; - -typedef struct -{ - UINT32 nmonitors; - RECTANGLE_16 area; - RECTANGLE_16 workarea; - MONITOR_INFO* monitors; -} VIRTUAL_SCREEN; - -#include "xf_client.h" -#include "xfreerdp.h" +#include "xf_types.h" FREERDP_API int xf_list_monitors(xfContext* xfc); FREERDP_API BOOL xf_detect_monitors(xfContext* xfc, UINT32* pWidth, UINT32* pHeight); diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h index 0867093a2..a91714851 100644 --- a/client/X11/xf_rail.h +++ b/client/X11/xf_rail.h @@ -20,11 +20,98 @@ #ifndef FREERDP_CLIENT_X11_RAIL_H #define FREERDP_CLIENT_X11_RAIL_H -#include "xf_client.h" -#include "xfreerdp.h" - #include +#include +#include + +#include "xf_types.h" + +enum xf_localmove_state +{ + LMS_NOT_ACTIVE, + LMS_STARTING, + LMS_ACTIVE, + LMS_TERMINATING +}; + +struct xf_localmove +{ + int root_x; + int root_y; + int window_x; + int window_y; + enum xf_localmove_state state; + int direction; +}; +typedef struct xf_localmove xfLocalMove; + +struct xf_app_window +{ + xfContext* xfc; + + int x; + int y; + int width; + int height; + char* title; + + UINT32 surfaceId; + UINT64 windowId; + UINT32 ownerWindowId; + + UINT32 dwStyle; + UINT32 dwExStyle; + UINT32 showState; + + INT32 clientOffsetX; + INT32 clientOffsetY; + UINT32 clientAreaWidth; + UINT32 clientAreaHeight; + + INT32 windowOffsetX; + INT32 windowOffsetY; + INT32 windowClientDeltaX; + INT32 windowClientDeltaY; + UINT32 windowWidth; + UINT32 windowHeight; + UINT32 numWindowRects; + RECTANGLE_16* windowRects; + + INT32 visibleOffsetX; + INT32 visibleOffsetY; + UINT32 numVisibilityRects; + RECTANGLE_16* visibilityRects; + + UINT32 localWindowOffsetCorrX; + UINT32 localWindowOffsetCorrY; + + UINT32 resizeMarginLeft; + UINT32 resizeMarginTop; + UINT32 resizeMarginRight; + UINT32 resizeMarginBottom; + + GC gc; + int shmid; + Window handle; + Window* xfwin; + BOOL fullscreen; + BOOL decorations; + BOOL is_mapped; + BOOL is_transient; + xfLocalMove local_move; + BYTE rail_state; + BOOL maxVert; + BOOL maxHorz; + BOOL minimized; + BOOL rail_ignore_configure; + + Pixmap pixmap; + XImage* image; +}; +typedef struct xf_app_window xfAppWindow; +typedef struct xf_rail_icon_cache xfRailIconCache; + BOOL xf_rail_paint(xfContext* xfc, const RECTANGLE_16* rect); BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* rect); diff --git a/client/X11/xf_types.h b/client/X11/xf_types.h new file mode 100644 index 000000000..22072ea2d --- /dev/null +++ b/client/X11/xf_types.h @@ -0,0 +1,42 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * X11 Client + * + * Copyright 2026 Thincast Technologies GmbH + * Copyright 2026 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +/* Forward declarations */ +typedef struct xf_context xfContext; + +typedef struct +{ + RECTANGLE_16 area; + RECTANGLE_16 workarea; + BOOL primary; +} MONITOR_INFO; + +typedef struct +{ + UINT32 nmonitors; + RECTANGLE_16 area; + RECTANGLE_16 workarea; + MONITOR_INFO* monitors; +} VIRTUAL_SCREEN; diff --git a/client/X11/xf_video.h b/client/X11/xf_video.h index d14e7d42a..899cfeed0 100644 --- a/client/X11/xf_video.h +++ b/client/X11/xf_video.h @@ -19,10 +19,11 @@ #ifndef CLIENT_X11_XF_VIDEO_H_ #define CLIENT_X11_XF_VIDEO_H_ -#include "xfreerdp.h" +#include -#include -#include +#include "xf_types.h" + +typedef struct s_xfVideoContext xfVideoContext; void xf_video_control_init(xfContext* xfc, VideoClientContext* video); void xf_video_control_uninit(xfContext* xfc, VideoClientContext* video); diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index 26e4292ba..5761170f8 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -26,14 +26,11 @@ #include #include -typedef struct xf_app_window xfAppWindow; - -typedef struct xf_localmove xfLocalMove; -typedef struct xf_window xfWindow; - -#include "xf_client.h" +#include "xf_types.h" +#include "xf_rail.h" #include "xf_floatbar.h" -#include "xfreerdp.h" + +typedef struct xf_window xfWindow; // Extended ICCM flags http://standards.freedesktop.org/wm-spec/wm-spec-latest.html WINPR_PRAGMA_DIAG_PUSH @@ -58,24 +55,6 @@ WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO WINPR_PRAGMA_DIAG_POP -enum xf_localmove_state -{ - LMS_NOT_ACTIVE, - LMS_STARTING, - LMS_ACTIVE, - LMS_TERMINATING -}; - -struct xf_localmove -{ - int root_x; - int root_y; - int window_x; - int window_y; - enum xf_localmove_state state; - int direction; -}; - struct xf_window { GC gc; @@ -94,70 +73,6 @@ struct xf_window BOOL is_transient; }; -struct xf_app_window -{ - xfContext* xfc; - - int x; - int y; - int width; - int height; - char* title; - - UINT32 surfaceId; - UINT64 windowId; - UINT32 ownerWindowId; - - UINT32 dwStyle; - UINT32 dwExStyle; - UINT32 showState; - - INT32 clientOffsetX; - INT32 clientOffsetY; - UINT32 clientAreaWidth; - UINT32 clientAreaHeight; - - INT32 windowOffsetX; - INT32 windowOffsetY; - INT32 windowClientDeltaX; - INT32 windowClientDeltaY; - UINT32 windowWidth; - UINT32 windowHeight; - UINT32 numWindowRects; - RECTANGLE_16* windowRects; - - INT32 visibleOffsetX; - INT32 visibleOffsetY; - UINT32 numVisibilityRects; - RECTANGLE_16* visibilityRects; - - UINT32 localWindowOffsetCorrX; - UINT32 localWindowOffsetCorrY; - - UINT32 resizeMarginLeft; - UINT32 resizeMarginTop; - UINT32 resizeMarginRight; - UINT32 resizeMarginBottom; - - GC gc; - int shmid; - Window handle; - Window* xfwin; - BOOL fullscreen; - BOOL decorations; - BOOL is_mapped; - BOOL is_transient; - xfLocalMove local_move; - BYTE rail_state; - BOOL maxVert; - BOOL maxHorz; - BOOL minimized; - BOOL rail_ignore_configure; - - Pixmap pixmap; - XImage* image; -}; - void xf_ewmhints_init(xfContext* xfc); BOOL xf_GetWorkArea(xfContext* xfc); @@ -207,8 +122,6 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); -void xf_rail_return_window(xfAppWindow* window); - WINPR_ATTR_MALLOC(xf_rail_return_window, 1) xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h index 2ae744f33..c82da03fb 100644 --- a/client/X11/xfreerdp.h +++ b/client/X11/xfreerdp.h @@ -24,7 +24,11 @@ #include -typedef struct xf_context xfContext; +#include "xf_types.h" +#include "xf_disp.h" +#include "xf_cliprdr.h" +#include "xf_video.h" +#include "xf_rail.h" #ifdef WITH_XCURSOR #include @@ -54,6 +58,7 @@ typedef struct xf_context xfContext; #include #include #include +#include #if !defined(XcursorUInt) typedef unsigned int XcursorUInt; @@ -109,11 +114,6 @@ struct xf_glyph }; typedef struct xf_glyph xfGlyph; -typedef struct xf_clipboard xfClipboard; -typedef struct s_xfDispContext xfDispContext; -typedef struct s_xfVideoContext xfVideoContext; -typedef struct xf_rail_icon_cache xfRailIconCache; - /* Number of buttons that are mapped from X11 to RDP button events. */ #define NUM_BUTTONS_MAPPED 11