398{
399 if (!_enabled)
400 return true;
401
402 std::unique_lock lock(_windowsLock);
403
404
405 if (_sentWorkArea.w == 0)
406 {
407 SDL_Rect usable{};
408 if (SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &usable))
409 sendWorkArea(usable);
410 }
411
412
413 for (auto& [wid, win] : _windows)
414 {
415 if (win.isDeleted() || !win.window())
416 continue;
417 for (SDL_Window* p = SDL_GetWindowParent(win.window()); p; p = SDL_GetWindowParent(p))
418 {
419 auto* owner = getWindowBySdlId(SDL_GetWindowID(p));
420 if (!owner || owner->isDeleted())
421 {
422 win.markDeleted();
423 break;
424 }
425 }
426 }
427
428
429 std::vector<std::pair<size_t, uint64_t>> dead;
430 for (auto& [wid, win] : _windows)
431 {
432 if (!win.isDeleted())
433 continue;
434 size_t depth = 0;
435 for (SDL_Window* p = win.window() ? SDL_GetWindowParent(win.window()) : nullptr; p;
436 p = SDL_GetWindowParent(p))
437 depth++;
438 dead.emplace_back(depth, wid);
439 }
440 std::sort(dead.begin(), dead.end(),
441 [](const auto& a, const auto& b) { return a.first > b.first; });
442 for (const auto& [depth, wid] : dead)
443 _windows.erase(wid);
444
445 _deadWindows.clear();
446
447
448 for (auto& it : _windows)
449 {
450 auto& win = it.second;
451 if (win.isPopup())
452 continue;
453
454 SDL_Window* parent = nullptr;
455 SDL_Rect parentRect{};
456 auto* owner = resolveParent(win.owner());
457 if (owner && (owner != &win))
458 {
459 parent = owner->window();
460 parentRect = owner->outerRect();
461 }
462 win.paint(primary, fallbackFormat, damage, parent, parentRect);
463
464 SDL_Rect refusedOuter{};
465 if (win.takeWmOverride(refusedOuter))
466 {
467 const SDL_Rect rect = win.serverRect(refusedOuter);
468 const SDL_Rect cur = win.windowRect();
469 WLog_DBG(TAG,
470 "wm-refused adopt id=0x%08" PRIx32
471 " outer=%dx%d -> srv=%d,%d %dx%d (was %dx%d)",
472 static_cast<uint32_t>(win.id()), refusedOuter.w, refusedOuter.h, rect.x,
473 rect.y, rect.w, rect.h, cur.w, cur.h);
474 if (!SDL_RectsEqual(&rect, &cur))
475 sendClientWindowMove(&win, rect);
476 win.adoptLocalGeometry(rect);
477 }
478 }
479
480
481 if (!_refreshSent)
482 {
483 for (auto& it : _windows)
484 {
485 if (it.second.isPopup() || !it.second.window())
486 continue;
487 auto* ctx = &_context->common()->context;
488 const auto dw = static_cast<UINT16>(
490 const auto dh = static_cast<UINT16>(
493 if (ctx->update && ctx->update->RefreshRect)
494 (void)ctx->update->RefreshRect(ctx, 1, &all);
495 _refreshSent = true;
496 WLog_DBG(TAG, "refresh-rect sent (first app window realized)");
497 break;
498 }
499 }
500
501 for (auto& it : _windows)
502 {
503 auto& popup = it.second;
504 if (!popup.isPopup())
505 continue;
506
507
508 if (!popup.isFrame() && isShadowFrame(popup))
509 popup.setFrame(true);
510
511
512 if (popup.isLayered())
513 {
514 constexpr int reach = 48;
515 const SDL_Rect sr = popup.windowRect();
516 bool anchored = false;
517 for (auto& other : _windows)
518 {
519 auto& cand = other.second;
520 if ((&cand == &popup) || !cand.isPopup() || cand.isLayered() || !cand.window() ||
521 ((SDL_GetWindowFlags(cand.window()) & SDL_WINDOW_HIDDEN) != 0))
522 continue;
523 SDL_Rect zone = cand.windowRect();
524 zone.x -= reach;
525 zone.y -= reach;
526 zone.w += 2 * reach;
527 zone.h += 2 * reach;
528
529
530 const bool inside = (sr.x >= zone.x) && (sr.y >= zone.y) &&
531 (sr.x + sr.w <= zone.x + zone.w) &&
532 (sr.y + sr.h <= zone.y + zone.h);
533 if (inside)
534 {
535 anchored = true;
536 break;
537 }
538 }
539 popup.setShadowAnchored(anchored);
540 }
541
543
544 if (auto* pw = popup.window())
545 {
546 SDL_Window* p = SDL_GetWindowParent(pw);
547 chosen = p ? getWindowBySdlId(SDL_GetWindowID(p)) : nullptr;
548 }
549 else
550 {
551 chosen = resolvePopupParent(popup);
552 if (!chosen)
553 WLog_DBG(TAG, "popup id=0x%08" PRIx32 " has no parent app window",
554 static_cast<UINT32>(popup.id()));
555 }
556
557 SDL_Window* parent = nullptr;
558 SDL_Rect parentRect{};
559 if (chosen)
560 {
561 parent = chosen->window();
562 parentRect = chosen->outerRect();
563 }
564 popup.paint(primary, fallbackFormat, damage, parent, parentRect);
565 }
566
567
568 applyZOrder();
569 return true;
570}