510{
511 if (!_enabled)
512 return true;
513
514 std::unique_lock lock(_windowsLock);
515
516
517 if (_sentWorkArea.w == 0)
518 {
519 SDL_Rect usable{};
520 if (SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &usable))
521 sendWorkArea(usable);
522 }
523
524
525
526
527
528
529
530
531
532
533
534 std::vector<std::pair<size_t, SdlRailWindow*>> release;
535 for (auto& [wid, win] : _windows)
536 {
537 if (!win.window())
538 continue;
539 size_t depth = 0;
540 bool orphan = false;
541 bool ownerDeleted = false;
542 for (SDL_Window* p = SDL_GetWindowParent(win.window()); p; p = SDL_GetWindowParent(p))
543 {
544 depth++;
545 auto* owner = getWindowBySdlId(SDL_GetWindowID(p));
546 if (!owner)
547 orphan = true;
548 else if (owner->isDeleted())
549 {
550 orphan = true;
551 if (owner->id() == win.owner())
552 ownerDeleted = true;
553 }
554 }
555 if (orphan && !win.isDeleted() && (ownerDeleted || !win.isPopup()))
556 win.markDeleted();
557 if (orphan || win.isDeleted())
558 release.emplace_back(depth, &win);
559 }
560 for (auto& [wid, win] : _deadWindows)
561 {
562 if (!win.window())
563 continue;
564 size_t depth = 0;
565 for (SDL_Window* p = SDL_GetWindowParent(win.window()); p; p = SDL_GetWindowParent(p))
566 depth++;
567 release.emplace_back(depth, &win);
568 }
569 std::sort(release.begin(), release.end(),
570 [](const auto& a, const auto& b) { return a.first > b.first; });
571 for (auto& [depth, win] : release)
572 win->releaseWindow();
573
574
575 for (auto it = _windows.begin(); it != _windows.end();)
576 it = it->second.isDeleted() ? _windows.erase(it) : std::next(it);
577
578 _deadWindows.clear();
579
580
581 for (auto& it : _windows)
582 {
583 auto& win = it.second;
584 if (win.isPopup())
585 continue;
586
587 SDL_Window* parent = nullptr;
588 SDL_Rect parentRect{};
589 auto* owner = resolveParent(win.owner());
590 if (owner && (owner != &win))
591 {
592 parent = owner->window();
593 parentRect = owner->outerRect();
594 }
595 win.paint(primary, fallbackFormat, damage, parent, parentRect);
596
597 SDL_Rect refusedOuter{};
598 if (win.takeWmOverride(refusedOuter))
599 {
600 const SDL_Rect rect = win.serverRect(refusedOuter);
601 const SDL_Rect cur = win.windowRect();
602 WLog_DBG(TAG,
603 "wm-refused adopt id=0x%08" PRIx32
604 " outer=%dx%d -> srv=%d,%d %dx%d (was %dx%d)",
605 static_cast<uint32_t>(win.id()), refusedOuter.w, refusedOuter.h, rect.x,
606 rect.y, rect.w, rect.h, cur.w, cur.h);
607 if (!SDL_RectsEqual(&rect, &cur))
608 sendClientWindowMove(&win, rect);
609 win.adoptLocalGeometry(rect);
610 }
611
612 SDL_Rect unconfirmed{};
613 if (win.takeUnconfirmedReport(unconfirmed) && !win.railMinimized() &&
614 !win.effectivelyMaximized())
615 {
616 WLog_DBG(TAG, "size resent id=0x%08" PRIx32 " %dx%d (not confirmed)",
617 static_cast<uint32_t>(win.id()), unconfirmed.w, unconfirmed.h);
618 sendClientWindowMove(&win, unconfirmed);
619 }
620 if (win.takeRestoreRetry())
621 {
622 WLog_DBG(TAG, "restore resent id=0x%08" PRIx32, static_cast<uint32_t>(win.id()));
623 sendSystemCommand(&win, SC_RESTORE);
624 }
625 }
626
627
628 if (!_refreshSent)
629 {
630 for (auto& it : _windows)
631 {
632 if (it.second.isPopup() || !it.second.window())
633 continue;
634 auto* ctx = &_context->common()->context;
635 const auto dw = static_cast<UINT16>(
637 const auto dh = static_cast<UINT16>(
640 if (ctx->update && ctx->update->RefreshRect)
641 (void)ctx->update->RefreshRect(ctx, 1, &all);
642 _refreshSent = true;
643 WLog_DBG(TAG, "refresh-rect sent (first app window realized)");
644 break;
645 }
646 }
647
648 for (auto& it : _windows)
649 {
650 auto& popup = it.second;
651 if (!popup.isPopup())
652 continue;
653
654
655 if (!popup.isFrame() && isShadowFrame(popup))
656 popup.setFrame(true);
657
658
659 if (popup.isLayered())
660 {
661 constexpr int reach = 48;
662 const SDL_Rect sr = popup.windowRect();
663 bool anchored = false;
664 for (auto& other : _windows)
665 {
666 auto& cand = other.second;
667 if ((&cand == &popup) || !cand.isPopup() || cand.isLayered() || !cand.window() ||
668 ((SDL_GetWindowFlags(cand.window()) & SDL_WINDOW_HIDDEN) != 0))
669 continue;
670 SDL_Rect zone = cand.windowRect();
671 zone.x -= reach;
672 zone.y -= reach;
673 zone.w += 2 * reach;
674 zone.h += 2 * reach;
675
676
677 const bool inside = (sr.x >= zone.x) && (sr.y >= zone.y) &&
678 (sr.x + sr.w <= zone.x + zone.w) &&
679 (sr.y + sr.h <= zone.y + zone.h);
680 if (inside)
681 {
682 anchored = true;
683 break;
684 }
685 }
686 popup.setShadowAnchored(anchored);
687 }
688
690
691 if (auto* pw = popup.window())
692 {
693 SDL_Window* p = SDL_GetWindowParent(pw);
694 chosen = p ? getWindowBySdlId(SDL_GetWindowID(p)) : nullptr;
695 }
696 else
697 {
698 chosen = resolvePopupParent(popup);
699 if (!chosen)
700 WLog_DBG(TAG, "popup id=0x%08" PRIx32 " has no parent app window",
701 static_cast<UINT32>(popup.id()));
702 }
703
704 SDL_Window* parent = nullptr;
705 SDL_Rect parentRect{};
706 if (chosen)
707 {
708 parent = chosen->window();
709 parentRect = chosen->outerRect();
710 }
711 popup.paint(primary, fallbackFormat, damage, parent, parentRect);
712 }
713
714
715 applyZOrder();
716 return true;
717}