24#include <winpr/assert.h>
25#include <winpr/platform.h>
26#include <winpr/cast.h>
28#include <freerdp/freerdp.h>
29#include <freerdp/log.h>
30#include <freerdp/codec/color.h>
31#include <freerdp/codec/region.h>
32#include <freerdp/client/rail.h>
34#include "sdl_rail.hpp"
35#include "sdl_context.hpp"
36#include "sdl_rail_platform.hpp"
37#include "sdl_types.hpp"
38#include "sdl_utils.hpp"
39#include "sdl_wayland.hpp"
42#define TAG CLIENT_TAG("sdl.rail")
44SdlRail::SdlRail(
SdlContext* context) : _context(context)
48SdlRail::~SdlRail() =
default;
50SdlRail* SdlRail::get(rdpContext* context)
52 auto sdl = get_context(context);
55 return &
sdl->getRailChannelContext();
60 auto it = _windows.find(
id);
61 return (it == _windows.end()) ? nullptr : &it->second;
68 for (
auto& it : _windows)
70 if (it.second.sdlId() ==
id)
77static bool adjoinsOutside(
const SDL_Rect& popup,
const SDL_Rect& win)
80 if (SDL_GetRectIntersection(&popup, &win, &hit))
82 const SDL_Rect grown = { win.x - 1, win.y - 1, win.w + 2, win.h + 2 };
83 return SDL_GetRectIntersection(&popup, &grown, &hit);
89 return (!w.isPopup() || w.isFullscreen()) && (w.window() !=
nullptr);
94 auto* owner = getWindow(ownerId);
95 if (owner && canParentPopup(*owner))
107 const SDL_Rect pr = popup.windowRect();
108 const SDL_Point origin = { pr.x, pr.y };
109 for (uint32_t
id : _zOrder)
111 auto* w = getWindow(
id);
112 if (!w || !canParentPopup(*w))
114 const SDL_Rect wr = w->windowRect();
115 if (SDL_PointInRect(&origin, &wr))
123 for (
auto& other : _windows)
125 auto& w = other.second;
126 if (!canParentPopup(w))
128 const SDL_Rect wr = w.windowRect();
129 if (SDL_PointInRect(&origin, &wr))
138 chosen = resolveParent(_focusedAppId);
141 for (
auto& other : _windows)
143 auto& w = other.second;
144 if (canParentPopup(w))
157 if (!popup.isLayered())
159 const SDL_Rect pr = popup.windowRect();
160 for (
auto& other : _windows)
162 auto& w = other.second;
163 if (canParentPopup(w) && adjoinsOutside(pr, w.windowRect()))
169bool SdlRail::ownsWindow(SDL_WindowID
id)
171 std::unique_lock lock(_windowsLock);
172 return getWindowBySdlId(
id) !=
nullptr;
175void SdlRail::invalidateWindow(SDL_WindowID
id)
177 std::unique_lock lock(_windowsLock);
178 auto* appWindow = getWindowBySdlId(
id);
181 appWindow->invalidateAll();
183 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
187void SdlRail::sendSystemCommand(
SdlRailWindow* appWindow, uint16_t command)
189 if (!_rail || !_rail->ClientSystemCommand)
192 syscommand.windowId =
static_cast<UINT32
>(appWindow->id());
193 syscommand.command = command;
194 std::ignore = _rail->ClientSystemCommand(_rail, &syscommand);
197void SdlRail::sendWorkArea(
const SDL_Rect& area)
199 if (!_rail || !_rail->ClientSystemParam || (area.w <= 0) || (area.h <= 0))
201 if (SDL_RectsEqual(&area, &_sentWorkArea))
206 param.params = SPI_MASK_SET_WORK_AREA;
207 param.workArea.left = WINPR_ASSERTING_INT_CAST(UINT16, area.x);
208 param.workArea.top = WINPR_ASSERTING_INT_CAST(UINT16, area.y);
209 param.workArea.right = WINPR_ASSERTING_INT_CAST(UINT16, area.x + area.w);
210 param.workArea.bottom = WINPR_ASSERTING_INT_CAST(UINT16, area.y + area.h);
211 if (_rail->ClientSystemParam(_rail, ¶m) == CHANNEL_RC_OK)
212 _sentWorkArea = area;
215void SdlRail::handleMaximized(SDL_WindowID
id)
217 std::unique_lock lock(_windowsLock);
218 auto* appWindow = getWindowBySdlId(
id);
219 if (!appWindow || appWindow->isPopup() || !appWindow->window())
222 if (appWindow->railMaximized())
224 WLog_DBG(TAG,
"local maximize skipped id=0x%08" PRIx32
" (already rail-maximized)",
225 static_cast<UINT32
>(appWindow->id()));
228 appWindow->setRailMaximized(
true);
230 appWindow->invalidateAll();
232 if (appWindow->loopEndPending())
234 appWindow->deferMaximize();
235 WLog_DBG(TAG,
"local maximize deferred id=0x%08" PRIx32
" (modal loop open)",
236 static_cast<UINT32
>(appWindow->id()));
240 WLog_DBG(TAG,
"local maximize id=0x%08" PRIx32
" -> SC_MAXIMIZE",
241 static_cast<UINT32
>(appWindow->id()));
242 sendSystemCommand(appWindow, SC_MAXIMIZE);
245 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
248void SdlRail::handleMinimized(SDL_WindowID
id)
250 std::unique_lock lock(_windowsLock);
251 auto* appWindow = getWindowBySdlId(
id);
252 if (!appWindow || appWindow->isPopup() || !appWindow->window())
254 if (appWindow->railMinimized())
256 appWindow->setRailMinimized(
true);
257 WLog_DBG(TAG,
"local minimize id=0x%08" PRIx32
" -> SC_MINIMIZE",
258 static_cast<UINT32
>(appWindow->id()));
259 sendSystemCommand(appWindow, SC_MINIMIZE);
262void SdlRail::handleClose(SDL_WindowID
id)
264 std::unique_lock lock(_windowsLock);
265 auto* appWindow = getWindowBySdlId(
id);
266 if (!appWindow || !appWindow->window())
268 WLog_DBG(TAG,
"local close id=0x%08" PRIx32
" -> SC_CLOSE",
269 static_cast<UINT32
>(appWindow->id()));
270 sendSystemCommand(appWindow, SC_CLOSE);
273void SdlRail::handleRestored(SDL_WindowID
id)
275 std::unique_lock lock(_windowsLock);
276 auto* appWindow = getWindowBySdlId(
id);
277 if (!appWindow || appWindow->isPopup() || !appWindow->window())
280 if (appWindow->railMinimized())
281 appWindow->setRailMinimized(
false);
282 else if (appWindow->railMaximized())
283 appWindow->setRailMaximized(
false);
286 WLog_DBG(TAG,
"local restore id=0x%08" PRIx32
" -> SC_RESTORE",
287 static_cast<UINT32
>(appWindow->id()));
288 appWindow->invalidateAll();
289 sendSystemCommand(appWindow, SC_RESTORE);
291 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
294void SdlRail::handleFocus(SDL_WindowID
id,
bool gained)
296 std::unique_lock lock(_windowsLock);
297 auto appWindow = getWindowBySdlId(
id);
298 if (!appWindow || (appWindow->isPopup() && !appWindow->isFullscreen()) || !appWindow->window())
303 if (!gained && (_localMove.id ==
static_cast<uint32_t
>(appWindow->id())))
308 _focusedAppId = appWindow->id();
311 const auto wid =
static_cast<uint32_t
>(appWindow->id());
312 sendClientActivate(wid, gained);
316void SdlRail::sendClientActivate(uint32_t wid,
bool enabled)
318 if (!_rail || !_rail->ClientActivate)
321 _clientActiveId = wid;
323 activate.windowId = wid;
324 activate.enabled = enabled;
325 std::ignore = _rail->ClientActivate(_rail, &activate);
329void SdlRail::ensureActive(SDL_WindowID
id)
331 std::unique_lock lock(_windowsLock);
332 auto* appWindow = getWindowBySdlId(
id);
333 if (!appWindow || (appWindow->isPopup() && !appWindow->isFullscreen()) || !appWindow->window())
335 const auto wid =
static_cast<uint32_t
>(appWindow->id());
336 if (wid == _clientActiveId)
338 sendClientActivate(wid,
true);
341bool SdlRail::translateToServer(SDL_WindowID
id,
float& x,
float& y)
343 std::unique_lock lock(_windowsLock);
344 auto* appWindow = getWindowBySdlId(
id);
347 SDL_FPoint rpos = { x, y };
348 if (
auto* renderer = appWindow->renderer())
349 (void)SDL_RenderCoordinatesFromWindow(renderer, x, y, &rpos.x, &rpos.y);
351 const SDL_Point origin = appWindow->serverOrigin();
352 x = rpos.x +
static_cast<float>(origin.x);
353 y = rpos.y +
static_cast<float>(origin.y);
357SdlRailWindow* SdlRail::addWindow(uint64_t
id,
const SDL_Rect& rect)
360 auto it = _windows.find(
id);
361 if ((it != _windows.end()) && it->second.isDeleted())
363 WLog_DBG(TAG,
"window recreate id=0x%08" PRIx32
" reused a deleted entry",
364 static_cast<uint32_t
>(
id));
365 _deadWindows.insert(_windows.extract(it));
368 auto res = _windows.emplace(std::piecewise_construct, std::forward_as_tuple(
id),
369 std::forward_as_tuple(
id, rect));
370 return &res.first->second;
373void SdlRail::enableRemoteAppMode(
bool enable)
378bool SdlRail::paint(SDL_Surface* primary, SDL_PixelFormat fallbackFormat,
379 const std::vector<SDL_Rect>& damage)
384 std::unique_lock lock(_windowsLock);
387 if (_sentWorkArea.w == 0)
390 if (SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &usable))
391 sendWorkArea(usable);
395 for (
auto& [wid, win] : _windows)
397 if (win.isDeleted() || !win.window())
399 for (SDL_Window* p = SDL_GetWindowParent(win.window()); p; p = SDL_GetWindowParent(p))
401 auto* owner = getWindowBySdlId(SDL_GetWindowID(p));
402 if (!owner || owner->isDeleted())
411 std::vector<std::pair<size_t, uint64_t>> dead;
412 for (
auto& [wid, win] : _windows)
414 if (!win.isDeleted())
417 for (SDL_Window* p = win.window() ? SDL_GetWindowParent(win.window()) : nullptr; p;
418 p = SDL_GetWindowParent(p))
420 dead.emplace_back(depth, wid);
422 std::sort(dead.begin(), dead.end(),
423 [](
const auto& a,
const auto& b) { return a.first > b.first; });
424 for (
const auto& [depth, wid] : dead)
427 _deadWindows.clear();
430 for (
auto& it : _windows)
432 auto& win = it.second;
436 SDL_Window* parent =
nullptr;
437 SDL_Rect parentRect{};
438 auto* owner = resolveParent(win.owner());
439 if (owner && (owner != &win))
441 parent = owner->window();
442 parentRect = owner->outerRect();
444 win.paint(primary, fallbackFormat, damage, parent, parentRect);
446 SDL_Rect refusedOuter{};
447 if (win.takeWmOverride(refusedOuter))
449 const SDL_Rect rect = win.serverRect(refusedOuter);
450 const SDL_Rect cur = win.windowRect();
452 "wm-refused adopt id=0x%08" PRIx32
453 " outer=%dx%d -> srv=%d,%d %dx%d (was %dx%d)",
454 static_cast<uint32_t
>(win.id()), refusedOuter.w, refusedOuter.h, rect.x,
455 rect.y, rect.w, rect.h, cur.w, cur.h);
456 if (!SDL_RectsEqual(&rect, &cur))
457 sendClientWindowMove(&win, rect);
458 win.adoptLocalGeometry(rect);
465 for (
auto& it : _windows)
467 if (it.second.isPopup() || !it.second.window())
469 auto* ctx = &_context->common()->context;
470 const auto dw =
static_cast<UINT16
>(
472 const auto dh =
static_cast<UINT16
>(
475 if (ctx->update && ctx->update->RefreshRect)
476 (void)ctx->update->RefreshRect(ctx, 1, &all);
478 WLog_DBG(TAG,
"refresh-rect sent (first app window realized)");
483 for (
auto& it : _windows)
485 auto& popup = it.second;
486 if (!popup.isPopup())
490 if (!popup.isFrame() && isShadowFrame(popup))
491 popup.setFrame(
true);
494 if (popup.isLayered())
496 constexpr int reach = 48;
497 const SDL_Rect sr = popup.windowRect();
498 bool anchored =
false;
499 for (
auto& other : _windows)
501 auto& cand = other.second;
502 if ((&cand == &popup) || !cand.isPopup() || cand.isLayered() || !cand.window() ||
503 ((SDL_GetWindowFlags(cand.window()) & SDL_WINDOW_HIDDEN) != 0))
505 SDL_Rect zone = cand.windowRect();
512 const bool inside = (sr.x >= zone.x) && (sr.y >= zone.y) &&
513 (sr.x + sr.w <= zone.x + zone.w) &&
514 (sr.y + sr.h <= zone.y + zone.h);
521 popup.setShadowAnchored(anchored);
526 if (
auto* pw = popup.window())
528 SDL_Window* p = SDL_GetWindowParent(pw);
529 chosen = p ? getWindowBySdlId(SDL_GetWindowID(p)) : nullptr;
533 chosen = resolvePopupParent(popup);
535 WLog_DBG(TAG,
"popup id=0x%08" PRIx32
" has no parent app window",
536 static_cast<UINT32
>(popup.id()));
539 SDL_Window* parent =
nullptr;
540 SDL_Rect parentRect{};
543 parent = chosen->window();
544 parentRect = chosen->outerRect();
546 popup.paint(primary, fallbackFormat, damage, parent, parentRect);
555void SdlRail::applyZOrder()
560 if (!railPlatformCaps().supportsRestack)
562 _zOrderDirty =
false;
566 if (_localMove.id != 0)
568 WLog_VRB(TAG,
"zorder apply deferred: local move 0x%08" PRIx32
" active", _localMove.id);
572 std::vector<SDL_Window*> stack;
573 std::vector<uint32_t> applied;
574 stack.reserve(_zOrder.size());
575 applied.reserve(_zOrder.size());
576 for (uint32_t
id : _zOrder)
578 auto* w = getWindow(
id);
579 if (w && !w->isPopup() && w->window() &&
580 ((SDL_GetWindowFlags(w->window()) & SDL_WINDOW_HIDDEN) == 0))
582 stack.push_back(w->window());
583 applied.push_back(
id);
587 if (applied == _appliedZOrder)
589 _zOrderDirty =
false;
592 if (stack.size() >= 2)
594 std::ignore = sdl_x11_restack_windows(stack);
597 _appliedZOrder = std::move(applied);
598 _zOrderDirty =
false;
601UINT SdlRail::updateWindowFromSurface(gdiGfxSurface* surface)
604 return CHANNEL_RC_OK;
606 std::unique_lock lock(_windowsLock);
607 auto appWindow = getWindow(surface->windowId);
611 WLog_VRB(TAG,
"gfx surface for untracked id=0x%08" PRIx64, surface->windowId);
612 return CHANNEL_RC_OK;
617 std::min(surface->mappedWidth ? surface->mappedWidth : surface->width, surface->width);
619 std::min(surface->mappedHeight ? surface->mappedHeight : surface->height, surface->height);
623 const RECTANGLE_16* rects = region16_rects(&surface->invalidRegion, &nbRects);
625 const bool remapped = appWindow->takeSurfaceChange(surface->surfaceId);
626 if ((nbRects == 0) && !remapped)
627 return CHANNEL_RC_OK;
628 appWindow->updateGfxSurface(surface->data, surface->scanline, w, h, rects, nbRects,
630 region16_clear(&surface->invalidRegion);
632 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
633 return CHANNEL_RC_OK;
636UINT SdlRail::UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface)
638 WINPR_ASSERT(context);
639 auto gdi =
static_cast<rdpGdi*
>(context->custom);
640 if (!gdi || !gdi->context)
641 return CHANNEL_RC_OK;
642 auto sdl = get_context(gdi->context);
644 return CHANNEL_RC_OK;
645 return sdl->getRailChannelContext().updateWindowFromSurface(surface);
648bool SdlRail::init(RailClientContext* rail)
654 registerUpdateCallbacks(_context->context()->update);
657 std::unique_lock lock(_windowsLock);
659 for (
auto it = _windows.begin(); it != _windows.end();)
662 _deadWindows.insert(_windows.extract(cur));
664 auto* settings = _context->context()->settings;
665 const uint32_t caches =
669 _iconCache.assign(
static_cast<size_t>(caches) * _iconCacheEntries, {});
673 rail->ServerExecuteResult = SdlRail::server_execute_result;
675 rail->ServerLocalMoveSize = SdlRail::server_local_move_size;
676 rail->ServerMinMaxInfo = SdlRail::server_min_max_info;
679 WLog_WARN(TAG,
"RemoteApp/RAIL support in the SDL client is experimental");
681 WLog_DBG(TAG,
"RAIL channel initialized: driver=%s positionsReadable=%d transparentWindows=%d",
682 sdl::utils::isWaylandDriver() ?
"wayland"
683 : (
sdl::utils::isX11Driver() ?
"x11" :
"other"),
684 caps.positionsReadable ? 1 : 0, caps.supportsTransparentWindows ? 1 : 0);
685#if SDL_VERSION_ATLEAST(3, 4, 10)
687 if (sdl::utils::isX11Driver())
688 SDL_SetHint(SDL_HINT_VIDEO_X11_ENABLE_XSYNC_EXT,
"1");
693bool SdlRail::uninit(WINPR_ATTR_UNUSED RailClientContext* rail)
698 std::unique_lock lock(_windowsLock);
699 _refreshSent =
false;
702 _sessionMargins = {};
707 _appliedZOrder.clear();
708 _zOrderDirty =
false;
712 WLog_DBG(TAG,
"RAIL channel uninit, marking %zu windows for main-thread teardown",
714 for (
auto& kv : _windows)
715 kv.second.markDeleted();
719 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
723void SdlRail::registerUpdateCallbacks(rdpUpdate* update)
725 WINPR_ASSERT(update);
726 auto window = update->window;
727 WINPR_ASSERT(window);
729 window->WindowCreate = SdlRail::window_common;
730 window->WindowUpdate = SdlRail::window_common;
731 window->WindowIcon = SdlRail::window_icon;
732 window->WindowCachedIcon = SdlRail::window_cached_icon;
733 window->WindowDelete = SdlRail::window_delete;
734 window->MonitoredDesktop = SdlRail::monitored_desktop;
735 window->NonMonitoredDesktop = SdlRail::non_monitored_desktop;
740UINT SdlRail::server_execute_result(RailClientContext* context,
743 WINPR_ASSERT(context);
744 WINPR_ASSERT(execResult);
745 auto rail =
static_cast<SdlRail*
>(context->custom);
748 if (execResult->execResult != RAIL_EXEC_S_OK)
750 WLog_ERR(TAG,
"RAIL exec error: execResult=0x%04" PRIx16
" rawResult=0x%08" PRIx32,
751 execResult->execResult, execResult->rawResult);
752 freerdp_abort_connect_context(rail->_context->context());
756 WLog_DBG(TAG,
"RemoteApp exec OK, enabling RAIL mode");
757 rail->enableRemoteAppMode(
true);
759 return CHANNEL_RC_OK;
762UINT SdlRail::server_local_move_size(RailClientContext* context,
765 WINPR_ASSERT(context);
766 WINPR_ASSERT(localMoveSize);
769 if (localMoveSize->isMoveSizeStart && (localMoveSize->moveSizeType >= RAIL_WMSZ_LEFT) &&
770 (localMoveSize->moveSizeType <= RAIL_WMSZ_MOVE))
772 WLog_DBG(TAG,
"server move/size start id=0x%08" PRIx32
" type=%" PRIu16
" pos=%d,%d",
773 localMoveSize->windowId, localMoveSize->moveSizeType, localMoveSize->posX,
774 localMoveSize->posY);
775 (void)sdl_push_user_event(SDL_EVENT_USER_RAIL_MOVE, localMoveSize->windowId,
776 static_cast<int>(localMoveSize->moveSizeType));
778 else if (!localMoveSize->isMoveSizeStart)
780 WLog_DBG(TAG,
"server move/size end id=0x%08" PRIx32, localMoveSize->windowId);
782 auto rail =
static_cast<SdlRail*
>(context->custom);
785 std::unique_lock lock(rail->_windowsLock);
787 const bool ownedByWaylandResize = rail->_localMove.wayland &&
788 (rail->_localMove.id == localMoveSize->windowId) &&
789 rail->_localMove.sawResize;
790 if (!ownedByWaylandResize)
792 if (
auto* appWindow = rail->getWindow(localMoveSize->windowId))
793 appWindow->setLocalMoveActive(
false);
794 if (rail->_localMove.id == localMoveSize->windowId)
796 if (rail->_localMove.wayland)
799 rail->_localMove = {};
800 if (
auto* appWindow = rail->getWindow(localMoveSize->windowId))
801 appWindow->invalidateAll();
802 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
807 rail->_localMove.serverEnded =
true;
812 auto* appWindow = rail->getWindow(localMoveSize->windowId);
813 if (appWindow && appWindow->loopEndPending())
815 const auto actions = appWindow->takeLoopEnd();
816 if (actions.maximize && appWindow->railMaximized())
818 WLog_DBG(TAG,
"deferred SC_MAXIMIZE id=0x%08" PRIx32, localMoveSize->windowId);
819 rail->sendSystemCommand(appWindow, SC_MAXIMIZE);
824 WLog_DBG(TAG,
"deferred snap resize id=0x%08" PRIx32
" rect=%d,%d %dx%d",
825 localMoveSize->windowId, actions.snapRect.x, actions.snapRect.y,
826 actions.snapRect.w, actions.snapRect.h);
827 rail->sendClientWindowMove(appWindow, actions.snapRect);
832 if (appWindow->window() && !appWindow->effectivelyMaximized() &&
833 !appWindow->localMoveSizeChanged())
835 const SDL_WindowID sdlId = appWindow->sdlId();
837 rail->syncGeometry(sdlId);
842 return CHANNEL_RC_OK;
846bool SdlRail::suppressServerInput(SDL_WindowID
id)
848 std::unique_lock lock(_windowsLock);
849 if (_localMove.id == 0)
851 auto* appWindow = getWindowBySdlId(
id);
852 return appWindow && appWindow->localMoveActive() && !_localMove.wayland;
855bool SdlRail::suppressServerMotion(SDL_WindowID
id)
857 std::unique_lock lock(_windowsLock);
858 if (_localMove.id == 0)
860 auto* appWindow = getWindowBySdlId(
id);
862 return appWindow && appWindow->localMoveActive();
866static int railToNetDirection(uint16_t moveType)
870 case RAIL_WMSZ_TOPLEFT:
874 case RAIL_WMSZ_TOPRIGHT:
876 case RAIL_WMSZ_RIGHT:
878 case RAIL_WMSZ_BOTTOMRIGHT:
880 case RAIL_WMSZ_BOTTOM:
882 case RAIL_WMSZ_BOTTOMLEFT:
893static constexpr struct
898 { RAIL_WMSZ_TOP, 1 }, { RAIL_WMSZ_BOTTOM, 2 }, { RAIL_WMSZ_LEFT, 4 },
899 { RAIL_WMSZ_TOPLEFT, 5 }, { RAIL_WMSZ_BOTTOMLEFT, 6 }, { RAIL_WMSZ_RIGHT, 8 },
900 { RAIL_WMSZ_TOPRIGHT, 9 }, { RAIL_WMSZ_BOTTOMRIGHT, 10 },
904static uint32_t railToXdgEdge(uint16_t moveType)
906 for (
const auto& e : kRailXdgEdges)
907 if (e.rail == moveType)
920static RailEdges railEdges(uint16_t t)
922 return { (t == RAIL_WMSZ_TOPLEFT) || (t == RAIL_WMSZ_LEFT) || (t == RAIL_WMSZ_BOTTOMLEFT),
923 (t == RAIL_WMSZ_TOPRIGHT) || (t == RAIL_WMSZ_RIGHT) || (t == RAIL_WMSZ_BOTTOMRIGHT),
924 (t == RAIL_WMSZ_TOPLEFT) || (t == RAIL_WMSZ_TOP) || (t == RAIL_WMSZ_TOPRIGHT),
925 (t == RAIL_WMSZ_BOTTOMLEFT) || (t == RAIL_WMSZ_BOTTOM) ||
926 (t == RAIL_WMSZ_BOTTOMRIGHT) };
929void SdlRail::handleLocalMoveRequested(uint32_t windowId, uint16_t moveType)
931 const bool wayland = sdl::utils::isWaylandDriver();
932 const bool x11 = sdl::utils::isX11Driver();
933 if (!wayland && !x11)
936 std::unique_lock lock(_windowsLock);
937 auto appWindow = getWindow(windowId);
938 if (!appWindow || !appWindow->window() || appWindow->isPopup())
941 const bool isMove = (moveType == RAIL_WMSZ_MOVE);
942 WLog_DBG(TAG,
"local move start id=0x%08" PRIx32
" driver=%s type=%" PRIu16
" %s", windowId,
943 wayland ?
"wayland" :
"x11", moveType, isMove ?
"move" :
"resize");
944 bool started =
false;
949 started = sdl_wayland_begin_move(appWindow->window());
951 started = sdl_wayland_begin_resize(appWindow->window(), railToXdgEdge(moveType));
952 if (started && !isMove)
955 _localMove.id = windowId;
956 _localMove.wayland =
true;
957 _localMove.type = moveType;
963 started = sdl_x11_begin_move_size(appWindow->window(), railToNetDirection(moveType));
967 _localMove.id = windowId;
968 _localMove.type = moveType;
970 _localMove.anchor = _lastPressServer;
971 _localMove.pointer = _lastPointerServer;
973 appWindow->clearLoopEnd();
975 const RailEdges e = railEdges(moveType);
976 const int row = e.top ? 2 : (e.bottom ? 0 : 1);
977 const int col = e.left ? 2 : (e.right ? 0 : 1);
978 (void)sdl_x11_set_bit_gravity(appWindow->window(), row * 3 + col + 1);
982 if (started && (!wayland || !isMove))
984 appWindow->setLocalMoveActive(
true);
988 const RailEdges e = railEdges(moveType);
989 appWindow->setResizeAnchor(e.left, e.top);
994 WLog_WARN(TAG,
"WM move failed for RAIL window 0x%08" PRIx32, windowId);
1000void SdlRail::sendClientWindowMove(
SdlRailWindow* appWindow,
const SDL_Rect& serverRect)
1002 const SDL_Rect fm = appWindow->frameMargins();
1004 move.windowId =
static_cast<UINT32
>(appWindow->id());
1005 move.left = WINPR_ASSERTING_INT_CAST(INT16, serverRect.x - fm.x);
1006 move.top = WINPR_ASSERTING_INT_CAST(INT16, serverRect.y - fm.y);
1007 move.right = WINPR_ASSERTING_INT_CAST(INT16, serverRect.x + serverRect.w + fm.w);
1008 move.bottom = WINPR_ASSERTING_INT_CAST(INT16, serverRect.y + serverRect.h + fm.h);
1009 if (_rail && _rail->ClientWindowMove &&
1010 (_rail->ClientWindowMove(_rail, &move) != CHANNEL_RC_OK))
1011 WLog_WARN(TAG,
"ClientWindowMove failed for RAIL window 0x%08" PRIx32, move.windowId);
1015void SdlRail::reportAndAdopt(
SdlRailWindow* appWindow,
int x,
int y,
int w,
int h)
1018 const SDL_Rect rect = appWindow->serverRect({ x, y, w, h });
1020 const bool maximized = appWindow->effectivelyMaximized();
1022 const SDL_Rect cur = appWindow->windowRect();
1023 const bool unchanged = SDL_RectsEqual(&rect, &cur);
1024 if (!maximized && !unchanged)
1025 sendClientWindowMove(appWindow, rect);
1029 appWindow->setLocalMoveActive(
false);
1031 appWindow->adoptLocalGeometry(rect);
1033 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1036void SdlRail::clampIntoDesktop(
int& x,
int& y,
int w,
int h)
const
1038 auto* settings = _context->context()->settings;
1041 x = std::clamp(x, 0, std::max(0, dw - w));
1042 y = std::clamp(y, 0, std::max(0, dh - h));
1045void SdlRail::noteDragResize(SDL_WindowID
id,
int w,
int h)
1047 std::unique_lock lock(_windowsLock);
1048 if ((_localMove.id == 0) || _localMove.wayland || (_localMove.type != RAIL_WMSZ_MOVE))
1050 auto* appWindow = getWindowBySdlId(
id);
1051 if (!appWindow || (
static_cast<uint32_t
>(appWindow->id()) != _localMove.id))
1054 if (appWindow->localMoveSizeChanged())
1056 const SDL_Rect outer = appWindow->outerRect();
1057 if ((w == outer.w) && (h == outer.h))
1060 _localMove.wmSized =
true;
1061 _localMove.wmSize = { w, h };
1065void SdlRail::reportMaximizedWorkArea(
SdlRailWindow* appWindow)
1069 SDL_GetWindowSize(appWindow->window(), &w, &h);
1070 if ((w <= 0) || (h <= 0))
1074 if (railPlatformCaps().positionsReadable)
1075 SDL_GetWindowPosition(appWindow->window(), &x, &y);
1079 if (!SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &primary))
1081 const SDL_Rect area = { x, y, w, h };
1083 if (!SDL_GetRectIntersection(&area, &primary, &overlap) || !SDL_RectsEqual(&overlap, &area))
1088void SdlRail::syncGeometry(SDL_WindowID
id)
1090 std::unique_lock lock(_windowsLock);
1091 auto* w = getWindowBySdlId(
id);
1092 if (!w || !w->window() || w->isDeleted() || w->isPopup())
1096 if (w->effectivelyMaximized())
1098 reportMaximizedWorkArea(w);
1101 if (w->railMinimized())
1104 const auto wid =
static_cast<uint32_t
>(w->id());
1106 if (w->localMoveActive() || (_localMove.id == wid) || w->loopEndPending() ||
1107 w->stateTransitionPending() || w->awaitingRestoreRect())
1112 SDL_GetWindowSize(w->window(), &lw, &lh);
1113 if ((lw <= 0) || (lh <= 0))
1117 const SDL_Rect ins = w->insets();
1118 if (((lw - ins.x - ins.w) <= 0) || ((lh - ins.y - ins.h) <= 0))
1120 WLog_DBG(TAG,
"geometry sync id=0x%08" PRIx32
" %dx%d dropped (content collapsed)", wid, lw,
1125 const SDL_Rect vis = w->outerRect();
1126 const bool posKnown = railPlatformCaps().positionsReadable;
1130 SDL_GetWindowPosition(w->window(), &lx, &ly);
1132 clampIntoDesktop(lx, ly, lw, lh);
1135 if ((lw == vis.w) && (lh == vis.h) && (!posKnown || ((lx == vis.x) && (ly == vis.y))))
1137 w->clearGeomApplyPending();
1142 if (w->geomApplyPending())
1145 const bool overtaken = !posKnown && ((lw > vis.w) || (lh > vis.h));
1148 w->clearGeomApplyPending();
1151 WLog_DBG(TAG,
"geometry sync id=0x%08" PRIx32
" local=%d,%d %dx%d (sync)", wid, lx, ly, lw, lh);
1152 reportAndAdopt(w, lx, ly, lw, lh);
1155void SdlRail::noteResizeGrab(SDL_WindowID
id)
1157 std::unique_lock lock(_windowsLock);
1158 if (_localMove.id == 0)
1160 auto* appWindow = getWindowBySdlId(
id);
1162 if (appWindow && (
static_cast<uint32_t
>(appWindow->id()) == _localMove.id) &&
1164 _localMove.sawResize =
true;
1167void SdlRail::handleWaylandResize(SDL_WindowID
id)
1169 if (!sdl::utils::isWaylandDriver())
1171 std::unique_lock lock(_windowsLock);
1172 auto* appWindow = getWindowBySdlId(
id);
1173 if (!appWindow || appWindow->isPopup() || !appWindow->window())
1176 if (
static_cast<uint32_t
>(appWindow->id()) == _localMove.id)
1178 if (_localMove.wayland)
1179 _localMove.sawResize =
true;
1185 SDL_GetWindowSize(appWindow->window(), &w, &h);
1186 if ((w <= 0) || (h <= 0))
1190 const SDL_Rect vis = appWindow->outerRect();
1191 if ((w == vis.w) && (h == vis.h))
1199void SdlRail::completeWaylandResize(
bool definitive)
1201 std::unique_lock lock(_windowsLock);
1202 if ((_localMove.id == 0) || !_localMove.wayland)
1205 if (!_localMove.sawResize)
1209 if (
auto* appWindow = getWindow(_localMove.id))
1211 appWindow->setLocalMoveActive(
false);
1214 appWindow->invalidateAll();
1215 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1223 const LocalMove drag = _localMove;
1226 auto appWindow = getWindow(drag.id);
1227 if (!appWindow || !appWindow->window())
1230 (void)SDL_SyncWindow(appWindow->window());
1233 SDL_GetWindowSize(appWindow->window(), &w, &h);
1236 const SDL_Rect start = appWindow->windowRect();
1237 const RailEdges e = railEdges(drag.type);
1238 int x = e.left ? (start.x + start.w - w) : start.x;
1239 int y = e.top ? (start.y + start.h - h) : start.y;
1240 clampIntoDesktop(x, y, w, h);
1241 reportAndAdopt(appWindow, x, y, w, h);
1244void SdlRail::completeLocalMoveIfPending()
1246 std::unique_lock lock(_windowsLock);
1247 if (_localMove.id == 0)
1250 if (_localMove.wayland)
1254 const LocalMove drag = _localMove;
1257 auto appWindow = getWindow(drag.id);
1258 if (!appWindow || !appWindow->window() || appWindow->isDeleted())
1262 if ((SDL_GetMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK) &&
1263 !(SDL_GetGlobalMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK))
1264 (void)sdl_x11_send_left_button_release(appWindow->window());
1267 (void)SDL_SyncWindow(appWindow->window());
1273 SDL_GetWindowSize(appWindow->window(), &w, &h);
1274 SDL_GetWindowPosition(appWindow->window(), &x, &y);
1275 const bool isMove = (drag.type == RAIL_WMSZ_MOVE);
1276 const SDL_Rect start = appWindow->outerRect();
1281 if (!appWindow->effectivelyMaximized() && drag.wmSized &&
1282 !appWindow->localMoveSizeChanged())
1287 appWindow->deferSnap(appWindow->serverRect({ x, y, w, h }));
1296 if (!drag.serverEnded)
1298 int px = drag.anchor.x;
1299 int py = drag.anchor.y;
1300 if (isMove && appWindow->localMoveSizeChanged())
1303 const SDL_Point sp = appWindow->localMoveServerPos();
1304 const SDL_Rect ii = appWindow->insets();
1305 px = drag.pointer.x + (x + ii.x) - sp.x;
1306 py = drag.pointer.y + (y + ii.y) - sp.y;
1308 else if (!appWindow->effectivelyMaximized())
1310 const RailEdges e = railEdges(drag.type);
1311 if (isMove || e.left)
1314 px += (x + w) - (start.x + start.w);
1315 if (isMove || e.top)
1318 py += (y + h) - (start.y + start.h);
1320 (void)freerdp_client_send_button_event(_context->common(), FALSE, PTR_FLAGS_BUTTON1, px,
1323 appWindow->armLoopEnd();
1326 const SDL_Rect rect = appWindow->serverRect({ x, y, w, h });
1327 const bool restoredMidDrag = appWindow->localMoveSizeChanged();
1328 appWindow->adoptLocalGeometry(rect);
1330 if (restoredMidDrag)
1331 appWindow->markGeometryDirty();
1332 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1337 clampIntoDesktop(x, y, w, h);
1338 reportAndAdopt(appWindow, x, y, w, h);
1341 (void)sdl_x11_set_bit_gravity(appWindow->window(), 0 );
1344UINT SdlRail::server_min_max_info(RailClientContext* context,
1347 WINPR_ASSERT(context);
1348 WINPR_ASSERT(minMaxInfo);
1349 auto rail =
static_cast<SdlRail*
>(context->custom);
1352 std::unique_lock lock(rail->_windowsLock);
1353 auto appWindow = rail->getWindow(minMaxInfo->windowId);
1356 WLog_VRB(TAG,
"server minmax id=0x%08" PRIx32
" min=%dx%d max=%dx%d", minMaxInfo->windowId,
1357 minMaxInfo->minTrackWidth, minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
1358 minMaxInfo->maxTrackHeight);
1359 appWindow->setMinMaxSize({ minMaxInfo->minTrackWidth, minMaxInfo->minTrackHeight },
1360 { minMaxInfo->maxTrackWidth, minMaxInfo->maxTrackHeight });
1362 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1364 return CHANNEL_RC_OK;
1370static bool marginsSet(
const SDL_Rect& m)
1372 return m.x || m.y || m.w || m.h;
1379 const UINT32 fieldFlags = orderInfo->fieldFlags;
1380 SDL_Rect m = appWindow->frameMargins();
1381 if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X)
1383 m.x =
static_cast<int>(state->resizeMarginLeft);
1384 m.w =
static_cast<int>(state->resizeMarginRight);
1386 if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y)
1388 m.y =
static_cast<int>(state->resizeMarginTop);
1389 m.h =
static_cast<int>(state->resizeMarginBottom);
1392 appWindow->setFrameMargins(m);
1393 SDL_Rect& sm = _sessionMargins;
1394 if (marginsSet(m) && !marginsSet(sm))
1398 for (
auto& [
id, w] : _windows)
1399 if (!marginsSet(w.resizeMargins()))
1400 w.setResizeMargins(m.x, m.y, m.w, m.h);
1402 const SDL_Rect bandSrc = marginsSet(m) ? m : sm;
1403 appWindow->setResizeMargins(bandSrc.x, bandSrc.y, bandSrc.w, bandSrc.h);
1404 WLog_DBG(TAG,
"margins id=0x%08" PRIx32
" raw L%d T%d R%d B%d", orderInfo->windowId, m.x, m.y,
1412 WLog_DBG(TAG,
"server showState id=0x%08" PRIx32
" state=0x%02" PRIx32, orderInfo->windowId,
1414 appWindow->setVisible(state->showState != WINDOW_HIDE);
1415 switch (state->showState)
1417 case WINDOW_SHOW_MAXIMIZED:
1418 appWindow->setServerMaximized(
true);
1419 appWindow->setServerMinimized(
false);
1421 case WINDOW_SHOW_MINIMIZED:
1422 appWindow->setServerMinimized(
true);
1425 appWindow->setServerMaximized(
false);
1426 appWindow->setServerMinimized(
false);
1437 std::vector<SDL_Rect> rects;
1438 rects.reserve(state->numVisibilityRects);
1439 for (UINT32 i = 0; i < state->numVisibilityRects; i++)
1442 rects.push_back({ r.left, r.top, r.right - r.left, r.bottom - r.top });
1445 WLog_DBG(TAG,
"visrects id=0x%08" PRIx32
" n=0", orderInfo->windowId);
1447 WLog_DBG(TAG,
"visrects id=0x%08" PRIx32
" n=%zu first=%dx%d+%d+%d", orderInfo->windowId,
1448 rects.size(), rects.at(0).w, rects.at(0).h, rects.at(0).x, rects.at(0).y);
1449 appWindow->setVisibilityRects(std::move(rects));
1452BOOL SdlRail::window_common(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1455 WINPR_ASSERT(orderInfo);
1456 WINPR_ASSERT(windowState);
1457 auto rail = SdlRail::get(context);
1461 const UINT32 fieldFlags = orderInfo->fieldFlags;
1462 std::unique_lock lock(rail->_windowsLock);
1463 auto appWindow = rail->getWindow(orderInfo->windowId);
1465 if (fieldFlags & WINDOW_ORDER_STATE_NEW)
1467 const SDL_Rect rect = {
static_cast<int>(windowState->windowOffsetX),
1468 static_cast<int>(windowState->windowOffsetY),
1469 static_cast<int>(windowState->windowWidth),
1470 static_cast<int>(windowState->windowHeight) };
1471 appWindow = rail->addWindow(orderInfo->windowId, rect);
1476 "window create id=0x%08" PRIx32
" %dx%d+%d+%d style=0x%08" PRIx32
1477 " ex=0x%08" PRIx32
" owner=0x%08" PRIx32,
1478 orderInfo->windowId, rect.w, rect.h, rect.x, rect.y, windowState->style,
1479 windowState->extendedStyle, windowState->ownerWindowId);
1481 const SDL_Rect& sm = rail->_sessionMargins;
1483 appWindow->setResizeMargins(sm.x, sm.y, sm.w, sm.h);
1490 if (fieldFlags & (WINDOW_ORDER_FIELD_WND_OFFSET | WINDOW_ORDER_FIELD_WND_SIZE))
1492 SDL_Rect r = appWindow->windowRect();
1493 if (fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET)
1495 r.x =
static_cast<int>(windowState->windowOffsetX);
1496 r.y =
static_cast<int>(windowState->windowOffsetY);
1498 if (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE)
1500 r.w =
static_cast<int>(windowState->windowWidth);
1501 r.h =
static_cast<int>(windowState->windowHeight);
1503 WLog_VRB(TAG,
"server geom id=0x%08" PRIx32
" rect=%d,%d %dx%d", orderInfo->windowId, r.x,
1505 appWindow->updateWindowRect(r);
1507 if (fieldFlags & WINDOW_ORDER_FIELD_OWNER)
1508 appWindow->setOwner(windowState->ownerWindowId);
1509 if (fieldFlags & (WINDOW_ORDER_FIELD_RESIZE_MARGIN_X | WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y))
1510 rail->updateMargins(appWindow, orderInfo, windowState);
1511 if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
1512 appWindow->setStyle(windowState->style, windowState->extendedStyle);
1513 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
1515 char* title = rail_string_to_utf8_string(&windowState->titleInfo);
1518 appWindow->setTitle(title);
1523 if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
1524 updateShowState(appWindow, orderInfo, windowState);
1526 if (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET)
1528 WLog_DBG(TAG,
"visoff id=0x%08" PRIx32
" %d,%d", orderInfo->windowId,
1529 windowState->visibleOffsetX, windowState->visibleOffsetY);
1530 appWindow->setVisibleOffset({
static_cast<int>(windowState->visibleOffsetX),
1531 static_cast<int>(windowState->visibleOffsetY) });
1533 if (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY)
1534 updateVisRects(appWindow, orderInfo, windowState);
1537 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1541SdlRailIcon* SdlRail::iconCacheLookup(uint32_t cacheId, uint32_t cacheEntry)
1543 if (cacheId == 0xFF)
1544 return &_iconScratch;
1545 const size_t idx =
static_cast<size_t>(cacheId) * _iconCacheEntries + cacheEntry;
1546 if ((_iconCacheEntries == 0) || (cacheEntry >= _iconCacheEntries) || (idx >= _iconCache.size()))
1548 return &_iconCache.at(idx);
1554 icon.w = info->width;
1555 icon.h = info->height;
1556 icon.bgra.assign(4ULL * info->width * info->height, 0);
1557 return freerdp_image_copy_from_icon_data(
1558 icon.bgra.data(), PIXEL_FORMAT_BGRA32, 0, 0, 0,
1559 WINPR_ASSERTING_INT_CAST(UINT16, info->width),
1560 WINPR_ASSERTING_INT_CAST(UINT16, info->height), info->bitsColor,
1561 WINPR_ASSERTING_INT_CAST(UINT16, info->cbBitsColor), info->bitsMask,
1562 WINPR_ASSERTING_INT_CAST(UINT16, info->cbBitsMask), info->colorTable,
1563 WINPR_ASSERTING_INT_CAST(UINT16, info->cbColorTable), info->bpp) == TRUE;
1566BOOL SdlRail::window_icon(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1569 WINPR_ASSERT(orderInfo);
1570 WINPR_ASSERT(windowIcon);
1571 WINPR_ASSERT(windowIcon->iconInfo);
1572 auto rail = SdlRail::get(context);
1576 std::unique_lock lock(rail->_windowsLock);
1577 auto appWindow = rail->getWindow(orderInfo->windowId);
1582 const ICON_INFO* info = windowIcon->iconInfo;
1583 auto* icon = rail->iconCacheLookup(info->cacheId, info->cacheEntry);
1584 if (!icon || !convertRailIcon(info, *icon))
1586 WLog_WARN(TAG,
"failed to decode icon %02" PRIX32
":%04" PRIX32
" for window 0x%08" PRIx32,
1587 info->cacheId, info->cacheEntry, orderInfo->windowId);
1590 appWindow->setIcon(*icon);
1592 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1596BOOL SdlRail::window_cached_icon(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1599 WINPR_ASSERT(orderInfo);
1600 WINPR_ASSERT(windowCachedIcon);
1601 auto rail = SdlRail::get(context);
1605 std::unique_lock lock(rail->_windowsLock);
1606 auto appWindow = rail->getWindow(orderInfo->windowId);
1611 auto* icon = rail->iconCacheLookup(cached.cacheId, cached.cacheEntry);
1612 if (!icon || icon->bgra.empty())
1615 "cached icon %02" PRIX32
":%04" PRIX32
" not in cache (window 0x%08" PRIx32
")",
1616 cached.cacheId, cached.cacheEntry, orderInfo->windowId);
1619 appWindow->setIcon(*icon);
1621 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1625BOOL SdlRail::window_delete(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo)
1627 WINPR_ASSERT(orderInfo);
1628 auto rail = SdlRail::get(context);
1633 std::unique_lock lock(rail->_windowsLock);
1634 auto appWindow = rail->getWindow(orderInfo->windowId);
1637 WLog_DBG(TAG,
"window delete id=0x%08" PRIx32, orderInfo->windowId);
1638 appWindow->markDeleted();
1642 if (rail->_localMove.id == orderInfo->windowId)
1643 rail->_localMove = {};
1644 if (rail->_clientActiveId == orderInfo->windowId)
1645 rail->_clientActiveId = 0;
1646 if (rail->_focusedAppId == orderInfo->windowId)
1647 rail->_focusedAppId = 0;
1650 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1654BOOL SdlRail::monitored_desktop(rdpContext* context,
const WINDOW_ORDER_INFO* orderInfo,
1657 WINPR_ASSERT(orderInfo);
1658 WINPR_ASSERT(monitoredDesktop);
1659 auto rail = SdlRail::get(context);
1664 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ARC_COMPLETED)
1666 rail->enableRemoteAppMode(
true);
1669 if (app && (strnlen(app, 1) > 0))
1671 WLog_DBG(TAG,
"RAIL mode enabled (monitored desktop); launching '%s'", app);
1672 if (client_rail_server_start_cmd(rail->_rail) != CHANNEL_RC_OK)
1674 WLog_ERR(TAG,
"client_rail_server_start_cmd failed for '%s'", app);
1681 if (orderInfo->fieldFlags &
1682 (WINDOW_ORDER_FIELD_DESKTOP_ZORDER | WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND))
1684 std::unique_lock lock(rail->_windowsLock);
1685 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND)
1689 rail->_clientActiveId = monitoredDesktop->activeWindowId;
1691 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ZORDER)
1693 if (monitoredDesktop->windowIds && (monitoredDesktop->numWindowIds > 0))
1694 rail->_zOrder.assign(monitoredDesktop->windowIds,
1695 monitoredDesktop->windowIds + monitoredDesktop->numWindowIds);
1697 rail->_zOrder.clear();
1698 rail->_zOrderDirty =
true;
1700 (void)sdl_push_user_event(SDL_EVENT_USER_UPDATE);
1706BOOL SdlRail::non_monitored_desktop(rdpContext* context,
1709 WINPR_ASSERT(orderInfo);
1710 auto rail = SdlRail::get(context);
1713 WLog_DBG(TAG,
"RAIL mode disabled (non-monitored desktop)");
1714 rail->enableRemoteAppMode(
false);
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.