FreeRDP
Loading...
Searching...
No Matches
SdlRailWindow Class Reference

#include <sdl_rail_window.hpp>

Data Structures

struct  LoopEndActions
 

Public Member Functions

 SdlRailWindow (uint64_t id, const SDL_Rect &rect)
 
 SdlRailWindow (SdlRailWindow &&)=delete
 
 SdlRailWindow (const SdlRailWindow &)=delete
 
SdlRailWindow & operator= (const SdlRailWindow &)=delete
 
SdlRailWindow & operator= (SdlRailWindow &&)=delete
 
uint64_t id () const
 
SDL_WindowID sdlId () const
 
SDL_Window * window () const
 
SDL_Renderer * renderer () const
 
void updateWindowRect (const SDL_Rect &rect)
 
SDL_Rect windowRect () const
 
void setLocalMoveActive (bool active)
 
bool localMoveActive () const
 
bool localMoveSizeChanged () const
 
SDL_Point localMoveServerPos () const
 
void adoptLocalGeometry (const SDL_Rect &rect)
 
void setResizeAnchor (bool right, bool bottom)
 
void markDeleted ()
 
void releaseWindow ()
 
bool isDeleted () const
 
bool geomApplyPending () const
 
void clearGeomApplyPending ()
 
void armLoopEnd ()
 
bool loopEndPending () const
 
void deferMaximize ()
 
void deferSnap (const SDL_Rect &serverRect)
 
void clearLoopEnd ()
 
LoopEndActions takeLoopEnd ()
 
bool takeWmOverride (SDL_Rect &outer)
 
void setVisibilityRects (std::vector< SDL_Rect > rects)
 
void setVisibleOffset (SDL_Point offset)
 
void setMinMaxSize (SDL_Point minSize, SDL_Point maxSize)
 
void setResizeMargins (int left, int top, int right, int bottom)
 
SDL_Rect resizeMargins () const
 
void setFrameMargins (const SDL_Rect &m)
 
SDL_Rect frameMargins () const
 
bool takeUnconfirmedReport (SDL_Rect &rect)
 
void setOwner (uint64_t ownerId)
 
uint64_t owner () const
 
bool isPopup () const
 
bool isFullscreen () const
 
bool activatable () const
 
bool isLayered () const
 
void setShadowAnchored (bool anchored)
 
void setFrame (bool frame)
 
bool isFrame () const
 
SDL_Rect insets () const
 
SDL_Rect outerRect () const
 
SDL_Point serverOrigin () const
 
void notePlaced ()
 
bool placedRel (SDL_Point &rel) const
 
void placePopup (const SDL_Rect &vis, const SDL_Rect &parentRect)
 
void commitPlacement ()
 
SDL_Rect serverRect (const SDL_Rect &outer) const
 
void setLocalShift (const SDL_Point &shift)
 
void setStyle (uint32_t style, uint32_t exStyle)
 
void setTitle (const std::string &title)
 
void setVisible (bool visible)
 
void setIcon (const SdlRailIcon &icon)
 
bool takeSurfaceChange (uint32_t surfaceId)
 
void updateGfxSurface (const void *data, uint32_t stride, uint32_t width, uint32_t height, const RECTANGLE_16 *damage, uint32_t nbDamage, uint32_t format)
 
void invalidateAll ()
 
bool railMaximized () const
 
void setRailMaximized (bool m)
 
void setServerMaximized (bool m)
 
bool railMinimized () const
 
void setRailMinimized (bool m)
 
void setServerMinimized (bool m)
 
void noteLocalRestore ()
 
bool takeRestoreRetry ()
 
void markGeometryDirty ()
 
bool geometryFrozen () const
 
bool awaitingRestoreRect () const
 
bool insetsStale () const
 
bool effectivelyMaximized () const
 
bool stateTransitionPending () const
 
bool reconcile (SDL_Window *parent, const SDL_Rect &parentRect)
 
bool paint (SDL_Surface *primary, SDL_PixelFormat fallbackFormat, const std::vector< SDL_Rect > &damage, SDL_Window *parent, const SDL_Rect &parentRect)
 

Detailed Description

A single RemoteApp window, backed by a borderless SDL window.

Geometry is server-authoritative: _windowRect mirrors the server's WINDOW_ORDER offset/size and the local SDL window is moved/resized to follow it.

Definition at line 49 of file sdl_rail_window.hpp.


Data Structure Documentation

◆ SdlRailWindow::LoopEndActions

struct SdlRailWindow::LoopEndActions

Definition at line 99 of file sdl_rail_window.hpp.

Data Fields
bool maximize = false
bool snap = false
SDL_Rect snapRect = { 0, 0, 0, 0 }

Constructor & Destructor Documentation

◆ SdlRailWindow()

SdlRailWindow::SdlRailWindow ( uint64_t  id,
const SDL_Rect &  rect 
)

Definition at line 85 of file sdl_rail_window.cpp.

85 : _id(id), _windowRect(rect)
86{
87}

Member Function Documentation

◆ activatable()

bool SdlRailWindow::activatable ( ) const

Definition at line 426 of file sdl_rail_window.cpp.

427{
428 std::unique_lock lock(_gfxLock);
429 return (!_isPopup || _fullscreen) && ((_exStyle & WS_EX_NOACTIVATE) == 0);
430}

◆ adoptLocalGeometry()

void SdlRailWindow::adoptLocalGeometry ( const SDL_Rect &  rect)

Definition at line 198 of file sdl_rail_window.cpp.

199{
200 /* Adopt local geometry so the server's echoing WINDOW_ORDER is a no-op (not a size snap). */
201 std::unique_lock lock(_gfxLock);
202 /* A completed local move settles geometry via the completion path, not the reporter. */
203 _geomApplyPending = false;
204 /* Translate visible offset by move delta. */
205 if (_visOffsetSet)
206 {
207 _visOffset.x += rect.x - _windowRect.x;
208 _visOffset.y += rect.y - _windowRect.y;
209 }
210 if (!SDL_RectsEqual(&rect, &_windowRect))
211 {
212 if (_reported.size() >= kMaxReported)
213 _reported.erase(_reported.begin());
214 _reported.push_back(rect);
215 armDeadline(_reportCheckAt, kReportConfirmMs);
216 }
217 _windowRect = rect;
218 _geometryDirty = false;
219 /* A resize drag ends here; the server surface still has the pre-drag size. */
220 _awaitingFrameUntil =
221 (_localMoveActive && _localMoveIsResize) ? (SDL_GetTicks() + kAwaitServerFrameMs) : 0;
222 _localMoveActive = false;
223 _needsFullBlit = true;
224 /* Repaint real content (clear placeholder). */
225 if (_hasGfx)
226 _gfxDamage.assign(1, SDL_Rect{ 0, 0, static_cast<int>(_gfxW), static_cast<int>(_gfxH) });
227}

◆ armLoopEnd()

void SdlRailWindow::armLoopEnd ( )

Definition at line 280 of file sdl_rail_window.cpp.

281{
282 std::unique_lock lock(_gfxLock);
283 _loopEnd.pending = true;
284}

◆ awaitingRestoreRect()

bool SdlRailWindow::awaitingRestoreRect ( ) const
inline

Definition at line 207 of file sdl_rail_window.hpp.

208 {
209 std::unique_lock lock(_gfxLock);
210 return restoreRectPending();
211 }

◆ clearGeomApplyPending()

void SdlRailWindow::clearGeomApplyPending ( )

Definition at line 274 of file sdl_rail_window.cpp.

275{
276 std::unique_lock lock(_gfxLock);
277 _geomApplyPending = false;
278}

◆ clearLoopEnd()

void SdlRailWindow::clearLoopEnd ( )

Definition at line 305 of file sdl_rail_window.cpp.

306{
307 std::unique_lock lock(_gfxLock);
308 _loopEnd = {};
309}

◆ commitPlacement()

void SdlRailWindow::commitPlacement ( )

Definition at line 628 of file sdl_rail_window.cpp.

629{
630 if (!_pendingRel)
631 return;
632 _placedRel = _pendingRel;
633 _pendingRel.reset();
634}

◆ deferMaximize()

void SdlRailWindow::deferMaximize ( )

Definition at line 292 of file sdl_rail_window.cpp.

293{
294 std::unique_lock lock(_gfxLock);
295 _loopEnd.maximize = true;
296}

◆ deferSnap()

void SdlRailWindow::deferSnap ( const SDL_Rect &  serverRect)

Definition at line 298 of file sdl_rail_window.cpp.

299{
300 std::unique_lock lock(_gfxLock);
301 _loopEnd.snap = true;
302 _loopEnd.snapRect = serverRect;
303}

◆ effectivelyMaximized()

bool SdlRailWindow::effectivelyMaximized ( ) const

Definition at line 1227 of file sdl_rail_window.cpp.

1228{
1229 return _maxState.rail ||
1230 (_win && (SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_MAXIMIZED) != 0);
1231}

◆ frameMargins()

SDL_Rect SdlRailWindow::frameMargins ( ) const

Definition at line 383 of file sdl_rail_window.cpp.

384{
385 std::unique_lock lock(_gfxLock);
386 return _frameMargins;
387}

◆ geomApplyPending()

bool SdlRailWindow::geomApplyPending ( ) const

Definition at line 268 of file sdl_rail_window.cpp.

269{
270 std::unique_lock lock(_gfxLock);
271 return _geomApplyPending;
272}

◆ geometryFrozen()

bool SdlRailWindow::geometryFrozen ( ) const
inline

Definition at line 202 of file sdl_rail_window.hpp.

203 {
204 return _maxState.rail || _maxState.server || _minState.rail || _minState.server;
205 }

◆ id()

uint64_t SdlRailWindow::id ( ) const

Definition at line 92 of file sdl_rail_window.cpp.

93{
94 return _id;
95}

◆ insets()

SDL_Rect SdlRailWindow::insets ( ) const

Definition at line 516 of file sdl_rail_window.cpp.

517{
518 std::unique_lock lock(_gfxLock);
519 return _appliedInsets;
520}

◆ insetsStale()

bool SdlRailWindow::insetsStale ( ) const

Definition at line 1215 of file sdl_rail_window.cpp.

1216{
1217 std::unique_lock lock(_gfxLock);
1218 return _wasMaximized != effectivelyMaximized();
1219}

◆ invalidateAll()

void SdlRailWindow::invalidateAll ( )

Definition at line 1124 of file sdl_rail_window.cpp.

1125{
1126 std::unique_lock lock(_gfxLock);
1127 _needsFullBlit = true;
1128 if (_hasGfx)
1129 _gfxDamage.assign(1, SDL_Rect{ 0, 0, static_cast<int>(_gfxW), static_cast<int>(_gfxH) });
1130}

◆ isDeleted()

bool SdlRailWindow::isDeleted ( ) const

Definition at line 262 of file sdl_rail_window.cpp.

263{
264 std::unique_lock lock(_gfxLock);
265 return _deleted;
266}

◆ isFrame()

bool SdlRailWindow::isFrame ( ) const

Definition at line 450 of file sdl_rail_window.cpp.

451{
452 std::unique_lock lock(_gfxLock);
453 return _frame;
454}

◆ isFullscreen()

bool SdlRailWindow::isFullscreen ( ) const

Definition at line 420 of file sdl_rail_window.cpp.

421{
422 std::unique_lock lock(_gfxLock);
423 return _fullscreen;
424}

◆ isLayered()

bool SdlRailWindow::isLayered ( ) const

Definition at line 432 of file sdl_rail_window.cpp.

433{
434 std::unique_lock lock(_gfxLock);
435 return _layered;
436}

◆ isPopup()

bool SdlRailWindow::isPopup ( ) const

Definition at line 414 of file sdl_rail_window.cpp.

415{
416 std::unique_lock lock(_gfxLock);
417 return _isPopup;
418}

◆ localMoveActive()

bool SdlRailWindow::localMoveActive ( ) const

Definition at line 180 of file sdl_rail_window.cpp.

181{
182 std::unique_lock lock(_gfxLock);
183 return _localMoveActive;
184}

◆ localMoveServerPos()

SDL_Point SdlRailWindow::localMoveServerPos ( ) const

Definition at line 192 of file sdl_rail_window.cpp.

193{
194 std::unique_lock lock(_gfxLock);
195 return _localMoveServerPos;
196}

◆ localMoveSizeChanged()

bool SdlRailWindow::localMoveSizeChanged ( ) const

Definition at line 186 of file sdl_rail_window.cpp.

187{
188 std::unique_lock lock(_gfxLock);
189 return _localMoveSizeChanged;
190}

◆ loopEndPending()

bool SdlRailWindow::loopEndPending ( ) const

Definition at line 286 of file sdl_rail_window.cpp.

287{
288 std::unique_lock lock(_gfxLock);
289 return _loopEnd.pending;
290}

◆ markDeleted()

void SdlRailWindow::markDeleted ( )

Definition at line 235 of file sdl_rail_window.cpp.

236{
237 std::unique_lock lock(_gfxLock);
238 _deleted = true;
239}

◆ markGeometryDirty()

void SdlRailWindow::markGeometryDirty ( )
inline

Definition at line 196 of file sdl_rail_window.hpp.

197 {
198 std::unique_lock lock(_gfxLock);
199 _geometryDirty = true;
200 }

◆ noteLocalRestore()

void SdlRailWindow::noteLocalRestore ( )

Definition at line 1163 of file sdl_rail_window.cpp.

1164{
1165 std::unique_lock lock(_gfxLock);
1166 _restoreRetried = false;
1167 armDeadline(_localRestoreUntil, kAwaitRestoreRectMs);
1168}

◆ notePlaced()

void SdlRailWindow::notePlaced ( )

Definition at line 576 of file sdl_rail_window.cpp.

577{
578 std::unique_lock lock(_gfxLock);
579 placeRequested();
580}

◆ outerRect()

SDL_Rect SdlRailWindow::outerRect ( ) const

Definition at line 523 of file sdl_rail_window.cpp.

524{
525 std::unique_lock lock(_gfxLock);
526 SDL_Rect r = addInsets(_windowRect, _appliedInsets);
527 r.x += _localShift.x;
528 r.y += _localShift.y;
529 return r;
530}

◆ owner()

uint64_t SdlRailWindow::owner ( ) const

Definition at line 408 of file sdl_rail_window.cpp.

409{
410 std::unique_lock lock(_gfxLock);
411 return _ownerId;
412}

◆ paint()

bool SdlRailWindow::paint ( SDL_Surface *  primary,
SDL_PixelFormat  fallbackFormat,
const std::vector< SDL_Rect > &  damage,
SDL_Window *  parent,
const SDL_Rect &  parentRect 
)

Definition at line 1233 of file sdl_rail_window.cpp.

1236{
1237 if (!reconcile(parent, parentRect))
1238 return false;
1239
1240 auto ok = false;
1241 auto legacy = false;
1242 {
1243 std::unique_lock lock(_gfxLock);
1244 if (_hasGfx)
1245 ok = paintGfx(fallbackFormat);
1246 else
1247 legacy = true;
1248 }
1249 if (legacy)
1250 ok = paintLegacy(primary, damage);
1251
1252 /* Map in the same pass as the first frame, else the window defers a frame (menu lag). */
1253 if (_gfxPresented && !_mapped && !_minState.rail)
1254 {
1255 _mapped = true;
1256 WLog_DBG(TAG, "map id=0x%08" PRIx32 " %s", static_cast<uint32_t>(_id), role());
1257 SDL_ShowWindow(_win->window());
1258 /* Reassert target position after show to override stale events. */
1259 if (_isPopup && parent)
1260 {
1261 std::unique_lock lock(_gfxLock);
1262 const SDL_Rect vis = targetOuterRect();
1263 placePopup(vis, parentRect);
1264 }
1265 if (((!_isPopup && !_layered) || _fullscreen) && ((_exStyle & WS_EX_NOACTIVATE) == 0))
1266 _win->raise(); /* Bring app and fullscreen windows to front. */
1267 }
1268 return ok;
1269}

◆ placedRel()

bool SdlRailWindow::placedRel ( SDL_Point &  rel) const

Definition at line 636 of file sdl_rail_window.cpp.

637{
638 std::unique_lock lock(_gfxLock);
639 /* Only while moving: a resting popup may sit elsewhere (off screen), map it as on X11. */
640 if (!_placedRel || (SDL_GetTicks() - _requestedAt >= kMoveActiveMs))
641 return false;
642 rel = *_placedRel;
643 return true;
644}

◆ placePopup()

void SdlRailWindow::placePopup ( const SDL_Rect &  vis,
const SDL_Rect &  parentRect 
)

Definition at line 601 of file sdl_rail_window.cpp.

602{
603 const SDL_Point rel = { vis.x - parentRect.x, vis.y - parentRect.y };
604 const bool wayland = !railPlatformCaps().supportsWindowPositions;
605 const bool moved = (rel.x != _requestedRel.x) || (rel.y != _requestedRel.y);
606 if (wayland)
607 {
608 _requestedRel = rel;
609 _requestedAt = SDL_GetTicks();
610 }
611 if (!SDL_SetWindowPosition(_win->window(), rel.x, rel.y))
612 {
613 WLog_VRB(TAG, "popup reposition unsupported id=0x%08" PRIx32 ": %s",
614 static_cast<uint32_t>(_id), SDL_GetError());
615 return;
616 }
617 if (!wayland)
618 return;
619 _needsFullBlit = true; /* Wayland applies move on next surface commit */
620 if (!moved || !_mapped)
621 return;
622 _placeInFlight = true;
623 if (SDL_AddTimer(static_cast<Uint32>(kPlaceWaitMs + 1), placeWaitTimer, nullptr) == 0)
624 WLog_WARN(TAG, "popup placement timer failed: %s", SDL_GetError());
625}

◆ railMaximized()

bool SdlRailWindow::railMaximized ( ) const
inline

Definition at line 172 of file sdl_rail_window.hpp.

173 {
174 return _maxState.rail;
175 }

◆ railMinimized()

bool SdlRailWindow::railMinimized ( ) const
inline

Definition at line 182 of file sdl_rail_window.hpp.

183 {
184 return _minState.rail;
185 }

◆ reconcile()

bool SdlRailWindow::reconcile ( SDL_Window *  parent,
const SDL_Rect &  parentRect 
)

Definition at line 772 of file sdl_rail_window.cpp.

773{
774 std::unique_lock lock(_gfxLock);
775
776 /* Hidden windows get no local SDL window. Layered windows (e.g. docks) need GFX content and
777 * vis-rects to show; this hides empty DWM snap overlays. */
778 const bool popupReady = !_isPopup || _hasGfx;
779 /* Suppress server shadow frames and unparented full-display overlays. */
780 const bool unplaceable =
781 _frame || (_overlay && !railPlatformCaps().supportsWindowPositions && isFullDisplaySize());
782 /* Layered window is shown only when anchored to an adjoining visible popup or overlay. */
783 const bool drawable =
784 _visible && (_windowRect.w > 0) && (_windowRect.h > 0) && popupReady && !unplaceable &&
785 (!_layered || (_hasGfx && !_visRects.empty() && (_shadowAnchored || _overlay)));
786
787 if (!drawable)
788 {
789 /* Log standalone layered windows suppressed by popup shadow heuristic. */
790 if (_layered && _hasGfx && !_visRects.empty() && !_shadowAnchored && !_overlay &&
791 !_shadowSuppressLogged)
792 {
793 _shadowSuppressLogged = true;
794 WLog_DBG(TAG,
795 "shadow-rule suppressed id=0x%08" PRIx32 " %dx%d style=0x%08" PRIx32
796 " ex=0x%08" PRIx32,
797 static_cast<uint32_t>(_id), _windowRect.w, _windowRect.h, _style, _exStyle);
798 }
799 if (_win)
800 {
801 if ((SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_HIDDEN) == 0)
802 {
803 WLog_DBG(TAG, "hide id=0x%08" PRIx32 " %s", static_cast<uint32_t>(_id), role());
804 SDL_HideWindow(_win->window());
805 }
806 _mapped = false;
807 }
808 if (_isPopup && !_visible)
809 {
810 _gfxPresented = false;
811 _hasGfx = false;
812 }
813 return false;
814 }
815
816 if (!_win)
817 {
818 if (!create(parent, parentRect))
819 return false;
820 _geometryDirty = false;
821 /* Shown + raised in paint() after the first frame (created hidden). */
822 }
823
824 /* create() returned false on failure, so _win is non-null from here down. */
825 if (_topmostDirty)
826 {
827 _topmostDirty = false;
828 /* X11 only; Wayland has no protocol for this, the call still returns true. */
829 if (!_isPopup && !_layered)
830 std::ignore = SDL_SetWindowAlwaysOnTop(_win->window(), _topmost);
831 }
832
833 /* Resizability first: SDL refuses to maximize a non-resizable window. */
834 if (_styleDirty)
835 {
836 _win->resizeable(styleResizable());
837 _styleDirty = false;
838 /* Insets refresh on geometry updates to avoid flicker. */
839 }
840
841 /* State first: maximized/minimized gates the geometry apply below. */
842 /* Hold geometry sync until server sends restored rect to prevent re-maximizing. */
843 if (applyServerState(_maxState, "maximize", SDL_MaximizeWindow))
844 armDeadline(_awaitRestoreUntil, kAwaitRestoreRectMs);
845 /* Still minimized after our SC_RESTORE: ask once more, then follow the server. */
846 if ((_localRestoreUntil != 0) && (SDL_GetTicks() >= _localRestoreUntil))
847 {
848 _localRestoreUntil = 0;
849 if (_minState.server && !_minState.rail && !_restoreRetried)
850 {
851 _restoreRetried = true;
852 _restoreRetry = true;
853 armDeadline(_localRestoreUntil, kAwaitRestoreRectMs);
854 }
855 else if (_minState.server != _minState.rail)
856 _minState.dirty = true;
857 }
858 std::ignore = applyServerState(_minState, "minimize", SDL_MinimizeWindow);
859 const bool restorePending = restoreRectPending();
860
861 /* Update insets across maximize transitions outside active drag grab. */
862 const bool maxed = effectivelyMaximized();
863 const SDL_Rect band = bandInsets();
864 if ((maxed != _wasMaximized) && !_localMoveActive && !restorePending)
865 {
866 setAppliedInsets(band);
867 if (!maxed)
868 _geometryDirty = true;
869 _wasMaximized = maxed;
870 }
871
872 /* X11: set frame extents before the resize (KWin needs that), not while restoring. */
873 if ((railPlatformCaps().band == RailBandMechanism::FrameExtents) && !restorePending &&
874 !SDL_RectsEqual(&band, &_extentsApplied) && railPlatformSetBand(_win->window(), band))
875 {
876 _extentsApplied = band;
877 }
878 const bool wmOwnsGeometry =
879 _localMoveActive || geometryFrozen() || _fullscreen || restorePending;
880 /* Clamp min size hints to target bounds so stale hints do not block programmatic resize.
881 * Defer size hints and programmatic resize during active WM move/resize grab. */
882 if ((_minMaxDirty || _geometryDirty) && !wmOwnsGeometry)
883 {
884 const SDL_Rect vis = targetOuterRect();
885 const int bandW = band.x + band.w;
886 const int bandH = band.y + band.h;
887 /* Convert server track size hints (which include server margins) to outer window bounds. */
888 const SDL_Rect& m = _resizeMargins;
889 const int padW = bandW - m.x - m.w;
890 const int padH = bandH - m.y - m.h;
891 const auto toOuter = [](int track, int pad)
892 { return (track > 0) ? std::max(1, track + pad) : 0; };
893 const int minW = toOuter(_minSize.x, padW);
894 const int minH = toOuter(_minSize.y, padH);
895 SDL_SetWindowMinimumSize(_win->window(), std::clamp(minW, 0, vis.w),
896 std::clamp(minH, 0, vis.h));
897 int maxW = toOuter(_maxSize.x, padW);
898 int maxH = toOuter(_maxSize.y, padH);
899 /* Wayland: cap to the usable area - an oversized window cannot be dragged into reach. */
900 SDL_Rect usable{};
901 if (!railPlatformCaps().supportsWindowPositions &&
902 SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &usable))
903 {
904 const int capW = usable.w + bandW;
905 const int capH = usable.h + bandH;
906 if ((maxW == 0) || (maxW > capW))
907 maxW = capW;
908 if ((maxH == 0) || (maxH > capH))
909 maxH = capH;
910 }
911 SDL_SetWindowMaximumSize(_win->window(), maxW, maxH);
912 _minMaxDirty = false;
913 }
914
915 /* Skip geometry apply when maximized, minimized or WM-dragged. */
916 if (_geometryDirty && !wmOwnsGeometry)
917 {
918 const SDL_Rect vis = targetOuterRect();
919 /* Record applied insets so server coordinate mapping strips the exact offset. */
920 setAppliedInsets(band);
921 int cw = 0;
922 int ch = 0;
923 SDL_GetWindowSize(_win->window(), &cw, &ch);
924 bool applied = false;
925 bool placed = false; /* railPlatformSetWindowRect set position and size at once */
926 /* Apply position and size if changed. */
927 if (_isPopup && parent)
928 {
929 placePopup(vis, parentRect);
930 applied = true;
931 }
932 else if (railPlatformCaps().supportsWindowPositions &&
933 !(_isPopup && SDL_GetWindowParent(_win->window())))
934 {
935 int cx = 0;
936 int cy = 0;
937 SDL_GetWindowPosition(_win->window(), &cx, &cy);
938 if ((cx != vis.x) || (cy != vis.y))
939 {
940 placed = railPlatformSetWindowRect(_win->window(), vis);
941 if (!placed)
942 SDL_SetWindowPosition(_win->window(), vis.x, vis.y);
943 applied = true;
944 }
945 }
946 if ((cw != vis.w) || (ch != vis.h))
947 {
948 if (!placed && !railPlatformSetWindowRect(_win->window(), vis))
949 std::ignore = _win->resize({ vis.w, vis.h });
950 applied = true;
951 /* Settle window resize via SDL_SyncWindow before reading back geometry to avoid
952 * adopting transient dimensions while configure events are in flight. */
953 if (railPlatformCaps().supportsWindowPositions && !_isPopup && !_localMoveActive &&
954 !_loopEnd.pending)
955 {
956 const bool settled = SDL_SyncWindow(_win->window());
957 int aw = 0;
958 int ah = 0;
959 SDL_GetWindowSize(_win->window(), &aw, &ah);
960 if (settled && ((aw != vis.w) || (ah != vis.h)))
961 {
962 /* Adopt WM size while preserving server origin. */
963 _wmRefusedOuter = { vis.x, vis.y, aw, ah };
964 _wmRefused = true;
965 applied = false; /* Geometry refused by WM; do not await echo. */
966 }
967 }
968 }
969 _geometryDirty = false;
970 /* Echo filtering is only tracked for top-level app windows. */
971 _geomApplyPending = applied && !_isPopup;
972 }
973 /* Make dialog transient for owner. */
974 /* Attach only to a shown owner: SDL hides child windows with their parent. */
975 if (!_isPopup && parent && !_parentApplied &&
976 ((SDL_GetWindowFlags(parent) & SDL_WINDOW_HIDDEN) == 0))
977 {
978 if (SDL_SetWindowParent(_win->window(), parent))
979 {
980 _parentApplied = true;
981 /* Reattached after the owner was shown again: the owner was mapped on top. */
982 if (((SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_HIDDEN) == 0) &&
983 !railPlatformRaiseOwned(_win->window()))
984 WLog_WARN(TAG, "raising dialog id=0x%08" PRIx32 " failed",
985 static_cast<uint32_t>(_id));
986 }
987 }
988 if (_titleDirty)
989 {
990 if (!_isPopup)
991 SDL_SetWindowTitle(_win->window(), _title.c_str());
992 _titleDirty = false;
993 }
994 if (_iconDirty)
995 {
996 if (!_isPopup && !_icon.bgra.empty())
997 {
998 SDL_Surface* s = SDL_CreateSurfaceFrom(
999 static_cast<int>(_icon.w), static_cast<int>(_icon.h), SDL_PIXELFORMAT_BGRA32,
1000 _icon.bgra.data(), static_cast<int>(_icon.w * 4));
1001 if (s)
1002 {
1003 if (!SDL_SetWindowIcon(_win->window(), s))
1004 WLog_WARN(TAG, "SDL_SetWindowIcon failed for window 0x%08" PRIx32 ": %s",
1005 static_cast<uint32_t>(_id), SDL_GetError());
1006 SDL_DestroySurface(s);
1007 }
1008 }
1009 _iconDirty = false;
1010 }
1011
1012 /* Defer show until first frame; this runs every reconcile, so skip the call once visible. */
1013 if (!_minState.rail && _gfxPresented &&
1014 (SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_HIDDEN))
1015 {
1016 /* If transient-for owner was closed or hidden, show dialog standalone. */
1017 SDL_Window* owner = _isPopup ? nullptr : SDL_GetWindowParent(_win->window());
1018 if (owner && ((SDL_GetWindowFlags(owner) & SDL_WINDOW_HIDDEN) != 0))
1019 {
1020 if (SDL_SetWindowParent(_win->window(), nullptr))
1021 _parentApplied = false; /* reattach once the owner is shown again */
1022 else
1023 WLog_WARN(TAG, "detaching dialog id=0x%08" PRIx32 " failed: %s",
1024 static_cast<uint32_t>(_id), SDL_GetError());
1025 }
1026 SDL_ShowWindow(_win->window());
1027 }
1028 return true;
1029}

◆ releaseWindow()

void SdlRailWindow::releaseWindow ( )

Definition at line 241 of file sdl_rail_window.cpp.

242{
243 std::unique_lock lock(_gfxLock);
244 if (!_win)
245 return;
246 WLog_DBG(TAG, "release id=0x%08" PRIx32 " %s%s", static_cast<uint32_t>(_id), role(),
247 _deleted ? "" : " (SDL parent gone, re-created on next pass)");
248 _win.reset();
249 /* Back to the "never created" state: create() + first frame + map run again. */
250 _mapped = false;
251 _painted = false;
252 _gfxPresented = false;
253 _needsFullBlit = true;
254 _geometryDirty = true;
255 _parentApplied = false;
256 _fullscreen = false;
257 _appliedInsets = { 0, 0, 0, 0 };
258 _extentsApplied = { 0, 0, 0, 0 };
259 _lastWinW = _lastWinH = _lastGfxW = _lastGfxH = 0;
260}

◆ renderer()

SDL_Renderer * SdlRailWindow::renderer ( ) const

Definition at line 107 of file sdl_rail_window.cpp.

108{
109 return _win ? _win->renderer() : nullptr;
110}

◆ resizeMargins()

SDL_Rect SdlRailWindow::resizeMargins ( ) const

Definition at line 371 of file sdl_rail_window.cpp.

372{
373 std::unique_lock lock(_gfxLock);
374 return _resizeMargins;
375}

◆ sdlId()

SDL_WindowID SdlRailWindow::sdlId ( ) const

Definition at line 97 of file sdl_rail_window.cpp.

98{
99 return _win ? _win->id() : 0;
100}

◆ serverOrigin()

SDL_Point SdlRailWindow::serverOrigin ( ) const

Definition at line 646 of file sdl_rail_window.cpp.

647{
648 std::unique_lock lock(_gfxLock);
649 /* Derive server coordinate from blit offset to match on-screen pixels. */
650 const SDL_Point dst = blitOffset();
651 return { _windowRect.x - dst.x, _windowRect.y - dst.y };
652}

◆ serverRect()

SDL_Rect SdlRailWindow::serverRect ( const SDL_Rect &  outer) const

Definition at line 532 of file sdl_rail_window.cpp.

533{
534 SDL_Rect r = stripInsets(outer, insets());
535 r.x -= _localShift.x;
536 r.y -= _localShift.y;
537 return r;
538}

◆ setFrame()

void SdlRailWindow::setFrame ( bool  frame)

Definition at line 444 of file sdl_rail_window.cpp.

445{
446 std::unique_lock lock(_gfxLock);
447 _frame = frame;
448}

◆ setFrameMargins()

void SdlRailWindow::setFrameMargins ( const SDL_Rect &  m)

Definition at line 377 of file sdl_rail_window.cpp.

378{
379 std::unique_lock lock(_gfxLock);
380 _frameMargins = m;
381}

◆ setIcon()

void SdlRailWindow::setIcon ( const SdlRailIcon &  icon)

Definition at line 692 of file sdl_rail_window.cpp.

693{
694 std::unique_lock lock(_gfxLock);
695 _icon = icon;
696 _iconDirty = true;
697}

◆ setLocalMoveActive()

void SdlRailWindow::setLocalMoveActive ( bool  active)

Definition at line 161 of file sdl_rail_window.cpp.

162{
163 std::unique_lock lock(_gfxLock);
164 _localMoveActive = active;
165 if (active)
166 {
167 _localMoveIsResize = false; /* default to move; setResizeAnchor marks a resize */
168 _localMoveSizeChanged = false;
169 }
170}

◆ setLocalShift()

void SdlRailWindow::setLocalShift ( const SDL_Point &  shift)

Definition at line 540 of file sdl_rail_window.cpp.

541{
542 std::unique_lock lock(_gfxLock);
543 _localShift = shift;
544}

◆ setMinMaxSize()

void SdlRailWindow::setMinMaxSize ( SDL_Point  minSize,
SDL_Point  maxSize 
)

Definition at line 349 of file sdl_rail_window.cpp.

350{
351 std::unique_lock lock(_gfxLock);
352 _minSize = { std::max(0, minSize.x), std::max(0, minSize.y) };
353 _maxSize = { std::max(0, maxSize.x), std::max(0, maxSize.y) };
354 _minMaxDirty = true;
355 /* Re-apply geometry on min/max change. */
356 _geometryDirty = true;
357}

◆ setOwner()

void SdlRailWindow::setOwner ( uint64_t  ownerId)

Definition at line 402 of file sdl_rail_window.cpp.

403{
404 std::unique_lock lock(_gfxLock);
405 _ownerId = ownerId;
406}

◆ setRailMaximized()

void SdlRailWindow::setRailMaximized ( bool  m)
inline

Definition at line 176 of file sdl_rail_window.hpp.

177 {
178 std::unique_lock lock(_gfxLock);
179 _maxState.rail = m;
180 }

◆ setRailMinimized()

void SdlRailWindow::setRailMinimized ( bool  m)
inline

Definition at line 186 of file sdl_rail_window.hpp.

187 {
188 std::unique_lock lock(_gfxLock);
189 _minState.rail = m;
190 }

◆ setResizeAnchor()

void SdlRailWindow::setResizeAnchor ( bool  right,
bool  bottom 
)

Definition at line 172 of file sdl_rail_window.cpp.

173{
174 std::unique_lock lock(_gfxLock);
175 _resizeAnchorRight = right;
176 _resizeAnchorBottom = bottom;
177 _localMoveIsResize = true;
178}

◆ setResizeMargins()

void SdlRailWindow::setResizeMargins ( int  left,
int  top,
int  right,
int  bottom 
)

Definition at line 359 of file sdl_rail_window.cpp.

360{
361 std::unique_lock lock(_gfxLock);
362 const SDL_Rect m = { left, top, right, bottom };
363 if (SDL_RectsEqual(&m, &_resizeMargins))
364 return;
365 _resizeMargins = m;
366 /* Margins usually arrive after the first frame; the window must regrow to cover them. */
367 if (!railMaximized())
368 _geometryDirty = true;
369}

◆ setServerMaximized()

void SdlRailWindow::setServerMaximized ( bool  m)

Definition at line 1142 of file sdl_rail_window.cpp.

1143{
1144 setServerState(_maxState, m);
1145}

◆ setServerMinimized()

void SdlRailWindow::setServerMinimized ( bool  m)

Definition at line 1147 of file sdl_rail_window.cpp.

1148{
1149 {
1150 std::unique_lock lock(_gfxLock);
1151 /* Late minimize of a window the user already restored. */
1152 if (m && (_localRestoreUntil != 0) && (SDL_GetTicks() < _localRestoreUntil))
1153 {
1154 _minState.server = true;
1155 return;
1156 }
1157 if (!m)
1158 _localRestoreUntil = 0;
1159 }
1160 setServerState(_minState, m);
1161}

◆ setShadowAnchored()

void SdlRailWindow::setShadowAnchored ( bool  anchored)

Definition at line 438 of file sdl_rail_window.cpp.

439{
440 std::unique_lock lock(_gfxLock);
441 _shadowAnchored = anchored;
442}

◆ setStyle()

void SdlRailWindow::setStyle ( uint32_t  style,
uint32_t  exStyle 
)

Definition at line 654 of file sdl_rail_window.cpp.

655{
656 std::unique_lock lock(_gfxLock);
657 const bool wasTopmost = _topmost;
658 _style = style;
659 _exStyle = exStyle;
660 _topmost = (exStyle & WS_EX_TOPMOST) != 0;
661 if (_topmost != wasTopmost)
662 _topmostDirty = true;
663 if (!_everResizable && ((style & (WS_THICKFRAME | WS_MAXIMIZEBOX)) != 0))
664 {
665 _everResizable = true;
666 _styleDirty = true;
667 }
668 /* Classify popup and layered window types once on creation. */
669 if (!_popupClassified)
670 {
671 const auto classification = classifySdlRailWindowStyle(style, exStyle, _ownerId != 0);
672 _isPopup = classification.popup;
673 _layered = classification.layered;
674 _overlay = classification.overlay;
675 _popupClassified = true;
676 }
677}

◆ setTitle()

void SdlRailWindow::setTitle ( const std::string &  title)

Definition at line 679 of file sdl_rail_window.cpp.

680{
681 std::unique_lock lock(_gfxLock);
682 _title = title;
683 _titleDirty = true;
684}

◆ setVisibilityRects()

void SdlRailWindow::setVisibilityRects ( std::vector< SDL_Rect >  rects)

Definition at line 329 of file sdl_rail_window.cpp.

330{
331 std::unique_lock lock(_gfxLock);
332 _visRects = std::move(rects);
333 _visDirty = true;
334}

◆ setVisible()

void SdlRailWindow::setVisible ( bool  visible)

Definition at line 686 of file sdl_rail_window.cpp.

687{
688 std::unique_lock lock(_gfxLock);
689 _visible = visible;
690}

◆ setVisibleOffset()

void SdlRailWindow::setVisibleOffset ( SDL_Point  offset)

Definition at line 336 of file sdl_rail_window.cpp.

337{
338 std::unique_lock lock(_gfxLock);
339 /* Freeze visible offset during local move. */
340 if (_localMoveActive)
341 return;
342 if (_visOffsetSet && (offset.x == _visOffset.x) && (offset.y == _visOffset.y))
343 return;
344 _visOffset = offset;
345 _visOffsetSet = true;
346 _visDirty = true;
347}

◆ stateTransitionPending()

bool SdlRailWindow::stateTransitionPending ( ) const
inline

Definition at line 217 of file sdl_rail_window.hpp.

218 {
219 /* _maxState/_minState are written under _gfxLock on the RDP thread. */
220 std::unique_lock lock(_gfxLock);
221 return _maxState.dirty || _minState.dirty || (_maxState.rail != _maxState.server) ||
222 (_minState.rail != _minState.server);
223 }

◆ takeLoopEnd()

SdlRailWindow::LoopEndActions SdlRailWindow::takeLoopEnd ( )

Definition at line 311 of file sdl_rail_window.cpp.

312{
313 std::unique_lock lock(_gfxLock);
314 const LoopEndActions actions{ _loopEnd.maximize, _loopEnd.snap, _loopEnd.snapRect };
315 _loopEnd = {};
316 return actions;
317}

◆ takeRestoreRetry()

bool SdlRailWindow::takeRestoreRetry ( )

Definition at line 1170 of file sdl_rail_window.cpp.

1171{
1172 std::unique_lock lock(_gfxLock);
1173 return std::exchange(_restoreRetry, false);
1174}

◆ takeSurfaceChange()

bool SdlRailWindow::takeSurfaceChange ( uint32_t  surfaceId)

Definition at line 1031 of file sdl_rail_window.cpp.

1032{
1033 std::unique_lock lock(_gfxLock);
1034 if (_surfaceId == surfaceId)
1035 return false;
1036 _surfaceId = surfaceId;
1037 return true;
1038}

◆ takeUnconfirmedReport()

bool SdlRailWindow::takeUnconfirmedReport ( SDL_Rect &  rect)

Definition at line 389 of file sdl_rail_window.cpp.

390{
391 std::unique_lock lock(_gfxLock);
392 if ((_reportCheckAt == 0) || (SDL_GetTicks() < _reportCheckAt))
393 return false;
394 _reportCheckAt = 0;
395 if (_reported.empty())
396 return false;
397 rect = _reported.back();
398 _reported.clear();
399 return true;
400}

◆ takeWmOverride()

bool SdlRailWindow::takeWmOverride ( SDL_Rect &  outer)

Definition at line 319 of file sdl_rail_window.cpp.

320{
321 std::unique_lock lock(_gfxLock);
322 if (!_wmRefused)
323 return false;
324 outer = _wmRefusedOuter;
325 _wmRefused = false;
326 return true;
327}

◆ updateGfxSurface()

void SdlRailWindow::updateGfxSurface ( const void *  data,
uint32_t  stride,
uint32_t  width,
uint32_t  height,
const RECTANGLE_16 *  damage,
uint32_t  nbDamage,
uint32_t  format 
)

Definition at line 1040 of file sdl_rail_window.cpp.

1043{
1044 std::unique_lock lock(_gfxLock);
1045 /* The copies below are byte-for-byte; gdi_CreateSurface yields nothing else. */
1046 WINPR_ASSERT((format == PIXEL_FORMAT_BGRA32) || (format == PIXEL_FORMAT_BGRX32));
1047 /* Deep-copy GDI pixels to prevent UAF. */
1048 const size_t bytes = static_cast<size_t>(stride) * height;
1049 if (!data || (bytes == 0) || (width == 0) || (height == 0))
1050 {
1051 if (_hasGfx)
1052 WLog_DBG(TAG, "gfx cleared id=0x%08" PRIx32 " (surface unmapped)",
1053 static_cast<uint32_t>(_id));
1054 _gfxBuffer.clear();
1055 _hasGfx = false;
1056 _gfxDamage.clear();
1057 _gfxStride = stride;
1058 _gfxW = width;
1059 _gfxH = height;
1060 _gfxVisOrigin = { 0, 0 };
1061 return;
1062 }
1063
1064 const auto* src = static_cast<const uint8_t*>(data);
1065 /* Geometry/stride change, first frame, or no damage rects: full copy + repaint. */
1066 const bool full = !_hasGfx || (_gfxBuffer.size() != bytes) || (_gfxStride != stride) ||
1067 (_gfxW != width) || (_gfxH != height) || (nbDamage == 0);
1068 if (full)
1069 {
1070 const SDL_Rect bounds{ 0, 0, static_cast<int>(width), static_cast<int>(height) };
1071 /* Check _hasGfx first to avoid redundant full-surface blank checks once content exists. */
1072 if (!_hasGfx && regionIsBlank(src, stride, bounds))
1073 return;
1074 _gfxBuffer.assign(src, src + bytes);
1075 _gfxDamage.assign(1, SDL_Rect{ 0, 0, static_cast<int>(width), static_cast<int>(height) });
1076 _needsFullBlit = true;
1077 /* Blend real alpha, force opaque otherwise. */
1078 _gfxHasAlpha = honorsAlpha() &&
1079 regionHasAlpha(_gfxBuffer.data(), stride,
1080 { 0, 0, static_cast<int>(width), static_cast<int>(height) });
1081 }
1082 else
1083 {
1084 const SDL_Rect bounds{ 0, 0, static_cast<int>(width), static_cast<int>(height) };
1085 for (uint32_t i = 0; i < nbDamage; i++)
1086 {
1087 const SDL_Rect r{ damage[i].left, damage[i].top, damage[i].right - damage[i].left,
1088 damage[i].bottom - damage[i].top };
1089 SDL_Rect clip{};
1090 if (!SDL_GetRectIntersection(&r, &bounds, &clip))
1091 continue;
1092 std::ignore = freerdp_image_copy_no_overlap(
1093 _gfxBuffer.data(), PIXEL_FORMAT_BGRA32, stride, static_cast<UINT32>(clip.x),
1094 static_cast<UINT32>(clip.y), static_cast<UINT32>(clip.w),
1095 static_cast<UINT32>(clip.h), src, PIXEL_FORMAT_BGRA32, stride,
1096 static_cast<UINT32>(clip.x), static_cast<UINT32>(clip.y), nullptr,
1097 FREERDP_FLIP_NONE);
1098 _gfxDamage.push_back(clip);
1099 /* Alpha may first appear in a later incremental frame (e.g. a menu fading in). */
1100 if (honorsAlpha() && !_gfxHasAlpha && regionHasAlpha(_gfxBuffer.data(), stride, clip))
1101 _gfxHasAlpha = true;
1102 }
1103 /* A hidden window accumulates rects without ever painting; collapse to one full repaint. */
1104 if (_gfxDamage.size() > 32)
1105 _gfxDamage.assign(1,
1106 SDL_Rect{ 0, 0, static_cast<int>(width), static_cast<int>(height) });
1107 }
1108 if ((_gfxW != width) || (_gfxH != height))
1109 {
1110 _resizeAnchorRight = false;
1111 _resizeAnchorBottom = false;
1112 _needsFullBlit = true;
1113 /* Invalidate surface origin until visibility rect matching new dimensions arrives. */
1114 _gfxVisOrigin = { 0, 0 };
1115 }
1116 _hasGfx = true;
1117 _gfxStride = stride;
1118 _gfxW = width;
1119 _gfxH = height;
1120 WLog_VRB(TAG, "gfx id=0x%08" PRIx32 " %ux%u full=%d nDamage=%u", static_cast<uint32_t>(_id),
1121 width, height, full ? 1 : 0, nbDamage);
1122}

◆ updateWindowRect()

void SdlRailWindow::updateWindowRect ( const SDL_Rect &  rect)

Definition at line 112 of file sdl_rail_window.cpp.

113{
114 std::unique_lock lock(_gfxLock);
115 /* Minimized, Windows parks the window off screen; keep the rect to restore to. */
116 if (_minState.server)
117 return;
118 _awaitRestoreUntil = 0; /* Server geometry received. */
119 if (_localMoveActive)
120 {
121 /* Adopt server size change during local move. */
122 if (!_localMoveIsResize && ((rect.w != _windowRect.w) || (rect.h != _windowRect.h)))
123 {
124 WLog_DBG(TAG, "size adopted mid-move id=0x%08" PRIx32 " %dx%d -> %dx%d at %d,%d",
125 static_cast<uint32_t>(_id), _windowRect.w, _windowRect.h, rect.w, rect.h,
126 rect.x, rect.y);
127 _windowRect.w = rect.w;
128 _windowRect.h = rect.h;
129 /* Record server re-anchor position. */
130 _localMoveServerPos = { rect.x, rect.y };
131 _localMoveSizeChanged = true;
132 _painted = false;
133 }
134 return;
135 }
136 /* Accept post-move geometry echoes. */
137 if (SDL_RectsEqual(&rect, &_windowRect))
138 {
139 _reported.clear();
140 return;
141 }
142 /* The echo of an older report can arrive after a newer one was sent. */
143 const auto echo = std::find_if(_reported.begin(), _reported.end(),
144 [&](const SDL_Rect& r) { return SDL_RectsEqual(&r, &rect); });
145 if (echo != _reported.end())
146 {
147 WLog_DBG(TAG, "late echo id=0x%08" PRIx32 " %dx%d ignored", static_cast<uint32_t>(_id),
148 rect.w, rect.h);
149 _reported.erase(_reported.begin(), echo + 1);
150 return;
151 }
152 _reported.clear();
153 /* A resize recreates the render target, so the content needs a full re-copy. */
154 if ((rect.w != _windowRect.w) || (rect.h != _windowRect.h))
155 _painted = false;
156 _windowRect = rect;
157 /* Mark dirty even if frozen/maximized so pending restore rect applies upon unfreeze. */
158 _geometryDirty = true;
159}

◆ window()

SDL_Window * SdlRailWindow::window ( ) const

Definition at line 102 of file sdl_rail_window.cpp.

103{
104 return _win ? _win->window() : nullptr;
105}

◆ windowRect()

SDL_Rect SdlRailWindow::windowRect ( ) const

Definition at line 229 of file sdl_rail_window.cpp.

230{
231 std::unique_lock lock(_gfxLock);
232 return _windowRect;
233}

The documentation for this class was generated from the following files: