23#include <freerdp/codec/color.h>
24#include <freerdp/log.h>
25#include <freerdp/window.h>
27#include "sdl_rail_platform.hpp"
28#include "sdl_rail_window.hpp"
29#include "sdl_wayland.hpp"
30#include "sdl_window.hpp"
33#define TAG CLIENT_TAG("sdl.rail.window")
36static constexpr uint64_t kAwaitServerFrameMs = 1000;
39static constexpr uint64_t kAwaitRestoreRectMs = 500;
42static bool regionHasAlpha(
const uint8_t* base, uint32_t stride,
const SDL_Rect& r)
44 for (
int y = r.y; y < r.y + r.h; y++)
46 const uint8_t* row = base +
static_cast<size_t>(y) * stride;
47 for (
int x = r.x; x < r.x + r.w; x++)
48 if (row[
static_cast<size_t>(x) * 4 + 3] != 0)
54static bool regionIsBlank(
const uint8_t* base, uint32_t stride,
const SDL_Rect& r)
56 for (
int y = r.y; y < r.y + r.h; y++)
58 const auto* row =
reinterpret_cast<const uint32_t*
>(base +
static_cast<size_t>(y) * stride);
59 for (
int x = r.x; x < r.x + r.w; x++)
66SdlRailWindow::SdlRailWindow(uint64_t
id,
const SDL_Rect& rect) : _id(id), _windowRect(rect)
71SdlRailWindow::~SdlRailWindow() =
default;
73uint64_t SdlRailWindow::id()
const
78SDL_WindowID SdlRailWindow::sdlId()
const
80 return _win ? _win->id() : 0;
83SDL_Window* SdlRailWindow::window()
const
85 return _win ? _win->window() :
nullptr;
88SDL_Renderer* SdlRailWindow::renderer()
const
90 return _win ? _win->renderer() :
nullptr;
93void SdlRailWindow::updateWindowRect(
const SDL_Rect& rect)
95 std::unique_lock lock(_gfxLock);
96 _awaitRestoreUntil = 0;
100 if (!_localMoveIsResize && ((rect.w != _windowRect.w) || (rect.h != _windowRect.h)))
102 WLog_DBG(TAG,
"size adopted mid-move id=0x%08" PRIx32
" %dx%d -> %dx%d at %d,%d",
103 static_cast<uint32_t
>(_id), _windowRect.w, _windowRect.h, rect.w, rect.h,
105 _windowRect.w = rect.w;
106 _windowRect.h = rect.h;
108 _localMoveServerPos = { rect.x, rect.y };
109 _localMoveSizeChanged =
true;
115 if (SDL_RectsEqual(&rect, &_windowRect))
118 if ((rect.w != _windowRect.w) || (rect.h != _windowRect.h))
122 _geometryDirty =
true;
125void SdlRailWindow::setLocalMoveActive(
bool active)
127 std::unique_lock lock(_gfxLock);
128 _localMoveActive = active;
131 _localMoveIsResize =
false;
132 _localMoveSizeChanged =
false;
136void SdlRailWindow::setResizeAnchor(
bool right,
bool bottom)
138 std::unique_lock lock(_gfxLock);
139 _resizeAnchorRight = right;
140 _resizeAnchorBottom = bottom;
141 _localMoveIsResize =
true;
144bool SdlRailWindow::localMoveActive()
const
146 std::unique_lock lock(_gfxLock);
147 return _localMoveActive;
150bool SdlRailWindow::localMoveSizeChanged()
const
152 std::unique_lock lock(_gfxLock);
153 return _localMoveSizeChanged;
156SDL_Point SdlRailWindow::localMoveServerPos()
const
158 std::unique_lock lock(_gfxLock);
159 return _localMoveServerPos;
162void SdlRailWindow::adoptLocalGeometry(
const SDL_Rect& rect)
165 std::unique_lock lock(_gfxLock);
167 _geomApplyPending =
false;
171 _visOffset.x += rect.x - _windowRect.x;
172 _visOffset.y += rect.y - _windowRect.y;
175 _geometryDirty =
false;
177 _awaitingFrameUntil =
178 (_localMoveActive && _localMoveIsResize) ? (SDL_GetTicks() + kAwaitServerFrameMs) : 0;
179 _localMoveActive =
false;
180 _needsFullBlit =
true;
183 _gfxDamage.assign(1, SDL_Rect{ 0, 0,
static_cast<int>(_gfxW),
static_cast<int>(_gfxH) });
186SDL_Rect SdlRailWindow::windowRect()
const
188 std::unique_lock lock(_gfxLock);
192void SdlRailWindow::markDeleted()
194 std::unique_lock lock(_gfxLock);
198bool SdlRailWindow::isDeleted()
const
200 std::unique_lock lock(_gfxLock);
204bool SdlRailWindow::geomApplyPending()
const
206 std::unique_lock lock(_gfxLock);
207 return _geomApplyPending;
210void SdlRailWindow::clearGeomApplyPending()
212 std::unique_lock lock(_gfxLock);
213 _geomApplyPending =
false;
216void SdlRailWindow::armLoopEnd()
218 std::unique_lock lock(_gfxLock);
219 _loopEnd.pending =
true;
222bool SdlRailWindow::loopEndPending()
const
224 std::unique_lock lock(_gfxLock);
225 return _loopEnd.pending;
228void SdlRailWindow::deferMaximize()
230 std::unique_lock lock(_gfxLock);
231 _loopEnd.maximize =
true;
234void SdlRailWindow::deferSnap(
const SDL_Rect& serverRect)
236 std::unique_lock lock(_gfxLock);
237 _loopEnd.snap =
true;
238 _loopEnd.snapRect = serverRect;
241void SdlRailWindow::clearLoopEnd()
243 std::unique_lock lock(_gfxLock);
249 std::unique_lock lock(_gfxLock);
250 const LoopEndActions actions{ _loopEnd.maximize, _loopEnd.snap, _loopEnd.snapRect };
255bool SdlRailWindow::takeWmOverride(SDL_Rect& outer)
257 std::unique_lock lock(_gfxLock);
260 outer = _wmRefusedOuter;
265void SdlRailWindow::setVisibilityRects(std::vector<SDL_Rect> rects)
267 std::unique_lock lock(_gfxLock);
268 _visRects = std::move(rects);
272void SdlRailWindow::setVisibleOffset(SDL_Point offset)
274 std::unique_lock lock(_gfxLock);
276 if (_localMoveActive)
278 if (_visOffsetSet && (offset.x == _visOffset.x) && (offset.y == _visOffset.y))
281 _visOffsetSet =
true;
285void SdlRailWindow::setMinMaxSize(SDL_Point minSize, SDL_Point maxSize)
287 std::unique_lock lock(_gfxLock);
288 _minSize = { std::max(0, minSize.x), std::max(0, minSize.y) };
289 _maxSize = { std::max(0, maxSize.x), std::max(0, maxSize.y) };
292 _geometryDirty =
true;
295void SdlRailWindow::setResizeMargins(
int left,
int top,
int right,
int bottom)
297 std::unique_lock lock(_gfxLock);
298 const SDL_Rect m = { left, top, right, bottom };
299 if (SDL_RectsEqual(&m, &_resizeMargins))
303 if (!railMaximized())
304 _geometryDirty =
true;
307SDL_Rect SdlRailWindow::resizeMargins()
const
309 std::unique_lock lock(_gfxLock);
310 return _resizeMargins;
313void SdlRailWindow::setFrameMargins(
const SDL_Rect& m)
315 std::unique_lock lock(_gfxLock);
319SDL_Rect SdlRailWindow::frameMargins()
const
321 std::unique_lock lock(_gfxLock);
322 return _frameMargins;
325void SdlRailWindow::setOwner(uint64_t ownerId)
327 std::unique_lock lock(_gfxLock);
331uint64_t SdlRailWindow::owner()
const
333 std::unique_lock lock(_gfxLock);
337bool SdlRailWindow::isPopup()
const
339 std::unique_lock lock(_gfxLock);
343bool SdlRailWindow::isFullscreen()
const
345 std::unique_lock lock(_gfxLock);
349bool SdlRailWindow::isLayered()
const
351 std::unique_lock lock(_gfxLock);
355void SdlRailWindow::setShadowAnchored(
bool anchored)
357 std::unique_lock lock(_gfxLock);
358 _shadowAnchored = anchored;
361void SdlRailWindow::setFrame(
bool frame)
363 std::unique_lock lock(_gfxLock);
367bool SdlRailWindow::isFrame()
const
369 std::unique_lock lock(_gfxLock);
373bool SdlRailWindow::styleResizable()
const
376 return _everResizable || ((_style & (WS_THICKFRAME | WS_MAXIMIZEBOX)) != 0);
380static constexpr int kMinGrip = 4;
382SDL_Rect SdlRailWindow::bandMargins()
const
384 if (!_visible || _isPopup || _layered || !styleResizable() || effectivelyMaximized())
385 return { 0, 0, 0, 0 };
387 const SDL_Rect& m = _resizeMargins;
388 return { std::max(m.x, kMinGrip), std::max(m.y, kMinGrip), std::max(m.w, kMinGrip),
389 std::max(m.h, kMinGrip) };
392SDL_Rect SdlRailWindow::bandInsets()
const
395 const auto& caps = railPlatformCaps();
396 if (!caps.positionsReadable || !caps.supportsTransparentWindows)
397 return { 0, 0, 0, 0 };
398 return bandMargins();
402static SDL_Rect addInsets(
const SDL_Rect& r,
const SDL_Rect& i)
404 return { r.x - i.x, r.y - i.y, r.w + i.x + i.w, r.h + i.y + i.h };
409static SDL_Rect stripInsets(
const SDL_Rect& r,
const SDL_Rect& i)
411 return { r.x + i.x, r.y + i.y, r.w - i.x - i.w, r.h - i.y - i.h };
415bool SdlRailWindow::isFullDisplaySize()
const
418 return SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &disp) && (_windowRect.w >= disp.w) &&
419 (_windowRect.h >= disp.h);
424SDL_Rect SdlRailWindow::targetOuterRect()
const
426 return addInsets(_windowRect, bandInsets());
430SDL_Rect SdlRailWindow::insets()
const
432 std::unique_lock lock(_gfxLock);
433 return _appliedInsets;
437SDL_Rect SdlRailWindow::outerRect()
const
439 std::unique_lock lock(_gfxLock);
440 return addInsets(_windowRect, _appliedInsets);
443SDL_Rect SdlRailWindow::serverRect(
const SDL_Rect& outer)
const
445 return stripInsets(outer, insets());
449SDL_Point SdlRailWindow::blitOffset()
const
451 if (!effectivelyMaximized())
452 return { _appliedInsets.x, _appliedInsets.y };
454 const bool frameInSurface =
455 ((_frameMargins.x > 0) || (_frameMargins.y > 0)) &&
456 (
static_cast<int>(_gfxW) == _windowRect.w + _frameMargins.x + _frameMargins.w) &&
457 (
static_cast<int>(_gfxH) == _windowRect.h + _frameMargins.y + _frameMargins.h);
458 SDL_Point content = { 0, 0 };
460 content = { _frameMargins.x, _frameMargins.y };
461 else if (_visOffsetSet)
462 content = { _visOffset.x - _windowRect.x, _visOffset.y - _windowRect.y };
463 return { -std::clamp(content.x, 0,
static_cast<int>(_gfxW)),
464 -std::clamp(content.y, 0,
static_cast<int>(_gfxH)) };
467SDL_Point SdlRailWindow::serverOrigin()
const
469 std::unique_lock lock(_gfxLock);
471 const SDL_Point dst = blitOffset();
472 return { _windowRect.x - dst.x, _windowRect.y - dst.y };
475void SdlRailWindow::setStyle(uint32_t style, uint32_t exStyle)
477 std::unique_lock lock(_gfxLock);
478 const bool wasResizable = styleResizable();
479 const bool wasTopmost = _topmost;
482 _topmost = (exStyle & WS_EX_TOPMOST) != 0;
483 if (_topmost != wasTopmost)
484 _topmostDirty =
true;
485 if ((style & (WS_THICKFRAME | WS_MAXIMIZEBOX)) != 0)
486 _everResizable =
true;
488 if (styleResizable() != wasResizable)
491 if (!_popupClassified)
494 const bool captioned = (style & WS_CAPTION) == WS_CAPTION;
495 const bool isDialogOrApp =
497 ((style & (WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) != 0) ||
498 ((exStyle & WS_EX_APPWINDOW) != 0);
499 const bool isToolOrPopup =
500 ((exStyle & (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW)) != 0) || ((style & WS_POPUP) != 0);
502 const bool noActivate =
503 ((exStyle & WS_EX_NOACTIVATE) != 0) && ((exStyle & WS_EX_APPWINDOW) == 0);
505 constexpr uint32_t overlayEx = WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
506 const bool overlay = ((exStyle & overlayEx) == overlayEx) &&
507 ((exStyle & (WS_EX_NOACTIVATE | WS_EX_APPWINDOW)) == 0) && !captioned;
509 _isPopup = noActivate || overlay || (isToolOrPopup && !isDialogOrApp);
510 _layered = ((exStyle & WS_EX_LAYERED) != 0) && !isDialogOrApp;
511 _clickThrough = ((exStyle & WS_EX_TRANSPARENT) != 0);
512 _popupClassified =
true;
516void SdlRailWindow::setTitle(
const std::string& title)
518 std::unique_lock lock(_gfxLock);
523void SdlRailWindow::setVisible(
bool visible)
525 std::unique_lock lock(_gfxLock);
529void SdlRailWindow::setIcon(
const SdlRailIcon& icon)
531 std::unique_lock lock(_gfxLock);
536bool SdlRailWindow::create(SDL_Window* parent,
const SDL_Rect& parentRect)
544 const SDL_Rect vis = targetOuterRect();
545 _appliedInsets = bandInsets();
547 const bool fullscreen = _isPopup && !caps.positionsReadable && isFullDisplaySize();
548 if (_isPopup && parent && !fullscreen)
551 const SDL_Rect rel = { vis.x - parentRect.x, vis.y - parentRect.y, vis.w, vis.h };
552 _win = std::make_unique<SdlWindow>(
553 SdlWindow::createPopup(parent, rel, caps.supportsTransparentWindows, _overlay));
555 else if (_isPopup && !caps.positionsReadable && !fullscreen)
558 WLog_VRB(TAG,
"popup create deferred id=0x%08" PRIx32
": no parent yet",
559 static_cast<uint32_t
>(_id));
565 Uint32 flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_HIDDEN;
566 if (caps.supportsTransparentWindows)
567 flags |= SDL_WINDOW_TRANSPARENT;
568 _win = std::make_unique<SdlWindow>(
569 SdlWindow::create(SDL_GetPrimaryDisplay(), _title, flags, vis));
571 if (!_win || !_win->window() || !_win->renderer())
574 WLog_WARN(TAG,
"create failed id=0x%08" PRIx32
" %s",
static_cast<uint32_t
>(_id), role());
577 _win->resizeable(styleResizable());
582 SDL_SetWindowFullscreen(_win->window(),
true);
583 WLog_DBG(TAG,
"fullscreen id=0x%08" PRIx32
" %dx%d (WS_POPUP spans display)",
584 static_cast<uint32_t
>(_id), _windowRect.w, _windowRect.h);
588 if (!_isPopup && !caps.positionsReadable)
589 sdl_wayland_move_prepare(_win->window());
592 "create id=0x%08" PRIx32
" sdl=%" PRIu32
" %s vis=%dx%d+%d+%d margins=L%d,T%d,R%d,B%d "
594 static_cast<uint32_t
>(_id),
static_cast<uint32_t
>(_win->id()), role(), vis.w, vis.h,
595 vis.x, vis.y, _resizeMargins.x, _resizeMargins.y, _resizeMargins.w, _resizeMargins.h,
596 caps.supportsTransparentWindows ? 1 : 0);
600bool SdlRailWindow::reconcile(SDL_Window* parent,
const SDL_Rect& parentRect)
602 std::unique_lock lock(_gfxLock);
606 const bool popupReady = !_isPopup || _hasGfx;
608 const bool unplaceable =
609 _frame || (_overlay && !railPlatformCaps().positionsReadable && isFullDisplaySize());
611 const bool drawable =
612 _visible && (_windowRect.w > 0) && (_windowRect.h > 0) && popupReady && !unplaceable &&
613 (!_layered || (_hasGfx && !_visRects.empty() && (_shadowAnchored || _overlay)));
618 if (_layered && _hasGfx && !_visRects.empty() && !_shadowAnchored && !_overlay &&
619 !_shadowSuppressLogged)
621 _shadowSuppressLogged =
true;
623 "shadow-rule suppressed id=0x%08" PRIx32
" %dx%d style=0x%08" PRIx32
625 static_cast<uint32_t
>(_id), _windowRect.w, _windowRect.h, _style, _exStyle);
629 if ((SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_HIDDEN) == 0)
631 WLog_DBG(TAG,
"hide id=0x%08" PRIx32
" %s",
static_cast<uint32_t
>(_id), role());
632 SDL_HideWindow(_win->window());
636 if (_isPopup && !_visible)
638 _gfxPresented =
false;
646 if (!create(parent, parentRect))
648 _geometryDirty =
false;
655 _topmostDirty =
false;
657 if (!_isPopup && !_layered)
658 std::ignore = SDL_SetWindowAlwaysOnTop(_win->window(), _topmost);
664 _win->resizeable(styleResizable());
671 if (applyServerState(_maxState,
"maximize", SDL_MaximizeWindow))
672 _awaitRestoreUntil = SDL_GetTicks() + kAwaitRestoreRectMs;
673 std::ignore = applyServerState(_minState,
"minimize", SDL_MinimizeWindow);
674 const bool restorePending = restoreRectPending();
677 const bool maxed = effectivelyMaximized();
678 if ((maxed != _wasMaximized) && !_localMoveActive && !restorePending)
680 _appliedInsets = bandInsets();
682 _geometryDirty =
true;
683 _wasMaximized = maxed;
687 const SDL_Rect ext = bandInsets();
688 if (!SDL_RectsEqual(&ext, &_extentsApplied) &&
689 sdl_x11_set_frame_extents(_win->window(), ext.x, ext.w, ext.y, ext.h))
690 _extentsApplied = ext;
694 const bool wmOwnsGeometry =
695 _localMoveActive || geometryFrozen() || _fullscreen || restorePending;
696 if ((_minMaxDirty || _geometryDirty) && !wmOwnsGeometry)
698 const SDL_Rect vis = targetOuterRect();
699 SDL_SetWindowMinimumSize(_win->window(), std::clamp(_minSize.x, 0, vis.w),
700 std::clamp(_minSize.y, 0, vis.h));
701 int maxW = (_maxSize.x > 0) ? std::max(1, _maxSize.x) : 0;
702 int maxH = (_maxSize.y > 0) ? std::max(1, _maxSize.y) : 0;
705 if (!railPlatformCaps().positionsReadable &&
706 SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &usable))
709 const SDL_Rect bi = bandInsets();
710 const int capW = usable.w + bi.x + bi.w;
711 const int capH = usable.h + bi.y + bi.h;
712 if ((maxW == 0) || (maxW > capW))
714 if ((maxH == 0) || (maxH > capH))
717 SDL_SetWindowMaximumSize(_win->window(), maxW, maxH);
718 _minMaxDirty =
false;
722 if (_geometryDirty && !wmOwnsGeometry)
724 const SDL_Rect vis = targetOuterRect();
727 _appliedInsets = bandInsets();
730 SDL_GetWindowSize(_win->window(), &cw, &ch);
731 bool applied =
false;
733 if (_isPopup && parent)
735 if (!SDL_SetWindowPosition(_win->window(), vis.x - parentRect.x, vis.y - parentRect.y))
736 WLog_VRB(TAG,
"popup reposition unsupported id=0x%08" PRIx32
": %s",
737 static_cast<uint32_t
>(_id), SDL_GetError());
738 else if (!railPlatformCaps().positionsReadable)
739 _needsFullBlit =
true;
742 else if (railPlatformCaps().positionsReadable &&
743 !(_isPopup && SDL_GetWindowParent(_win->window())))
747 SDL_GetWindowPosition(_win->window(), &cx, &cy);
748 if ((cx != vis.x) || (cy != vis.y))
750 SDL_SetWindowPosition(_win->window(), vis.x, vis.y);
754 if ((cw != vis.w) || (ch != vis.h))
756 std::ignore = _win->resize({ vis.w, vis.h });
760 if (railPlatformCaps().positionsReadable && !_isPopup && !_localMoveActive &&
763 const bool settled = SDL_SyncWindow(_win->window());
766 SDL_GetWindowSize(_win->window(), &aw, &ah);
767 if (settled && ((aw != vis.w) || (ah != vis.h)))
770 _wmRefusedOuter = { vis.x, vis.y, aw, ah };
776 _geometryDirty =
false;
778 _geomApplyPending = applied && !_isPopup;
781 if (!_isPopup && parent && !_parentApplied)
783 if (SDL_SetWindowParent(_win->window(), parent))
784 _parentApplied =
true;
789 SDL_SetWindowTitle(_win->window(), _title.c_str());
794 if (!_isPopup && !_icon.bgra.empty())
796 SDL_Surface* s = SDL_CreateSurfaceFrom(
797 static_cast<int>(_icon.w),
static_cast<int>(_icon.h), SDL_PIXELFORMAT_BGRA32,
798 _icon.bgra.data(),
static_cast<int>(_icon.w * 4));
801 if (!SDL_SetWindowIcon(_win->window(), s))
802 WLog_WARN(TAG,
"SDL_SetWindowIcon failed for window 0x%08" PRIx32
": %s",
803 static_cast<uint32_t
>(_id), SDL_GetError());
804 SDL_DestroySurface(s);
811 if (!_minState.rail && _gfxPresented &&
812 (SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_HIDDEN))
815 SDL_Window* owner = _isPopup ? nullptr : SDL_GetWindowParent(_win->window());
816 if (owner && ((SDL_GetWindowFlags(owner) & SDL_WINDOW_HIDDEN) != 0))
817 std::ignore = SDL_SetWindowParent(_win->window(),
nullptr);
818 SDL_ShowWindow(_win->window());
823bool SdlRailWindow::takeSurfaceChange(uint32_t surfaceId)
825 std::unique_lock lock(_gfxLock);
826 if (_surfaceId == surfaceId)
828 _surfaceId = surfaceId;
832void SdlRailWindow::updateGfxSurface(
const void* data, uint32_t stride, uint32_t width,
833 uint32_t height,
const RECTANGLE_16* damage, uint32_t nbDamage,
834 WINPR_ATTR_UNUSED uint32_t format)
836 std::unique_lock lock(_gfxLock);
838 WINPR_ASSERT((format == PIXEL_FORMAT_BGRA32) || (format == PIXEL_FORMAT_BGRX32));
840 const size_t bytes =
static_cast<size_t>(stride) * height;
841 if (!data || (bytes == 0) || (width == 0) || (height == 0))
844 WLog_DBG(TAG,
"gfx cleared id=0x%08" PRIx32
" (surface unmapped)",
845 static_cast<uint32_t
>(_id));
852 _gfxVisOrigin = { 0, 0 };
856 const auto* src =
static_cast<const uint8_t*
>(data);
858 const bool full = !_hasGfx || (_gfxBuffer.size() != bytes) || (_gfxStride != stride) ||
859 (_gfxW != width) || (_gfxH != height) || (nbDamage == 0);
862 const SDL_Rect bounds{ 0, 0,
static_cast<int>(width),
static_cast<int>(height) };
864 if (!_hasGfx && regionIsBlank(src, stride, bounds))
866 _gfxBuffer.assign(src, src + bytes);
867 _gfxDamage.assign(1, SDL_Rect{ 0, 0,
static_cast<int>(width),
static_cast<int>(height) });
868 _needsFullBlit =
true;
870 _gfxHasAlpha = honorsAlpha() &&
871 regionHasAlpha(_gfxBuffer.data(), stride,
872 { 0, 0, static_cast<int>(width), static_cast<int>(height) });
876 const SDL_Rect bounds{ 0, 0,
static_cast<int>(width),
static_cast<int>(height) };
877 for (uint32_t i = 0; i < nbDamage; i++)
879 const SDL_Rect r{ damage[i].left, damage[i].top, damage[i].right - damage[i].left,
880 damage[i].bottom - damage[i].top };
882 if (!SDL_GetRectIntersection(&r, &bounds, &clip))
884 std::ignore = freerdp_image_copy_no_overlap(
885 _gfxBuffer.data(), PIXEL_FORMAT_BGRA32, stride,
static_cast<UINT32
>(clip.x),
886 static_cast<UINT32
>(clip.y),
static_cast<UINT32
>(clip.w),
887 static_cast<UINT32
>(clip.h), src, PIXEL_FORMAT_BGRA32, stride,
888 static_cast<UINT32
>(clip.x),
static_cast<UINT32
>(clip.y),
nullptr,
890 _gfxDamage.push_back(clip);
892 if (honorsAlpha() && !_gfxHasAlpha && regionHasAlpha(_gfxBuffer.data(), stride, clip))
896 if (_gfxDamage.size() > 32)
898 SDL_Rect{ 0, 0,
static_cast<int>(width),
static_cast<int>(height) });
900 if ((_gfxW != width) || (_gfxH != height))
902 _resizeAnchorRight =
false;
903 _resizeAnchorBottom =
false;
904 _needsFullBlit =
true;
906 _gfxVisOrigin = { 0, 0 };
912 WLog_VRB(TAG,
"gfx id=0x%08" PRIx32
" %ux%u full=%d nDamage=%u",
static_cast<uint32_t
>(_id),
913 width, height, full ? 1 : 0, nbDamage);
916void SdlRailWindow::invalidateAll()
918 std::unique_lock lock(_gfxLock);
919 _needsFullBlit =
true;
921 _gfxDamage.assign(1, SDL_Rect{ 0, 0,
static_cast<int>(_gfxW),
static_cast<int>(_gfxH) });
924void SdlRailWindow::setServerState(StateSync& s,
bool m)
926 std::unique_lock lock(_gfxLock);
934void SdlRailWindow::setServerMaximized(
bool m)
936 setServerState(_maxState, m);
939void SdlRailWindow::setServerMinimized(
bool m)
941 setServerState(_minState, m);
944bool SdlRailWindow::applyServerState(StateSync& s,
const char* what,
bool (*enter)(SDL_Window*))
948 bool restored =
false;
949 if (s.server && !s.rail)
953 WLog_DBG(TAG,
"%s id=0x%08" PRIx32
"", what,
static_cast<uint32_t
>(_id));
954 enter(_win->window());
956 else if (!s.server && s.rail)
960 WLog_DBG(TAG,
"restore id=0x%08" PRIx32
" (%s)",
static_cast<uint32_t
>(_id), what);
961 SDL_RestoreWindow(_win->window());
969 (void)SDL_SyncWindow(_win->window());
975bool SdlRailWindow::restoreRectPending()
const
977 return (_awaitRestoreUntil != 0) && (SDL_GetTicks() < _awaitRestoreUntil);
980bool SdlRailWindow::effectivelyMaximized()
const
982 return _maxState.rail ||
983 (_win && (SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_MAXIMIZED) != 0);
986bool SdlRailWindow::paint(SDL_Surface* primary, SDL_PixelFormat fallbackFormat,
987 const std::vector<SDL_Rect>& damage, SDL_Window* parent,
988 const SDL_Rect& parentRect)
990 if (!reconcile(parent, parentRect))
995 std::unique_lock lock(_gfxLock);
998 ok = paintGfx(fallbackFormat);
1003 ok = paintLegacy(primary, damage);
1008 if (_gfxPresented && !_mapped && !_minState.rail)
1011 WLog_DBG(TAG,
"map id=0x%08" PRIx32
" %s",
static_cast<uint32_t
>(_id), role());
1012 SDL_ShowWindow(_win->window());
1014 if (_isPopup && parent)
1016 std::unique_lock lock(_gfxLock);
1017 const SDL_Rect vis = targetOuterRect();
1018 SDL_SetWindowPosition(_win->window(), vis.x - parentRect.x, vis.y - parentRect.y);
1019 if (!railPlatformCaps().positionsReadable)
1020 _needsFullBlit =
true;
1022 if ((!_isPopup && !_layered) || _fullscreen)
1029bool SdlRailWindow::paintGfx(SDL_PixelFormat format)
1032 const SDL_Rect bi = _appliedInsets;
1035 SDL_GetWindowSizeInPixels(_win->window(), &ww, &wh);
1036 const int cw = ww - bi.x - bi.w;
1037 const int ch = wh - bi.y - bi.h;
1039 static_cast<int>(_gfxW);
1040 const int gh =
static_cast<int>(_gfxH);
1042 if ((_visRects.size() == 1) && (_visRects.at(0).w == gw) && (_visRects.at(0).h == gh))
1043 _gfxVisOrigin = { _visRects.at(0).x, _visRects.at(0).y };
1045 if (!_localMoveActive && (cw == gw) && (ch == gh))
1047 _resizeAnchorRight =
false;
1048 _resizeAnchorBottom =
false;
1049 _awaitingFrameUntil = 0;
1052 if ((_awaitingFrameUntil != 0) && (SDL_GetTicks() > _awaitingFrameUntil))
1054 WLog_DBG(TAG,
"resize frame timeout id=0x%08" PRIx32
" win=%dx%d gfx=%dx%d",
1055 static_cast<uint32_t
>(_id), cw, ch, gw, gh);
1056 _awaitingFrameUntil = 0;
1058 if ((cw != gw) || (ch != gh))
1060 if (_resizeAnchorRight)
1061 _windowRect.x += _windowRect.w - gw;
1062 if (_resizeAnchorBottom)
1063 _windowRect.y += _windowRect.h - gh;
1066 _resizeAnchorRight =
false;
1067 _resizeAnchorBottom =
false;
1068 _geometryDirty =
true;
1069 _needsFullBlit =
true;
1074 const bool awaitingServerResize = (_awaitingFrameUntil != 0) && ((cw != gw) || (ch != gh));
1075 const bool localResize = awaitingServerResize ||
1076 (_localMoveActive && (_localMoveIsResize || (cw != gw) || (ch != gh)));
1079 const bool winResized = (ww != _lastWinW) || (wh != _lastWinH);
1080 const bool gfxResized = (gw != _lastGfxW) || (gh != _lastGfxH);
1081 const bool serverResize =
1082 !_localMoveActive && !awaitingServerResize && (winResized || gfxResized || _needsFullBlit);
1085 if (!localResize && !serverResize && _gfxDamage.empty() && !(_layered && _visDirty))
1087 WLog_VRB(TAG,
"paintGfx skip id=0x%08" PRIx32
" no-damage no-resize",
1088 static_cast<uint32_t
>(_id));
1093 if (_isPopup && !_gfxPresented && (_gfxW > 0) && (_gfxH > 0))
1095 const uint32_t first = *
reinterpret_cast<const uint32_t*
>(_gfxBuffer.data());
1097 bool uniform = (first & 0x00FFFFFFu) == 0;
1098 for (uint32_t y = 0; uniform && (y < _gfxH); y++)
1100 const auto* row =
reinterpret_cast<const uint32_t*
>(
1101 _gfxBuffer.data() +
static_cast<size_t>(y) * _gfxStride);
1102 for (uint32_t x = 0; x < _gfxW; x++)
1103 if (row[x] != first)
1113 _needsFullBlit =
false;
1123 SDL_PixelFormat contentFormat = format;
1124 if (format == SDL_PIXELFORMAT_BGRA32)
1126 const bool blend = railPlatformCaps().supportsTransparentWindows;
1127 if (!blend || (_isPopup && !_layered && !_gfxHasAlpha))
1128 contentFormat = SDL_PIXELFORMAT_BGRX32;
1131 SDL_Surface* s = SDL_CreateSurfaceFrom(gw, gh, contentFormat, _gfxBuffer.data(),
1132 static_cast<int>(_gfxStride));
1135 WLog_WARN(TAG,
"paintGfx id=0x%08" PRIx32
" SDL_CreateSurfaceFrom failed: %s",
1136 static_cast<uint32_t
>(_id), SDL_GetError());
1144 const SDL_Point crop = { std::clamp(_gfxVisOrigin.x, 0, gw),
1145 std::clamp(_gfxVisOrigin.y, 0, gh) };
1147 const SDL_Point off = { (_resizeAnchorRight ? (ww - bi.w - gw) : bi.x) - crop.x,
1148 (_resizeAnchorBottom ? (wh - bi.h - gh) : bi.y) - crop.y };
1150 const bool showDashes = _localMoveActive && _localMoveIsResize;
1152 const bool fillRevealed = showDashes || awaitingServerResize;
1154 _win->paintResizeFrame(s, off, !_gfxDamage.empty(), bi, fillRevealed, showDashes);
1159 const SDL_Rect full = { 0, 0, gw, gh };
1160 const SDL_Point dst = blitOffset();
1161 const bool maxed = effectivelyMaximized();
1162 if (_layered && !_visRects.empty() && !maxed)
1165 const bool logClip = _visDirty;
1167 if (_visDirty || serverResize)
1170 std::ignore = _win->fill(
static_cast<Uint8
>(0), 0, 0, 0);
1171 _gfxDamage.assign(1, full);
1173 _needsFullBlit =
false;
1175 const SDL_Point off = { _visOffsetSet ? (_visOffset.x - _windowRect.x) : 0,
1176 _visOffsetSet ? (_visOffset.y - _windowRect.y) : 0 };
1179 "clip id=0x%08" PRIx32
" off=%d,%d nVis=%zu vis0=%dx%d+%d+%d win=%dx%d "
1181 static_cast<uint32_t
>(_id), off.x, off.y, _visRects.size(),
1182 _visRects.at(0).w, _visRects.at(0).h, _visRects.at(0).x, _visRects.at(0).y,
1183 ww, wh, _gfxW, _gfxH);
1184 const std::vector<SDL_Rect>& damageRects = _gfxDamage;
1185 std::vector<SDL_Rect> draw;
1186 draw.reserve(damageRects.size() * _visRects.size());
1187 for (
const auto& d : damageRects)
1189 for (
auto v : _visRects)
1194 if (SDL_GetRectIntersection(&d, &v, &part))
1195 draw.push_back(part);
1199 std::ignore = _win->drawRects(s, dst, draw);
1201 else if (_gfxDamage.empty() || serverResize)
1206 const bool transparentWin =
1207 (SDL_GetWindowFlags(_win->window()) & SDL_WINDOW_TRANSPARENT) != 0;
1209 std::ignore = _win->fill(
static_cast<Uint8
>(0), 0, 0,
1210 static_cast<Uint8
>(transparentWin ? 0x00 : 0xFF));
1211 _needsFullBlit =
false;
1213 std::ignore = _win->drawRects(s, dst, { full });
1217 std::ignore = _win->drawRects(s, dst, _gfxDamage);
1220 _win->updateSurface();
1221 _gfxPresented =
true;
1223 SDL_DestroySurface(s);
1224 WLog_VRB(TAG,
"paintGfx id=0x%08" PRIx32
" mode=%s win=%dx%d dmg=%zu",
1225 static_cast<uint32_t
>(_id),
1226 localResize ?
"resize" : (serverResize ?
"server-resize" :
"gfx"), ww, wh,
1240bool SdlRailWindow::paintLegacy(SDL_Surface* primary,
const std::vector<SDL_Rect>& damage)
1244 std::vector<SDL_Rect> vis;
1247 std::unique_lock lock(_gfxLock);
1249 if (!primary || _layered || (_windowRect.w <= 0) || (_windowRect.h <= 0))
1254 if (!full && damage.empty())
1256 WLog_VRB(TAG,
"paintLegacy skip id=0x%08" PRIx32
" no-damage",
1257 static_cast<uint32_t
>(_id));
1267 vis.push_back({ 0, 0, rect.w, rect.h });
1270 const SDL_Rect bounds = { 0, 0, primary->w, primary->h };
1271 bool blitted =
false;
1272 for (
const auto& v : vis)
1274 SDL_Rect src = { rect.x + v.x, rect.y + v.y, v.w, v.h };
1276 if (!SDL_GetRectIntersection(&src, &bounds, &clipped))
1281 SDL_Rect dst = { clipped.x - rect.x + bi.x, clipped.y - rect.y + bi.y, clipped.w,
1283 if (_win->blit(primary, clipped, dst))
1287 for (
const auto& d : damage)
1290 if (!SDL_GetRectIntersection(&clipped, &d, &part))
1292 SDL_Rect dst = { part.x - rect.x + bi.x, part.y - rect.y + bi.y, part.w, part.h };
1293 if (_win->blit(primary, part, dst))
1300 std::unique_lock lock(_gfxLock);
1303 _win->updateSurface();
1304 WLog_VRB(TAG,
"paintLegacy id=0x%08" PRIx32
" full=%d visRects=%zu",
1305 static_cast<uint32_t
>(_id), full ? 1 : 0, vis.size());