xdg-shell: queue state updates for toplevel (#14227)

ref https://github.com/hyprwm/Hyprland/discussions/14224
This commit is contained in:
Vaxry 2026-04-30 20:41:55 +01:00 committed by GitHub
parent cfb2deb664
commit 450d8a0bf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 93 additions and 38 deletions

View file

@ -16,6 +16,17 @@ using namespace Hyprutils::OS;
#define TIMESPEC_NSEC_PER_SEC 1000000000L
static uint64_t LAST_DO_LATER_SEQ = 1;
SEventLoopDoLaterLock::SEventLoopDoLaterLock(uint64_t seq_) : seq(seq_) {
;
}
SEventLoopDoLaterLock::~SEventLoopDoLaterLock() {
if (g_pEventLoopManager && seq > 0)
g_pEventLoopManager->removeDoLater(seq);
}
CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop) {
m_timers.timerfd = CFileDescriptor{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)};
m_wayland.loop = wlEventLoop;
@ -201,11 +212,13 @@ void CEventLoopManager::nudgeTimers() {
timerfd_settime(m_timers.timerfd.get(), TFD_TIMER_ABSTIME, &ts, nullptr);
}
void CEventLoopManager::doLater(const std::function<void()>& fn) {
m_idle.fns.emplace_back(fn);
uint64_t CEventLoopManager::doLater(const std::function<void()>& fn) {
const uint64_t NEW_SEQ = ++LAST_DO_LATER_SEQ;
m_idle.fns.emplace_back(std::make_pair<>(NEW_SEQ, fn));
if (m_idle.eventSource)
return;
return NEW_SEQ;
m_idle.eventSource = wl_event_loop_add_idle(
m_wayland.loop,
@ -215,11 +228,26 @@ void CEventLoopManager::doLater(const std::function<void()>& fn) {
IDLE->fns.clear();
IDLE->eventSource = nullptr;
for (auto& f : fns) {
if (f)
f();
if (f.second)
f.second();
}
},
&m_idle);
return NEW_SEQ;
}
void CEventLoopManager::removeDoLater(uint64_t seq) {
std::erase_if(m_idle.fns, [&seq](const auto& e) { return e.first == seq; });
if (m_idle.fns.empty() && m_idle.eventSource) {
wl_event_source_remove(m_idle.eventSource);
m_idle.eventSource = nullptr;
}
}
UP<SEventLoopDoLaterLock> CEventLoopManager::doLaterLock(const std::function<void()>& fn) {
return makeUnique<SEventLoopDoLaterLock>(doLater(fn));
}
void CEventLoopManager::doOnReadable(CFileDescriptor fd, std::function<void()>&& fn) {

View file

@ -14,6 +14,13 @@ namespace Aquamarine {
struct SPollFD;
};
struct SEventLoopDoLaterLock {
SEventLoopDoLaterLock(uint64_t seq);
~SEventLoopDoLaterLock();
uint64_t seq = 0;
};
class CEventLoopManager {
public:
CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop);
@ -30,12 +37,16 @@ class CEventLoopManager {
// schedules a recalc of the timers
void scheduleRecalc();
// schedules a function to run later, aka in a wayland idle event.
void doLater(const std::function<void()>& fn);
// schedules a function to run later, aka in a wayland idle event. Returns a sequence which can be used to remove it.
uint64_t doLater(const std::function<void()>& fn);
void removeDoLater(uint64_t seq);
// automatically cleaned up doLater instance
[[nodiscard]] UP<SEventLoopDoLaterLock> doLaterLock(const std::function<void()>& fn);
struct SIdleData {
wl_event_source* eventSource = nullptr;
std::vector<std::function<void()>> fns;
wl_event_source* eventSource = nullptr;
std::vector<std::pair<uint64_t, std::function<void()>>> fns;
};
struct SReadableWaiter {

View file

@ -4,6 +4,7 @@
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../managers/ANRManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../helpers/Monitor.hpp"
#include "core/Seat.hpp"
#include "core/Compositor.hpp"
@ -286,7 +287,7 @@ bool CXDGToplevelResource::anyChildModal() {
uint32_t CXDGToplevelResource::setSize(const Vector2D& size) {
m_pendingApply.size = size;
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -300,7 +301,9 @@ uint32_t CXDGToplevelResource::setMaximized(bool maximized) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED);
else if (!maximized && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -314,7 +317,9 @@ uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN);
else if (!fullscreen && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -328,7 +333,9 @@ uint32_t CXDGToplevelResource::setActive(bool active) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED);
else if (!active && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -345,22 +352,32 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED);
else if (!sus && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
void CXDGToplevelResource::scheduleStateApplication() {
if (!m_pendingApply.states.empty()) {
wl_array_add(&arr, m_pendingApply.states.size() * sizeof(int));
memcpy(arr.data, m_pendingApply.states.data(), m_pendingApply.states.size() * sizeof(int));
}
if (m_stateUpdate)
return;
m_resource->sendConfigure(m_pendingApply.size.x, m_pendingApply.size.y, &arr);
m_stateUpdate = g_pEventLoopManager->doLaterLock([this] {
wl_array arr;
wl_array_init(&arr);
wl_array_release(&arr);
if (!m_pendingApply.states.empty()) {
wl_array_add(&arr, m_pendingApply.states.size() * sizeof(int));
memcpy(arr.data, m_pendingApply.states.data(), m_pendingApply.states.size() * sizeof(int));
}
m_resource->sendConfigure(m_pendingApply.size.x, m_pendingApply.size.y, &arr);
wl_array_release(&arr);
m_stateUpdate.reset();
});
}
void CXDGToplevelResource::close() {
@ -516,8 +533,6 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
CXDGSurfaceResource::~CXDGSurfaceResource() {
m_events.destroy.emit();
if (m_configureSource)
wl_event_source_remove(m_configureSource);
if (m_surface)
m_surface->resetRole();
}
@ -531,22 +546,19 @@ SP<CXDGSurfaceResource> CXDGSurfaceResource::fromResource(wl_resource* res) {
return data ? data->m_self.lock() : nullptr;
}
static void onConfigure(void* data) {
sc<CXDGSurfaceResource*>(data)->configure();
}
uint32_t CXDGSurfaceResource::scheduleConfigure() {
if (m_configureSource)
if (m_stateUpdate)
return m_scheduledSerial;
m_configureSource = wl_event_loop_add_idle(g_pCompositor->m_wlEventLoop, onConfigure, this);
m_stateUpdate = g_pEventLoopManager->doLaterLock([this] { configure(); });
m_scheduledSerial = wl_display_next_serial(g_pCompositor->m_wlDisplay);
return m_scheduledSerial;
}
void CXDGSurfaceResource::configure() {
m_configureSource = nullptr;
m_stateUpdate.reset();
m_resource->sendConfigure(m_scheduledSerial);
}

View file

@ -18,6 +18,7 @@ class CXDGPopupResource;
class CSeatGrab;
class CWLSurfaceResource;
class CXDGDialogV1Resource;
struct SEventLoopDoLaterLock;
struct SXDGPositionerState {
Vector2D requestedSize;
@ -149,8 +150,11 @@ class CXDGToplevelResource {
std::vector<WP<CXDGToplevelResource>> m_children;
private:
SP<CXdgToplevel> m_resource;
void applyState();
SP<CXdgToplevel> m_resource;
UP<SEventLoopDoLaterLock> m_stateUpdate;
void scheduleStateApplication();
};
class CXDGSurfaceRole : public ISurfaceRole {
@ -202,12 +206,12 @@ class CXDGSurfaceResource {
void configure();
private:
SP<CXdgSurface> m_resource;
SP<CXdgSurface> m_resource;
uint32_t m_lastConfigureSerial = 0;
uint32_t m_scheduledSerial = 0;
UP<SEventLoopDoLaterLock> m_stateUpdate;
wl_event_source* m_configureSource = nullptr;
uint32_t m_lastConfigureSerial = 0;
uint32_t m_scheduledSerial = 0;
//
std::vector<WP<CXDGPopupResource>> m_popups;