From 6464ead0f120da20ecd12122d6181e294813f40a Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Mon, 20 Jan 2025 00:51:56 -0800 Subject: [PATCH] core/window: move input mask handling + commit scheduling to polish --- src/core/region.cpp | 12 +++++++ src/core/region.hpp | 6 ++++ src/wayland/hyprland/surface/qml.cpp | 4 +-- src/wayland/util.cpp | 12 ++----- src/wayland/util.hpp | 4 +-- src/window/proxywindow.cpp | 49 ++++++++++++++++++---------- src/window/proxywindow.hpp | 21 +++++++++++- 7 files changed, 76 insertions(+), 32 deletions(-) diff --git a/src/core/region.cpp b/src/core/region.cpp index 47f15d4a..439cfbd2 100644 --- a/src/core/region.cpp +++ b/src/core/region.cpp @@ -8,6 +8,7 @@ #include #include #include +#include PendingRegion::PendingRegion(QObject* parent): QObject(parent) { QObject::connect(this, &PendingRegion::shapeChanged, this, &PendingRegion::changed); @@ -105,8 +106,19 @@ QRegion PendingRegion::applyTo(QRegion& region) const { return region; } +QRegion PendingRegion::applyTo(const QRect& rect) const { + // if left as the default, dont combine it with the whole rect area, leave it as is. + if (this->mIntersection == Intersection::Combine) { + return this->build(); + } else { + auto baseRegion = QRegion(rect); + return this->applyTo(baseRegion); + } +} + void PendingRegion::regionsAppend(QQmlListProperty* prop, PendingRegion* region) { auto* self = static_cast(prop->object); // NOLINT + if (!region) return; QObject::connect(region, &QObject::destroyed, self, &PendingRegion::onChildDestroyed); QObject::connect(region, &PendingRegion::changed, self, &PendingRegion::childrenChanged); diff --git a/src/core/region.hpp b/src/core/region.hpp index 02d7a26b..6637d7bd 100644 --- a/src/core/region.hpp +++ b/src/core/region.hpp @@ -96,6 +96,7 @@ public: [[nodiscard]] bool empty() const; [[nodiscard]] QRegion build() const; [[nodiscard]] QRegion applyTo(QRegion& region) const; + [[nodiscard]] QRegion applyTo(const QRect& rect) const; RegionShape::Enum mShape = RegionShape::Rect; Intersection::Enum mIntersection = Intersection::Combine; @@ -109,6 +110,11 @@ signals: void widthChanged(); void heightChanged(); void childrenChanged(); + + /// Triggered when the region's geometry changes. + /// + /// In some cases the region does not update automatically. + /// In those cases you can emit this signal manually. void changed(); private slots: diff --git a/src/wayland/hyprland/surface/qml.cpp b/src/wayland/hyprland/surface/qml.cpp index ab8aace8..5150487f 100644 --- a/src/wayland/hyprland/surface/qml.cpp +++ b/src/wayland/hyprland/surface/qml.cpp @@ -62,7 +62,7 @@ void HyprlandWindow::setOpacity(qreal opacity) { if (this->surface) { this->surface->setOpacity(opacity); - qs::wayland::util::scheduleCommit(this->mWaylandWindow); + qs::wayland::util::scheduleCommit(this->proxyWindow); } emit this->opacityChanged(); @@ -127,7 +127,7 @@ void HyprlandWindow::onWaylandSurfaceCreated() { if (this->mOpacity != 1.0) { this->surface->setOpacity(this->mOpacity); - qs::wayland::util::scheduleCommit(this->mWaylandWindow); + qs::wayland::util::scheduleCommit(this->proxyWindow); } } diff --git a/src/wayland/util.cpp b/src/wayland/util.cpp index 6bce2621..abcf8a4d 100644 --- a/src/wayland/util.cpp +++ b/src/wayland/util.cpp @@ -1,17 +1,9 @@ #include "util.hpp" -#include -#include +#include "../window/proxywindow.hpp" namespace qs::wayland::util { -void scheduleCommit(QtWaylandClient::QWaylandWindow* window) { - // This seems to be one of the less offensive ways to force Qt to send a wl_surface.commit on its own terms. - // Ideally we would trigger the commit more directly. - QWindowSystemInterface::handleExposeEvent( - window->window(), - QRect(QPoint(), window->geometry().size()) - ); -} +void scheduleCommit(ProxyWindowBase* window) { window->schedulePolish(); } } // namespace qs::wayland::util diff --git a/src/wayland/util.hpp b/src/wayland/util.hpp index 7967fadc..aadf44f1 100644 --- a/src/wayland/util.hpp +++ b/src/wayland/util.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include "../window/proxywindow.hpp" namespace qs::wayland::util { -void scheduleCommit(QtWaylandClient::QWaylandWindow* window); +void scheduleCommit(ProxyWindowBase* window); } diff --git a/src/window/proxywindow.cpp b/src/window/proxywindow.cpp index 608883ae..d89a6344 100644 --- a/src/window/proxywindow.cpp +++ b/src/window/proxywindow.cpp @@ -28,15 +28,16 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent) : Reloadable(parent) - , mContentItem(new QQuickItem()) { + , mContentItem(new ProxyWindowContentItem()) { QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership); this->mContentItem->setParent(this); // clang-format off + QObject::connect(this->mContentItem, &ProxyWindowContentItem::polished, this, &ProxyWindowBase::onPolished); + QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged); QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onHeightChanged); - QObject::connect(this, &ProxyWindowBase::maskChanged, this, &ProxyWindowBase::onMaskChanged); QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged); QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged); @@ -264,6 +265,12 @@ void ProxyWindowBase::setVisibleDirect(bool visible) { } } +void ProxyWindowBase::schedulePolish() { + if (this->isVisibleDirect()) { + this->mContentItem->polish(); + } +} + void ProxyWindowBase::polishItems() { // Due to QTBUG-126704, layouts in invisible windows don't update their dimensions. // Usually this isn't an issue, but it is when the size of a window is based on the size @@ -385,11 +392,11 @@ void ProxyWindowBase::setMask(PendingRegion* mask) { this->mMask = mask; if (mask != nullptr) { - mask->setParent(this); QObject::connect(mask, &QObject::destroyed, this, &ProxyWindowBase::onMaskDestroyed); - QObject::connect(mask, &PendingRegion::changed, this, &ProxyWindowBase::maskChanged); + QObject::connect(mask, &PendingRegion::changed, this, &ProxyWindowBase::onMaskChanged); } + this->onMaskChanged(); emit this->maskChanged(); } @@ -410,23 +417,13 @@ void ProxyWindowBase::onMaskChanged() { void ProxyWindowBase::onMaskDestroyed() { this->mMask = nullptr; + this->onMaskChanged(); emit this->maskChanged(); } void ProxyWindowBase::updateMask() { - QRegion mask; - if (this->mMask != nullptr) { - // if left as the default, dont combine it with the whole window area, leave it as is. - if (this->mMask->mIntersection == Intersection::Combine) { - mask = this->mMask->build(); - } else { - auto windowRegion = QRegion(QRect(0, 0, this->width(), this->height())); - mask = this->mMask->applyTo(windowRegion); - } - } - - this->window->setFlag(Qt::WindowTransparentForInput, this->mMask != nullptr && mask.isEmpty()); - this->window->setMask(mask); + this->pendingPolish.inputMask = true; + this->schedulePolish(); } QQmlListProperty ProxyWindowBase::data() { @@ -463,3 +460,21 @@ void ProxiedWindow::exposeEvent(QExposeEvent* event) { this->QQuickWindow::exposeEvent(event); emit this->exposed(); } + +void ProxyWindowContentItem::updatePolish() { emit this->polished(); } + +void ProxyWindowBase::onPolished() { + if (this->pendingPolish.inputMask) { + QRegion mask; + if (this->mMask != nullptr) { + mask = this->mMask->applyTo(QRect(0, 0, this->width(), this->height())); + } + + this->window->setFlag(Qt::WindowTransparentForInput, this->mMask != nullptr && mask.isEmpty()); + this->window->setMask(mask); + + this->pendingPolish.inputMask = false; + } + + emit this->polished(); +} diff --git a/src/window/proxywindow.hpp b/src/window/proxywindow.hpp index 2ed4bcd3..6c7946b1 100644 --- a/src/window/proxywindow.hpp +++ b/src/window/proxywindow.hpp @@ -20,6 +20,7 @@ #include "windowinterface.hpp" class ProxiedWindow; +class ProxyWindowContentItem; // Proxy to an actual window exposing a limited property set with the ability to // transfer it to a new window. @@ -85,6 +86,8 @@ public: virtual void setVisible(bool visible); virtual void setVisibleDirect(bool visible); + void schedulePolish(); + [[nodiscard]] virtual qint32 x() const; [[nodiscard]] virtual qint32 y() const; @@ -124,6 +127,7 @@ signals: void colorChanged(); void maskChanged(); void surfaceFormatChanged(); + void polished(); protected slots: virtual void onWidthChanged(); @@ -131,6 +135,7 @@ protected slots: void onMaskChanged(); void onMaskDestroyed(); void onScreenDestroyed(); + void onPolished(); void runLints(); protected: @@ -141,12 +146,16 @@ protected: QColor mColor = Qt::white; PendingRegion* mMask = nullptr; ProxiedWindow* window = nullptr; - QQuickItem* mContentItem = nullptr; + ProxyWindowContentItem* mContentItem = nullptr; bool reloadComplete = false; bool ranLints = false; QsSurfaceFormat qsSurfaceFormat; QSurfaceFormat mSurfaceFormat; + struct { + bool inputMask : 1 = false; + } pendingPolish; + private: void polishItems(); void updateMask(); @@ -190,3 +199,13 @@ protected: private: ProxyWindowBase* mProxy; }; + +class ProxyWindowContentItem: public QQuickItem { + Q_OBJECT; + +signals: + void polished(); + +protected: + void updatePolish() override; +};