wayland/toplevel: reorganize toplevel management

This commit is contained in:
outfoxxed 2026-05-10 23:46:39 -07:00
parent d1760ed1f3
commit 7d1c9a9c67
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
20 changed files with 163 additions and 199 deletions

View file

@ -105,7 +105,7 @@ if (WAYLAND_SESSION_LOCK)
endif()
if (WAYLAND_TOPLEVEL_MANAGEMENT)
add_subdirectory(toplevel_management)
add_subdirectory(toplevel)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._ToplevelManagement)
endif()

View file

@ -24,7 +24,7 @@
#include "../../../core/logcat.hpp"
#include "../../../core/model.hpp"
#include "../../../core/qmlscreen.hpp"
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "hyprland_toplevel.hpp"
#include "monitor.hpp"
#include "toplevel_mapping.hpp"
@ -139,11 +139,10 @@ void HyprlandIpc::eventSocketReady() {
}
void HyprlandIpc::toplevelAddressed(
wayland::toplevel_management::impl::ToplevelHandle* handle,
wayland::toplevel::wlr::ToplevelHandle* handle,
quint64 address
) {
auto* waylandToplevel =
wayland::toplevel_management::ToplevelManager::instance()->forImpl(handle);
auto* waylandToplevel = wayland::toplevel::ToplevelManager::instance()->forImpl(handle);
if (!waylandToplevel) return;

View file

@ -15,7 +15,7 @@
#include "../../../core/model.hpp"
#include "../../../core/qmlscreen.hpp"
#include "../../../core/streamreader.hpp"
#include "../../../wayland/toplevel_management/handle.hpp"
#include "../../../wayland/toplevel/wlr_toplevel.hpp"
namespace qs::hyprland::ipc {
@ -128,10 +128,7 @@ private slots:
void eventSocketStateChanged(QLocalSocket::LocalSocketState state);
void eventSocketReady();
void toplevelAddressed(
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
quint64 address
);
void toplevelAddressed(qs::wayland::toplevel::wlr::ToplevelHandle* handle, quint64 address);
void onFocusedMonitorDestroyed();

View file

@ -6,12 +6,12 @@
#include <qtmetamacros.h>
#include <qtypes.h>
#include "../../toplevel_management/qml.hpp"
#include "../../toplevel/qml.hpp"
#include "connection.hpp"
#include "toplevel_mapping.hpp"
#include "workspace.hpp"
using namespace qs::wayland::toplevel_management;
using namespace qs::wayland::toplevel;
namespace qs::hyprland::ipc {
@ -112,7 +112,7 @@ Toplevel* HyprlandToplevel::waylandHandle() {
return ToplevelManager::instance()->forImpl(this->mWaylandHandle);
}
void HyprlandToplevel::setWaylandHandle(impl::ToplevelHandle* handle) {
void HyprlandToplevel::setWaylandHandle(wlr::ToplevelHandle* handle) {
if (this->mWaylandHandle == handle) return;
if (this->mWaylandHandle) {
QObject::disconnect(this->mWaylandHandle, nullptr, this, nullptr);

View file

@ -7,8 +7,8 @@
#include <qtmetamacros.h>
#include <qtypes.h>
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel_management/qml.hpp"
#include "../../toplevel/qml.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "connection.hpp"
namespace qs::hyprland::ipc {
@ -30,7 +30,7 @@ class HyprlandToplevel: public QObject {
/// Will be null until the address is reported
Q_PROPERTY(HyprlandToplevel* handle READ hyprlandHandle NOTIFY hyprlandHandleChanged);
/// The wayland toplevel handle. Will be null intil the address is reported
Q_PROPERTY(qs::wayland::toplevel_management::Toplevel* wayland READ waylandHandle NOTIFY waylandHandleChanged);
Q_PROPERTY(qs::wayland::toplevel::Toplevel* wayland READ waylandHandle NOTIFY waylandHandleChanged);
/// The title of the toplevel
Q_PROPERTY(QString title READ default NOTIFY titleChanged BINDABLE bindableTitle);
/// Whether the toplevel is active or not
@ -53,7 +53,7 @@ public:
/// When invoked from HyprlandIpc, reacting to Hyprland's IPC events.
explicit HyprlandToplevel(HyprlandIpc* ipc);
/// When attached from a Toplevel
explicit HyprlandToplevel(HyprlandIpc* ipc, qs::wayland::toplevel_management::Toplevel* toplevel);
explicit HyprlandToplevel(HyprlandIpc* ipc, qs::wayland::toplevel::Toplevel* toplevel);
static HyprlandToplevel* qmlAttachedProperties(QObject* object);
@ -69,8 +69,8 @@ public:
[[nodiscard]] HyprlandToplevel* hyprlandHandle() { return this->mHyprlandHandle; }
void setHyprlandHandle(HyprlandToplevel* handle);
[[nodiscard]] wayland::toplevel_management::Toplevel* waylandHandle();
void setWaylandHandle(wayland::toplevel_management::impl::ToplevelHandle* handle);
[[nodiscard]] wayland::toplevel::Toplevel* waylandHandle();
void setWaylandHandle(wayland::toplevel::wlr::ToplevelHandle* handle);
// clang-format on
[[nodiscard]] QBindable<QString> bindableTitle() { return &this->bTitle; }
@ -105,7 +105,7 @@ private:
quint64 mAddress = 0;
HyprlandIpc* ipc;
qs::wayland::toplevel_management::impl::ToplevelHandle* mWaylandHandle = nullptr;
qs::wayland::toplevel::wlr::ToplevelHandle* mWaylandHandle = nullptr;
HyprlandToplevel* mHyprlandHandle = nullptr;
// clang-format off

View file

@ -6,9 +6,9 @@
#include <qtypes.h>
#include <qwaylandclientextension.h>
#include "../../toplevel_management/manager.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
using namespace qs::wayland::toplevel_management::impl;
using namespace qs::wayland::toplevel::wlr;
namespace qs::hyprland::ipc {

View file

@ -8,7 +8,7 @@
#include <qwayland-hyprland-toplevel-mapping-v1.h>
#include <qwaylandclientextension.h>
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "wayland-hyprland-toplevel-mapping-v1-client-protocol.h"
namespace qs::hyprland::ipc {
@ -16,7 +16,7 @@ namespace qs::hyprland::ipc {
class HyprlandToplevelMappingHandle: QtWayland::hyprland_toplevel_window_mapping_handle_v1 {
public:
explicit HyprlandToplevelMappingHandle(
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
qs::wayland::toplevel::wlr::ToplevelHandle* handle,
::hyprland_toplevel_window_mapping_handle_v1* mapping
)
: QtWayland::hyprland_toplevel_window_mapping_handle_v1(mapping)
@ -34,7 +34,7 @@ protected:
void hyprland_toplevel_window_mapping_handle_v1_failed() override;
private:
qs::wayland::toplevel_management::impl::ToplevelHandle* handle;
qs::wayland::toplevel::wlr::ToplevelHandle* handle;
};
class HyprlandToplevelMappingManager
@ -48,22 +48,18 @@ public:
static HyprlandToplevelMappingManager* instance();
[[nodiscard]] quint64
getToplevelAddress(qs::wayland::toplevel_management::impl::ToplevelHandle* handle) const;
getToplevelAddress(qs::wayland::toplevel::wlr::ToplevelHandle* handle) const;
signals:
void toplevelAddressed(
qs::wayland::toplevel_management::impl::ToplevelHandle* handle,
quint64 address
);
void toplevelAddressed(qs::wayland::toplevel::wlr::ToplevelHandle* handle, quint64 address);
private slots:
void onToplevelReady(qs::wayland::toplevel_management::impl::ToplevelHandle* handle);
void onToplevelReady(qs::wayland::toplevel::wlr::ToplevelHandle* handle);
void onToplevelDestroyed(QObject* object);
private:
void
assignAddress(qs::wayland::toplevel_management::impl::ToplevelHandle* handle, quint64 address);
QHash<wayland::toplevel_management::impl::ToplevelHandle*, quint64> addresses;
void assignAddress(qs::wayland::toplevel::wlr::ToplevelHandle* handle, quint64 address);
QHash<wayland::toplevel::wlr::ToplevelHandle*, quint64> addresses;
friend class HyprlandToplevelMappingHandle;
};

View file

@ -3,7 +3,7 @@ description = "Wayland specific Quickshell types"
headers = [
"wlr_layershell/wlr_layershell.hpp",
"session_lock.hpp",
"toplevel_management/qml.hpp",
"toplevel/qml.hpp",
"screencopy/view.hpp",
"idle_inhibit/inhibitor.hpp",
"idle_notify/monitor.hpp",

View file

@ -9,7 +9,7 @@
#include <wayland-hyprland-toplevel-export-v1-client-protocol.h>
#include "../../../core/logcat.hpp"
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "../manager.hpp"
#include "hyprland_screencopy_p.hpp"
@ -29,7 +29,7 @@ HyprlandScreencopyManager* HyprlandScreencopyManager::instance() {
}
ScreencopyContext* HyprlandScreencopyManager::captureToplevel(
toplevel_management::impl::ToplevelHandle* handle,
toplevel::wlr::ToplevelHandle* handle,
bool paintCursors
) {
return new HyprlandScreencopyContext(this, handle, paintCursors);
@ -37,7 +37,7 @@ ScreencopyContext* HyprlandScreencopyManager::captureToplevel(
HyprlandScreencopyContext::HyprlandScreencopyContext(
HyprlandScreencopyManager* manager,
toplevel_management::impl::ToplevelHandle* handle,
toplevel::wlr::ToplevelHandle* handle,
bool paintCursors
)
: manager(manager)

View file

@ -3,7 +3,7 @@
#include <qwayland-hyprland-toplevel-export-v1.h>
#include <qwaylandclientextension.h>
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "../manager.hpp"
namespace qs::wayland::screencopy::hyprland {
@ -12,8 +12,7 @@ class HyprlandScreencopyManager
: public QWaylandClientExtensionTemplate<HyprlandScreencopyManager>
, public QtWayland::hyprland_toplevel_export_manager_v1 {
public:
ScreencopyContext*
captureToplevel(toplevel_management::impl::ToplevelHandle* handle, bool paintCursors);
ScreencopyContext* captureToplevel(toplevel::wlr::ToplevelHandle* handle, bool paintCursors);
static HyprlandScreencopyManager* instance();

View file

@ -3,7 +3,7 @@
#include <qtclasshelpermacros.h>
#include <qwayland-hyprland-toplevel-export-v1.h>
#include "../../toplevel_management/handle.hpp"
#include "../../toplevel/wlr_toplevel.hpp"
#include "../manager.hpp"
namespace qs::wayland::screencopy::hyprland {
@ -16,7 +16,7 @@ class HyprlandScreencopyContext
public:
explicit HyprlandScreencopyContext(
HyprlandScreencopyManager* manager,
toplevel_management::impl::ToplevelHandle* handle,
toplevel::wlr::ToplevelHandle* handle,
bool paintCursors
);
@ -43,7 +43,7 @@ private:
buffer::WlBufferRequest request;
bool copiedFirstFrame = false;
toplevel_management::impl::ToplevelHandle* handle;
toplevel::wlr::ToplevelHandle* handle;
bool paintCursors;
};

View file

@ -17,7 +17,7 @@
#endif
#if SCREENCOPY_HYPRLAND_TOPLEVEL
#include "../toplevel_management/qml.hpp"
#include "../toplevel/qml.hpp"
#include "hyprland_screencopy/hyprland_screencopy.hpp"
#endif
@ -42,7 +42,7 @@ ScreencopyContext* ScreencopyManager::createContext(QObject* object, bool paintC
}
#endif
#if SCREENCOPY_HYPRLAND_TOPLEVEL
} else if (auto* toplevel = qobject_cast<toplevel_management::Toplevel*>(object)) {
} else if (auto* toplevel = qobject_cast<toplevel::Toplevel*>(object)) {
auto* manager = hyprland::HyprlandScreencopyManager::instance();
if (manager->isActive()) {
return manager->captureToplevel(toplevel->implHandle(), paintCursors);

View file

@ -1,6 +1,5 @@
qt_add_library(quickshell-wayland-toplevel-management STATIC
manager.cpp
handle.cpp
wlr_toplevel.cpp
qml.cpp
)

View file

@ -10,23 +10,22 @@
#include "../../core/util.hpp"
#include "../../window/proxywindow.hpp"
#include "../output_tracking.hpp"
#include "handle.hpp"
#include "manager.hpp"
#include "wlr_toplevel.hpp"
namespace qs::wayland::toplevel_management {
namespace qs::wayland::toplevel {
Toplevel::Toplevel(impl::ToplevelHandle* handle, QObject* parent): QObject(parent), handle(handle) {
Toplevel::Toplevel(wlr::ToplevelHandle* handle, QObject* parent): QObject(parent), handle(handle) {
// clang-format off
QObject::connect(handle, &impl::ToplevelHandle::closed, this, &Toplevel::onClosed);
QObject::connect(handle, &impl::ToplevelHandle::appIdChanged, this, &Toplevel::appIdChanged);
QObject::connect(handle, &impl::ToplevelHandle::titleChanged, this, &Toplevel::titleChanged);
QObject::connect(handle, &impl::ToplevelHandle::parentChanged, this, &Toplevel::parentChanged);
QObject::connect(handle, &impl::ToplevelHandle::activatedChanged, this, &Toplevel::activatedChanged);
QObject::connect(handle, &wlr::ToplevelHandle::closed, this, &Toplevel::onClosed);
QObject::connect(handle, &wlr::ToplevelHandle::appIdChanged, this, &Toplevel::appIdChanged);
QObject::connect(handle, &wlr::ToplevelHandle::titleChanged, this, &Toplevel::titleChanged);
QObject::connect(handle, &wlr::ToplevelHandle::parentChanged, this, &Toplevel::parentChanged);
QObject::connect(handle, &wlr::ToplevelHandle::activatedChanged, this, &Toplevel::activatedChanged);
QObject::connect(&handle->visibleScreens, &WlOutputTracker::screenAdded, this, &Toplevel::screensChanged);
QObject::connect(&handle->visibleScreens, &WlOutputTracker::screenRemoved, this, &Toplevel::screensChanged);
QObject::connect(handle, &impl::ToplevelHandle::maximizedChanged, this, &Toplevel::maximizedChanged);
QObject::connect(handle, &impl::ToplevelHandle::minimizedChanged, this, &Toplevel::minimizedChanged);
QObject::connect(handle, &impl::ToplevelHandle::fullscreenChanged, this, &Toplevel::fullscreenChanged);
QObject::connect(handle, &wlr::ToplevelHandle::maximizedChanged, this, &Toplevel::maximizedChanged);
QObject::connect(handle, &wlr::ToplevelHandle::minimizedChanged, this, &Toplevel::minimizedChanged);
QObject::connect(handle, &wlr::ToplevelHandle::fullscreenChanged, this, &Toplevel::fullscreenChanged);
// clang-format on
}
@ -114,11 +113,11 @@ void Toplevel::onRectangleProxyDestroyed() {
}
ToplevelManager::ToplevelManager() {
auto* manager = impl::ToplevelManager::instance();
auto* manager = wlr::ToplevelManager::instance();
QObject::connect(
manager,
&impl::ToplevelManager::toplevelReady,
&wlr::ToplevelManager::toplevelReady,
this,
&ToplevelManager::onToplevelReady
);
@ -128,7 +127,7 @@ ToplevelManager::ToplevelManager() {
}
}
Toplevel* ToplevelManager::forImpl(impl::ToplevelHandle* impl) const {
Toplevel* ToplevelManager::forImpl(wlr::ToplevelHandle* impl) const {
if (impl == nullptr) return nullptr;
for (auto* toplevel: this->mToplevels.valueList()) {
@ -140,7 +139,7 @@ Toplevel* ToplevelManager::forImpl(impl::ToplevelHandle* impl) const {
ObjectModel<Toplevel>* ToplevelManager::toplevels() { return &this->mToplevels; }
void ToplevelManager::onToplevelReady(impl::ToplevelHandle* handle) {
void ToplevelManager::onToplevelReady(wlr::ToplevelHandle* handle) {
auto* toplevel = new Toplevel(handle, this);
// clang-format off
@ -191,4 +190,4 @@ Toplevel* ToplevelManagerQml::activeToplevel() {
return ToplevelManager::instance()->activeToplevel();
}
} // namespace qs::wayland::toplevel_management
} // namespace qs::wayland::toplevel

View file

@ -11,12 +11,12 @@
#include "../../core/util.hpp"
#include "../../window/proxywindow.hpp"
namespace qs::wayland::toplevel_management {
namespace qs::wayland::toplevel {
namespace impl {
namespace wlr {
class ToplevelManager; // NOLINT
class ToplevelHandle;
} // namespace impl
} // namespace wlr
///! Window from another application.
/// A window/toplevel from another application, retrievable from
@ -26,7 +26,7 @@ class Toplevel: public QObject {
Q_PROPERTY(QString appId READ appId NOTIFY appIdChanged);
Q_PROPERTY(QString title READ title NOTIFY titleChanged);
/// Parent toplevel if this toplevel is a modal/dialog, otherwise null.
Q_PROPERTY(qs::wayland::toplevel_management::Toplevel* parent READ parent NOTIFY parentChanged);
Q_PROPERTY(qs::wayland::toplevel::Toplevel* parent READ parent NOTIFY parentChanged);
/// If the window is currently activated or focused.
///
/// Activation can be requested with the @@activate() function.
@ -56,7 +56,7 @@ class Toplevel: public QObject {
QML_UNCREATABLE("Toplevels must be acquired from the ToplevelManager.");
public:
explicit Toplevel(impl::ToplevelHandle* handle, QObject* parent);
explicit Toplevel(wlr::ToplevelHandle* handle, QObject* parent);
/// Request that this toplevel is activated.
/// The request may be ignored by the compositor.
@ -91,7 +91,7 @@ public:
[[nodiscard]] bool fullscreen() const;
void setFullscreen(bool fullscreen);
[[nodiscard]] impl::ToplevelHandle* implHandle() const { return this->handle; }
[[nodiscard]] wlr::ToplevelHandle* implHandle() const { return this->handle; }
signals:
void closed();
@ -110,7 +110,7 @@ private slots:
void onRectangleProxyDestroyed();
private:
impl::ToplevelHandle* handle;
wlr::ToplevelHandle* handle;
ProxyWindowBase* rectWindow = nullptr;
QRect rectangle;
@ -121,7 +121,7 @@ class ToplevelManager: public QObject {
Q_OBJECT;
public:
Toplevel* forImpl(impl::ToplevelHandle* impl) const;
Toplevel* forImpl(wlr::ToplevelHandle* impl) const;
[[nodiscard]] ObjectModel<Toplevel>* toplevels();
@ -131,7 +131,7 @@ signals:
void activeToplevelChanged();
private slots:
void onToplevelReady(impl::ToplevelHandle* handle);
void onToplevelReady(wlr::ToplevelHandle* handle);
void onToplevelActiveChanged();
void onToplevelClosed();
@ -158,13 +158,13 @@ class ToplevelManagerQml: public QObject {
Q_OBJECT;
// clang-format off
/// All toplevel windows exposed by the compositor.
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::wayland::toplevel_management::Toplevel>*);
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::wayland::toplevel::Toplevel>*);
Q_PROPERTY(UntypedObjectModel* toplevels READ toplevels CONSTANT);
/// Active toplevel or null.
///
/// > [!INFO] If multiple are active, this will be the most recently activated one.
/// > Usually compositors will not report more than one toplevel as active at a time.
Q_PROPERTY(qs::wayland::toplevel_management::Toplevel* activeToplevel READ activeToplevel NOTIFY activeToplevelChanged);
Q_PROPERTY(qs::wayland::toplevel::Toplevel* activeToplevel READ activeToplevel NOTIFY activeToplevelChanged);
// clang-format on
QML_NAMED_ELEMENT(ToplevelManager);
QML_SINGLETON;
@ -179,4 +179,4 @@ signals:
void activeToplevelChanged();
};
} // namespace qs::wayland::toplevel_management
} // namespace qs::wayland::toplevel

View file

@ -1,4 +1,4 @@
#include "handle.hpp"
#include "wlr_toplevel.hpp"
#include <cstddef>
#include <private/qwaylanddisplay_p.h>
@ -13,13 +13,66 @@
#include <qobject.h>
#include <qscreen.h>
#include <qtmetamacros.h>
#include <qwaylandclientextension.h>
#include <wayland-util.h>
#include "manager.hpp"
#include "../../core/logcat.hpp"
#include "qwayland-wlr-foreign-toplevel-management-unstable-v1.h"
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
namespace qs::wayland::toplevel_management::impl {
namespace qs::wayland::toplevel::wlr {
QS_LOGGING_CATEGORY(logToplevelManagement, "quickshell.wayland.toplevelManagement", QtWarningMsg);
ToplevelManager::ToplevelManager(): QWaylandClientExtensionTemplate(3) { this->initialize(); }
bool ToplevelManager::available() const { return this->isActive(); }
const QVector<ToplevelHandle*>& ToplevelManager::readyToplevels() const {
return this->mReadyToplevels;
}
ToplevelHandle* ToplevelManager::handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel) {
if (toplevel == nullptr) return nullptr;
for (auto* other: this->mToplevels) {
if (other->object() == toplevel) return other;
}
return nullptr;
}
ToplevelManager* ToplevelManager::instance() {
static auto* instance = new ToplevelManager(); // NOLINT
return instance;
}
void ToplevelManager::zwlr_foreign_toplevel_manager_v1_toplevel(
::zwlr_foreign_toplevel_handle_v1* toplevel
) {
auto* handle = new ToplevelHandle();
QObject::connect(handle, &ToplevelHandle::closed, this, &ToplevelManager::onToplevelClosed);
QObject::connect(handle, &ToplevelHandle::ready, this, &ToplevelManager::onToplevelReady);
qCDebug(logToplevelManagement) << "Toplevel handle created" << handle;
this->mToplevels.push_back(handle);
// Not done in constructor as a close could technically be picked up immediately on init,
// making touching the handle a UAF.
handle->init(toplevel);
}
void ToplevelManager::onToplevelReady() {
auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
this->mReadyToplevels.push_back(handle);
emit this->toplevelReady(handle);
}
void ToplevelManager::onToplevelClosed() {
auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
this->mReadyToplevels.removeOne(handle);
this->mToplevels.removeOne(handle);
}
QString ToplevelHandle::appId() const { return this->mAppId; }
QString ToplevelHandle::title() const { return this->mTitle; }
@ -215,4 +268,4 @@ void ToplevelHandle::onParentClosed() {
emit this->parentChanged();
}
} // namespace qs::wayland::toplevel_management::impl
} // namespace qs::wayland::toplevel::wlr

View file

@ -1,15 +1,23 @@
#pragma once
#include <qloggingcategory.h>
#include <qobject.h>
#include <qrect.h>
#include <qscreen.h>
#include <qstring.h>
#include <qtmetamacros.h>
#include <qvector.h>
#include <qwayland-wlr-foreign-toplevel-management-unstable-v1.h>
#include <qwaylandclientextension.h>
#include <qwindow.h>
#include "../../core/logcat.hpp"
#include "../output_tracking.hpp"
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
namespace qs::wayland::toplevel_management::impl {
namespace qs::wayland::toplevel::wlr {
QS_DECLARE_LOGGING_CATEGORY(logToplevelManagement);
class ToplevelHandle
: public QObject
@ -73,4 +81,34 @@ private:
QWindow* rectWindow = nullptr;
};
} // namespace qs::wayland::toplevel_management::impl
class ToplevelManager
: public QWaylandClientExtensionTemplate<ToplevelManager>
, public QtWayland::zwlr_foreign_toplevel_manager_v1 {
Q_OBJECT;
public:
[[nodiscard]] bool available() const;
[[nodiscard]] const QVector<ToplevelHandle*>& readyToplevels() const;
[[nodiscard]] ToplevelHandle* handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel);
static ToplevelManager* instance();
signals:
void toplevelReady(ToplevelHandle* toplevel);
protected:
explicit ToplevelManager();
void
zwlr_foreign_toplevel_manager_v1_toplevel(::zwlr_foreign_toplevel_handle_v1* toplevel) override;
private slots:
void onToplevelReady();
void onToplevelClosed();
private:
QVector<ToplevelHandle*> mToplevels;
QVector<ToplevelHandle*> mReadyToplevels;
};
} // namespace qs::wayland::toplevel::wlr

View file

@ -1,68 +0,0 @@
#include "manager.hpp"
#include <qcontainerfwd.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qtmetamacros.h>
#include <qwaylandclientextension.h>
#include "../../core/logcat.hpp"
#include "handle.hpp"
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
namespace qs::wayland::toplevel_management::impl {
QS_LOGGING_CATEGORY(logToplevelManagement, "quickshell.wayland.toplevelManagement", QtWarningMsg);
ToplevelManager::ToplevelManager(): QWaylandClientExtensionTemplate(3) { this->initialize(); }
bool ToplevelManager::available() const { return this->isActive(); }
const QVector<ToplevelHandle*>& ToplevelManager::readyToplevels() const {
return this->mReadyToplevels;
}
ToplevelHandle* ToplevelManager::handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel) {
if (toplevel == nullptr) return nullptr;
for (auto* other: this->mToplevels) {
if (other->object() == toplevel) return other;
}
return nullptr;
}
ToplevelManager* ToplevelManager::instance() {
static auto* instance = new ToplevelManager(); // NOLINT
return instance;
}
void ToplevelManager::zwlr_foreign_toplevel_manager_v1_toplevel(
::zwlr_foreign_toplevel_handle_v1* toplevel
) {
auto* handle = new ToplevelHandle();
QObject::connect(handle, &ToplevelHandle::closed, this, &ToplevelManager::onToplevelClosed);
QObject::connect(handle, &ToplevelHandle::ready, this, &ToplevelManager::onToplevelReady);
qCDebug(logToplevelManagement) << "Toplevel handle created" << handle;
this->mToplevels.push_back(handle);
// Not done in constructor as a close could technically be picked up immediately on init,
// making touching the handle a UAF.
handle->init(toplevel);
}
void ToplevelManager::onToplevelReady() {
auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
this->mReadyToplevels.push_back(handle);
emit this->toplevelReady(handle);
}
void ToplevelManager::onToplevelClosed() {
auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
this->mReadyToplevels.removeOne(handle);
this->mToplevels.removeOne(handle);
}
} // namespace qs::wayland::toplevel_management::impl

View file

@ -1,48 +0,0 @@
#pragma once
#include <qcontainerfwd.h>
#include <qloggingcategory.h>
#include <qtmetamacros.h>
#include <qwayland-wlr-foreign-toplevel-management-unstable-v1.h>
#include <qwaylandclientextension.h>
#include "../../core/logcat.hpp"
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
namespace qs::wayland::toplevel_management::impl {
class ToplevelHandle;
QS_DECLARE_LOGGING_CATEGORY(logToplevelManagement);
class ToplevelManager
: public QWaylandClientExtensionTemplate<ToplevelManager>
, public QtWayland::zwlr_foreign_toplevel_manager_v1 {
Q_OBJECT;
public:
[[nodiscard]] bool available() const;
[[nodiscard]] const QVector<ToplevelHandle*>& readyToplevels() const;
[[nodiscard]] ToplevelHandle* handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel);
static ToplevelManager* instance();
signals:
void toplevelReady(ToplevelHandle* toplevel);
protected:
explicit ToplevelManager();
void
zwlr_foreign_toplevel_manager_v1_toplevel(::zwlr_foreign_toplevel_handle_v1* toplevel) override;
private slots:
void onToplevelReady();
void onToplevelClosed();
private:
QVector<ToplevelHandle*> mToplevels;
QVector<ToplevelHandle*> mReadyToplevels;
};
} // namespace qs::wayland::toplevel_management::impl