From 7bb8bdf4e8fedaf4dfae58512bc4c728671727df Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Tue, 22 Sep 2026 14:33:50 +0200 Subject: [PATCH] wayland: fix configure not applying sometimes (#415) --- include/aquamarine/backend/Wayland.hpp | 3 ++ src/backend/Wayland.cpp | 50 +++++++++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/include/aquamarine/backend/Wayland.hpp b/include/aquamarine/backend/Wayland.hpp index 8793d4e0..7e6636f8 100644 --- a/include/aquamarine/backend/Wayland.hpp +++ b/include/aquamarine/backend/Wayland.hpp @@ -64,6 +64,7 @@ namespace Aquamarine { Hyprutils::Memory::CSharedPointer wlBufferFromBuffer(Hyprutils::Memory::CSharedPointer buffer); void onFrameDone(); + void applyConfigure(); void onEnter(Hyprutils::Memory::CSharedPointer pointer, uint32_t serial); // frame loop — unified scheduler shared with DRM. See CFrameScheduler. @@ -90,6 +91,8 @@ namespace Aquamarine { Hyprutils::Memory::CSharedPointer xdgToplevel; Hyprutils::Memory::CSharedPointer frameCallback; Hyprutils::Math::Vector2D surfaceSize; + Hyprutils::Math::Vector2D pendingSize; + bool announced = false; } waylandState; friend class CWaylandBackend; diff --git a/src/backend/Wayland.cpp b/src/backend/Wayland.cpp index 8905adc2..6af7b93e 100644 --- a/src/backend/Wayland.cpp +++ b/src/backend/Wayland.cpp @@ -182,7 +182,8 @@ bool Aquamarine::CWaylandBackend::createOutput(const std::string& szName) { o->self = o; if (backend->ready) o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock()); - idleCallbacks.emplace_back([this, o]() { backend->events.newOutput.emit(SP(o)); }); + // Start the configure handshake even when there is no other host traffic. + wl_display_flush(waylandState.display); return true; } @@ -229,12 +230,17 @@ bool Aquamarine::CWaylandBackend::setCursor(Hyprutils::Memory::CSharedPointerswapchain = CSwapchain::create(backend->primaryAllocator, self.lock()); if (!o->swapchain) { backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", o->name)); continue; } + o->applyConfigure(); } } @@ -526,6 +532,12 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils:: waylandState.xdgSurface->setConfigure([this](CCXdgSurface* r, uint32_t serial) { backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: configure surface with {}", name, serial)); r->sendAckConfigure(serial); + + // Retain the acknowledged size for consumers that attach their listeners later. + if (modes.empty()) + modes.emplace_back(makeShared(SOutputMode{.refreshRate = 60000, .preferred = true})); + modes.front()->pixelSize = waylandState.pendingSize; + applyConfigure(); }); waylandState.xdgToplevel = makeShared(waylandState.xdgSurface->sendGetToplevel()); @@ -545,12 +557,8 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils:: w = 1280; h = 720; } - events.state.emit(SStateEvent{.size = {w, h}}); - // Kick off the first frame synchronously: the consumer expects events.frame in - // the same dispatch cycle as the toplevel configure. Deferring via scheduleFrame's - // idle races the first commit and can leave the output blank until the next event. - needsFrame = true; - sched.frameReady.emit(); + // The following xdg_surface.configure completes and acknowledges this size. + waylandState.pendingSize = {w, h}; }); waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { destroy(); }); @@ -570,6 +578,32 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils:: backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: initialized", name)); } +void Aquamarine::CWaylandOutput::applyConfigure() { + if (!backend->backend->ready || !swapchain || modes.empty()) + return; + + // Signal handlers may destroy the output. Keep it alive and stop notifying it + // if it is removed from the backend during an announcement or state change. + const auto OUTPUT = self.lock(); + const auto ACTIVE = [&]() { return std::ranges::find(backend->outputs, OUTPUT) != backend->outputs.end(); }; + if (!ACTIVE()) + return; + + if (!waylandState.announced) { + waylandState.announced = true; + backend->backend->events.newOutput.emit(SP(OUTPUT)); + if (!ACTIVE()) + return; + } + + events.state.emit(SStateEvent{.size = modes.front()->pixelSize}); + if (!ACTIVE()) + return; + + needsFrame = true; + sched.frameReady.emit(); +} + Aquamarine::CWaylandOutput::~CWaylandOutput() { events.destroy.emit(); // frameIdle captures a raw this and may still be queued; pull it before we die.