diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 26d29c8c..1d6543e4 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -77,6 +77,7 @@ qt_add_library(quickshell-wayland STATIC popupanchor.cpp xdgshell.cpp util.cpp + output_tracking.cpp ) # required to make sure the constructor is linked diff --git a/src/wayland/output_tracking.cpp b/src/wayland/output_tracking.cpp new file mode 100644 index 00000000..9d69ee71 --- /dev/null +++ b/src/wayland/output_tracking.cpp @@ -0,0 +1,73 @@ +#include "output_tracking.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qs::wayland { + +void WlOutputTracker::addOutput(::wl_output* output) { + auto* display = QtWaylandClient::QWaylandIntegration::instance()->display(); + + if (auto* platformScreen = display->screenForOutput(output)) { + auto* screen = platformScreen->screen(); + this->mScreens.append(screen); + emit this->screenAdded(screen); + } else { + QObject::connect( + static_cast(QGuiApplication::instance()), // NOLINT + &QGuiApplication::screenAdded, + this, + &WlOutputTracker::onQScreenAdded, + Qt::UniqueConnection + ); + + this->mOutputs.append(output); + } +} + +void WlOutputTracker::removeOutput(::wl_output* output) { + auto* display = QtWaylandClient::QWaylandIntegration::instance()->display(); + + if (auto* platformScreen = display->screenForOutput(output)) { + auto* screen = platformScreen->screen(); + this->mScreens.removeOne(screen); + emit this->screenRemoved(screen); + } else { + this->mOutputs.removeOne(output); + + if (this->mOutputs.isEmpty()) { + QObject::disconnect( + static_cast(QGuiApplication::instance()), // NOLINT + nullptr, + this, + nullptr + ); + } + } +} + +void WlOutputTracker::onQScreenAdded(QScreen* screen) { + if (auto* platformScreen = dynamic_cast(screen->handle())) { + if (this->mOutputs.removeOne(platformScreen->output())) { + this->mScreens.append(screen); + emit this->screenAdded(screen); + + if (this->mOutputs.isEmpty()) { + QObject::disconnect( + static_cast(QGuiApplication::instance()), // NOLINT + nullptr, + this, + nullptr + ); + } + } + } +} + +} // namespace qs::wayland diff --git a/src/wayland/output_tracking.hpp b/src/wayland/output_tracking.hpp new file mode 100644 index 00000000..988e53b3 --- /dev/null +++ b/src/wayland/output_tracking.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +struct wl_output; + +namespace qs::wayland { + +class WlOutputTracker: public QObject { + Q_OBJECT; + +public: + [[nodiscard]] const QList& screens() const { return this->mScreens; } + +signals: + void screenAdded(QScreen* screen); + void screenRemoved(QScreen* screen); + +public slots: + void addOutput(::wl_output* output); + void removeOutput(::wl_output* output); + +private slots: + void onQScreenAdded(QScreen* screen); + +private: + QList mScreens; + QList<::wl_output*> mOutputs; +}; + +} // namespace qs::wayland diff --git a/src/wayland/toplevel_management/handle.cpp b/src/wayland/toplevel_management/handle.cpp index 026b4396..914e44ec 100644 --- a/src/wayland/toplevel_management/handle.cpp +++ b/src/wayland/toplevel_management/handle.cpp @@ -23,7 +23,6 @@ namespace qs::wayland::toplevel_management::impl { QString ToplevelHandle::appId() const { return this->mAppId; } QString ToplevelHandle::title() const { return this->mTitle; } -QVector ToplevelHandle::visibleScreens() const { return this->mVisibleScreens; } ToplevelHandle* ToplevelHandle::parent() const { return this->mParent; } bool ToplevelHandle::activated() const { return this->mActivated; } bool ToplevelHandle::maximized() const { return this->mMaximized; } @@ -181,59 +180,13 @@ void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_state(wl_array* stateArray) } void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) { - auto* display = QtWaylandClient::QWaylandIntegration::instance()->display(); - - auto* platformScreen = display->screenForOutput(output); - if (!platformScreen) { - qCDebug(logToplevelManagement) << this << "got pending output enter" << output; - - if (this->mPendingVisibleScreens.isEmpty()) { - QObject::connect( - static_cast(QGuiApplication::instance()), // NOLINT - &QGuiApplication::screenAdded, - this, - &ToplevelHandle::onScreenAdded - ); - } - - this->mPendingVisibleScreens.append(output); - return; - } - - auto* screen = platformScreen->screen(); - - qCDebug(logToplevelManagement) << this << "got output enter" << screen; - - this->mVisibleScreens.append(screen); - emit this->visibleScreenAdded(screen); + qCDebug(logToplevelManagement) << this << "got output enter" << output; + this->visibleScreens.addOutput(output); } void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) { - auto* display = QtWaylandClient::QWaylandIntegration::instance()->display(); - auto* platformScreen = display->screenForOutput(output); - - if (!this->mPendingVisibleScreens.isEmpty()) { - this->mPendingVisibleScreens.removeOne(output); - - if (this->mPendingVisibleScreens.isEmpty()) { - qCDebug(logToplevelManagement) << this << "got pending output leave" << output; - - QObject::disconnect( - static_cast(QGuiApplication::instance()), // NOLINT - nullptr, - this, - nullptr - ); - } - } - - if (!platformScreen) return; - auto* screen = platformScreen->screen(); - - qCDebug(logToplevelManagement) << this << "got output leave" << screen; - - this->mVisibleScreens.removeOne(screen); - emit this->visibleScreenRemoved(screen); + qCDebug(logToplevelManagement) << this << "got output leave" << output; + this->visibleScreens.removeOutput(output); } void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_parent( @@ -262,26 +215,4 @@ void ToplevelHandle::onParentClosed() { emit this->parentChanged(); } -void ToplevelHandle::onScreenAdded(QScreen* screen) { - auto* waylandScreen = dynamic_cast(screen->handle()); - if (!waylandScreen) return; - - auto* output = waylandScreen->output(); - - if (this->mPendingVisibleScreens.removeOne(output)) { - qCDebug(logToplevelManagement) << this << "got pending entered output init" << screen; - this->mVisibleScreens.append(screen); - emit this->visibleScreenAdded(screen); - } - - if (this->mPendingVisibleScreens.isEmpty()) { - QObject::disconnect( - static_cast(QGuiApplication::instance()), // NOLINT - nullptr, - this, - nullptr - ); - } -} - } // namespace qs::wayland::toplevel_management::impl diff --git a/src/wayland/toplevel_management/handle.hpp b/src/wayland/toplevel_management/handle.hpp index 991069ac..92531a5a 100644 --- a/src/wayland/toplevel_management/handle.hpp +++ b/src/wayland/toplevel_management/handle.hpp @@ -6,6 +6,7 @@ #include #include +#include "../output_tracking.hpp" #include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" namespace qs::wayland::toplevel_management::impl { @@ -18,7 +19,6 @@ class ToplevelHandle public: [[nodiscard]] QString appId() const; [[nodiscard]] QString title() const; - [[nodiscard]] QVector visibleScreens() const; [[nodiscard]] ToplevelHandle* parent() const; [[nodiscard]] bool activated() const; [[nodiscard]] bool maximized() const; @@ -32,6 +32,8 @@ public: void fullscreenOn(QScreen* screen); void setRectangle(QWindow* window, QRect rect); + WlOutputTracker visibleScreens; + signals: // sent after the first done event. void ready(); @@ -40,8 +42,6 @@ signals: void appIdChanged(); void titleChanged(); - void visibleScreenAdded(QScreen* screen); - void visibleScreenRemoved(QScreen* screen); void parentChanged(); void activatedChanged(); void maximizedChanged(); @@ -51,7 +51,6 @@ signals: private slots: void onParentClosed(); void onRectWindowDestroyed(); - void onScreenAdded(QScreen* screen); private: void zwlr_foreign_toplevel_handle_v1_done() override; @@ -66,8 +65,6 @@ private: bool isReady = false; QString mAppId; QString mTitle; - QVector mVisibleScreens; - QVector mPendingVisibleScreens; ToplevelHandle* mParent = nullptr; bool mActivated = false; bool mMaximized = false; diff --git a/src/wayland/toplevel_management/qml.cpp b/src/wayland/toplevel_management/qml.cpp index 0d14d4d3..0eae3de6 100644 --- a/src/wayland/toplevel_management/qml.cpp +++ b/src/wayland/toplevel_management/qml.cpp @@ -10,6 +10,7 @@ #include "../../core/util.hpp" #include "../../window/proxywindow.hpp" #include "../../window/windowinterface.hpp" +#include "../output_tracking.hpp" #include "handle.hpp" #include "manager.hpp" @@ -22,8 +23,8 @@ Toplevel::Toplevel(impl::ToplevelHandle* handle, QObject* parent): QObject(paren 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, &impl::ToplevelHandle::visibleScreenAdded, this, &Toplevel::screensChanged); - QObject::connect(handle, &impl::ToplevelHandle::visibleScreenRemoved, this, &Toplevel::screensChanged); + 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); @@ -50,7 +51,7 @@ bool Toplevel::activated() const { return this->handle->activated(); } QList Toplevel::screens() const { QList screens; - for (auto* screen: this->handle->visibleScreens()) { + for (auto* screen: this->handle->visibleScreens.screens()) { screens.push_back(QuickshellTracked::instance()->screenInfo(screen)); }