From 3a0381dcbe9a8fe5f17ab5c9b085d6428b6b8285 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 27 Mar 2024 00:44:13 -0700 Subject: [PATCH] core/window: backing windows can now be destroyed and recreated This fixes a crash in layershells and the setVisible crash on nvidia. --- CMakeLists.txt | 19 ++- README.md | 12 ++ default.nix | 5 +- flake.nix | 5 +- package.nix | 1 - src/core/generation.cpp | 40 ++++++- src/core/generation.hpp | 1 + src/core/popupwindow.cpp | 45 +++---- src/core/popupwindow.hpp | 5 +- src/core/proxywindow.cpp | 166 ++++++++++++++++++-------- src/core/proxywindow.hpp | 17 ++- src/core/test/CMakeLists.txt | 12 +- src/core/test/popupwindow.cpp | 2 - src/wayland/wlr_layershell.cpp | 32 +++-- src/wayland/wlr_layershell.hpp | 6 +- src/wayland/wlr_layershell/window.cpp | 1 - 16 files changed, 257 insertions(+), 112 deletions(-) delete mode 100644 package.nix diff --git a/CMakeLists.txt b/CMakeLists.txt index ccdb5a8..cde8d62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,14 +5,18 @@ set(QT_MIN_VERSION "6.6.0") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -option(TESTS "Build tests" OFF) +option(BUILD_TESTING "Build tests" OFF) +option(ASAN "Enable ASAN" OFF) +option(FRAME_POINTERS "Always keep frame pointers" ${ASAN}) +option(NVIDIA_COMPAT "Workarounds for nvidia gpus" OFF) option(SOCKETS "Enable unix socket support" ON) option(WAYLAND "Enable wayland support" ON) option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" ON) option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON) message(STATUS "Quickshell configuration") +message(STATUS " NVIDIA workarounds: ${NVIDIA_COMPAT}") message(STATUS " Build tests: ${BUILD_TESTING}") message(STATUS " Sockets: ${SOCKETS}") message(STATUS " Wayland: ${WAYLAND}") @@ -31,6 +35,15 @@ endif() add_compile_options(-Wall -Wextra) +if (FRAME_POINTERS) + add_compile_options(-fno-omit-frame-pointer) +endif() + +if (ASAN) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + # nix workaround if (CMAKE_EXPORT_COMPILE_COMMANDS) set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) @@ -92,4 +105,8 @@ function (qs_pch target) endif() endfunction() +if (NVIDIA_COMPAT) + add_compile_definitions(NVIDIA_COMPAT) +endif() + add_subdirectory(src) diff --git a/README.md b/README.md index 37fd30f..74e741a 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ This repo has a nix flake you can use to install the package directly: Quickshell's binary is available at `quickshell.packages..default` to be added to lists such as `environment.systemPackages` or `home.packages`. +`quickshell.packages..nvidia` is also available for nvidia users which fixes some +common crashes. + Note: by default this package is built with clang as it is significantly faster. ## Manual @@ -75,6 +78,15 @@ To make a release build of quickshell run: $ just release ``` +If running an nvidia GPU, instead run: +```sh +$ just configure release -DNVIDIA_COMPAT=ON +$ just build +``` + +(These commands are just aliases for cmake commands you can run directly, +see the Justfile for more information.) + If you have all the dependencies installed and they are in expected locations this will build correctly. diff --git a/default.nix b/default.nix index e29c216..60865ac 100644 --- a/default.nix +++ b/default.nix @@ -21,8 +21,10 @@ then builtins.readFile ./.git/refs/heads/${builtins.elemAt matches 0} else headContent) else "unknown"), + debug ? false, enableWayland ? true, + nvidiaCompat ? false, }: buildStdenv.mkDerivation { pname = "quickshell${lib.optionalString debug "-debug"}"; version = "0.1.0"; @@ -56,7 +58,8 @@ cmakeFlags = [ "-DGIT_REVISION=${gitRev}" - ] ++ lib.optional (!enableWayland) "-DWAYLAND=OFF"; + ] ++ lib.optional (!enableWayland) "-DWAYLAND=OFF" + ++ lib.optional nvidiaCompat "-DNVIDIA_COMPAT=ON"; buildPhase = "ninjaBuildPhase"; enableParallelBuilding = true; diff --git a/flake.nix b/flake.nix index b9ba8d1..8e5f75b 100644 --- a/flake.nix +++ b/flake.nix @@ -9,8 +9,11 @@ (system: fn system nixpkgs.legacyPackages.${system}); in { packages = forEachSystem (system: pkgs: rec { - quickshell = import ./package.nix { inherit pkgs; }; + quickshell = pkgs.callPackage ./default.nix {}; + quickshell-nvidia = pkgs.callPackage ./default.nix { nvidiaCompat = true; }; + default = quickshell; + nvidia = quickshell-nvidia; }); devShells = forEachSystem (system: pkgs: rec { diff --git a/package.nix b/package.nix deleted file mode 100644 index a3e6249..0000000 --- a/package.nix +++ /dev/null @@ -1 +0,0 @@ -{ pkgs ? import {}, ... }: pkgs.callPackage ./default.nix {} diff --git a/src/core/generation.cpp b/src/core/generation.cpp index 2eb7ddf..76d1910 100644 --- a/src/core/generation.cpp +++ b/src/core/generation.cpp @@ -122,6 +122,32 @@ void EngineGeneration::registerIncubationController(QQmlIncubationController* co } } +void EngineGeneration::deregisterIncubationController(QQmlIncubationController* controller) { + QObject* obj = nullptr; + this->incubationControllers.removeIf([&](QPair other) { + if (controller == other.first) { + obj = other.second; + return true; + } else return false; + }); + + if (obj == nullptr) { + qCWarning(logIncubator) << "Failed to deregister incubation controller" << controller + << "as it was not registered to begin with"; + qCWarning(logIncubator) << "Current registered incuabation controllers" + << this->incubationControllers; + } else { + QObject::disconnect(obj, nullptr, this, nullptr); + qCDebug(logIncubator) << "Deregistered incubation controller" << controller; + } + + if (this->engine.incubationController() == controller) { + qCDebug(logIncubator + ) << "Destroyed incubation controller was currently active, reassigning from pool"; + this->assignIncubationController(); + } +} + void EngineGeneration::incubationControllerDestroyed() { auto* sender = this->sender(); QQmlIncubationController* controller = nullptr; @@ -150,8 +176,9 @@ void EngineGeneration::incubationControllerDestroyed() { } void EngineGeneration::assignIncubationController() { - auto* controller = this->incubationControllers.first().first; - if (controller == nullptr) controller = &this->delayedIncubationController; + QQmlIncubationController* controller = nullptr; + if (this->incubationControllers.isEmpty()) controller = &this->delayedIncubationController; + else controller = this->incubationControllers.first().first; qCDebug(logIncubator) << "Assigning incubation controller to engine:" << controller << "fallback:" << (controller == &this->delayedIncubationController); @@ -162,9 +189,14 @@ void EngineGeneration::assignIncubationController() { EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) { while (object != nullptr) { auto* context = QQmlEngine::contextForObject(object); - if (auto* generation = g_generations.value(context->engine())) { - return generation; + + if (context != nullptr) { + if (auto* generation = g_generations.value(context->engine())) { + return generation; + } } + + object = object->parent(); } return nullptr; diff --git a/src/core/generation.hpp b/src/core/generation.hpp index e730ca2..d80911d 100644 --- a/src/core/generation.hpp +++ b/src/core/generation.hpp @@ -28,6 +28,7 @@ public: void setWatchingFiles(bool watching); void registerIncubationController(QQmlIncubationController* controller); + void deregisterIncubationController(QQmlIncubationController* controller); static EngineGeneration* findObjectGeneration(QObject* object); diff --git a/src/core/popupwindow.cpp b/src/core/popupwindow.cpp index 392e665..40a3d19 100644 --- a/src/core/popupwindow.cpp +++ b/src/core/popupwindow.cpp @@ -15,15 +15,19 @@ ProxyPopupWindow::ProxyPopupWindow(QObject* parent): ProxyWindowBase(parent) { this->mVisible = false; } -void ProxyPopupWindow::setupWindow() { - this->ProxyWindowBase::setupWindow(); +void ProxyPopupWindow::completeWindow() { + this->ProxyWindowBase::completeWindow(); this->window->setFlag(Qt::ToolTip); this->updateTransientParent(); } +void ProxyPopupWindow::postCompleteWindow() {} + qint32 ProxyPopupWindow::x() const { - return this->ProxyWindowBase::x() + 1; // QTBUG-121550 + // QTBUG-121550 + auto basepos = this->mParentProxyWindow == nullptr ? 0 : this->mParentProxyWindow->x(); + return basepos + this->mRelativeX; } void ProxyPopupWindow::setParentWindow(QObject* parent) { @@ -58,7 +62,7 @@ void ProxyPopupWindow::setParentWindow(QObject* parent) { QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::xChanged, this, &ProxyPopupWindow::updateX); QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::yChanged, this, &ProxyPopupWindow::updateY); - QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::windowConnected, this, &ProxyPopupWindow::onParentConnected); + QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::backerVisibilityChanged, this, &ProxyPopupWindow::onParentUpdated); // clang-format on } @@ -68,18 +72,19 @@ void ProxyPopupWindow::setParentWindow(QObject* parent) { QObject* ProxyPopupWindow::parentWindow() const { return this->mParentWindow; } void ProxyPopupWindow::updateTransientParent() { - if (this->window == nullptr) return; this->updateX(); this->updateY(); - this->window->setTransientParent( - this->mParentProxyWindow == nullptr ? nullptr : this->mParentProxyWindow->backingWindow() - ); + if (this->window != nullptr) { + this->window->setTransientParent( + this->mParentProxyWindow == nullptr ? nullptr : this->mParentProxyWindow->backingWindow() + ); + } this->updateVisible(); } -void ProxyPopupWindow::onParentConnected() { this->updateTransientParent(); } +void ProxyPopupWindow::onParentUpdated() { this->updateTransientParent(); } void ProxyPopupWindow::onParentDestroyed() { this->mParentWindow = nullptr; @@ -99,7 +104,8 @@ void ProxyPopupWindow::setVisible(bool visible) { } void ProxyPopupWindow::updateVisible() { - auto target = this->wantsVisible && this->mParentWindow != nullptr; + auto target = this->wantsVisible && this->mParentWindow != nullptr + && this->mParentProxyWindow->isVisibleDirect(); if (target && this->window != nullptr && !this->window->isVisible()) { this->updateX(); // QTBUG-121550 @@ -127,13 +133,12 @@ qint32 ProxyPopupWindow::relativeY() const { return this->mRelativeY; } void ProxyPopupWindow::updateX() { if (this->mParentWindow == nullptr || this->window == nullptr) return; - // use the backing window's x to account for popups in popups with overridden x positions - auto target = this->mParentProxyWindow->backingWindow()->x() + this->relativeX(); + auto target = this->x() - 1; // QTBUG-121550 - auto reshow = this->window->isVisible() && (this->window->x() != target && this->x() != target); - if (reshow) this->window->setVisible(false); - this->window->setX(target - 1); // -1 due to QTBUG-121550 - if (reshow && this->wantsVisible) this->window->setVisible(true); + auto reshow = this->isVisibleDirect() && (this->window->x() != target && this->x() != target); + if (reshow) this->setVisibleDirect(false); + if (this->window != nullptr) this->window->setX(target); + if (reshow && this->wantsVisible) this->setVisibleDirect(true); } void ProxyPopupWindow::updateY() { @@ -141,11 +146,11 @@ void ProxyPopupWindow::updateY() { auto target = this->mParentProxyWindow->y() + this->relativeY(); - auto reshow = this->window->isVisible() && this->window->y() != target; + auto reshow = this->isVisibleDirect() && this->window->y() != target; if (reshow) { - this->window->setVisible(false); + this->setVisibleDirect(false); this->updateX(); // QTBUG-121550 } - this->window->setY(target); - if (reshow && this->wantsVisible) this->window->setVisible(true); + if (this->window != nullptr) this->window->setY(target); + if (reshow && this->wantsVisible) this->setVisibleDirect(true); } diff --git a/src/core/popupwindow.hpp b/src/core/popupwindow.hpp index cb691a5..d0aa4d9 100644 --- a/src/core/popupwindow.hpp +++ b/src/core/popupwindow.hpp @@ -62,7 +62,8 @@ class ProxyPopupWindow: public ProxyWindowBase { public: explicit ProxyPopupWindow(QObject* parent = nullptr); - void setupWindow() override; + void completeWindow() override; + void postCompleteWindow() override; void setScreen(QuickshellScreenInfo* screen) override; void setVisible(bool visible) override; @@ -84,7 +85,7 @@ signals: void relativeYChanged(); private slots: - void onParentConnected(); + void onParentUpdated(); void onParentDestroyed(); void updateX(); void updateY(); diff --git a/src/core/proxywindow.cpp b/src/core/proxywindow.cpp index 6cd8b34..06ee9ec 100644 --- a/src/core/proxywindow.cpp +++ b/src/core/proxywindow.cpp @@ -23,51 +23,81 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent) QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership); this->mContentItem->setParent(this); + // clang-format off 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); + // clang-format on } -ProxyWindowBase::~ProxyWindowBase() { - if (this->window != nullptr) { - this->window->deleteLater(); +ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(); } + +void ProxyWindowBase::onReload(QObject* oldInstance) { + this->window = this->retrieveWindow(oldInstance); + auto wasVisible = this->window != nullptr && this->window->isVisible(); + if (this->window == nullptr) this->window = new QQuickWindow(); + + Reloadable::reloadRecursive(this->mContentItem, oldInstance); + + this->connectWindow(); + this->completeWindow(); + + emit this->windowConnected(); + this->postCompleteWindow(); + + if (wasVisible && this->isVisibleDirect()) emit this->backerVisibilityChanged(); +} + +void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); } + +QQuickWindow* ProxyWindowBase::createQQuickWindow() { return new QQuickWindow(); } + +void ProxyWindowBase::createWindow() { + if (this->window != nullptr) return; + this->window = this->createQQuickWindow(); + + this->connectWindow(); + this->completeWindow(); + emit this->windowConnected(); +} + +void ProxyWindowBase::deleteWindow() { + if (auto* window = this->disownWindow()) { + if (auto* generation = EngineGeneration::findObjectGeneration(this)) { + generation->deregisterIncubationController(window->incubationController()); + } + + window->deleteLater(); } } -void ProxyWindowBase::onReload(QObject* oldInstance) { - this->window = this->createWindow(oldInstance); +QQuickWindow* ProxyWindowBase::disownWindow() { + if (this->window == nullptr) return nullptr; + QObject::disconnect(this->window, nullptr, this, nullptr); + + this->mContentItem->setParentItem(nullptr); + + auto* window = this->window; + this->window = nullptr; + return window; +} + +QQuickWindow* ProxyWindowBase::retrieveWindow(QObject* oldInstance) { + auto* old = qobject_cast(oldInstance); + return old == nullptr ? nullptr : old->disownWindow(); +} + +void ProxyWindowBase::connectWindow() { if (auto* generation = EngineGeneration::findObjectGeneration(this)) { // All windows have effectively the same incubation controller so it dosen't matter // which window it belongs to. We do want to replace the delay one though. generation->registerIncubationController(this->window->incubationController()); } - this->setupWindow(); - - Reloadable::reloadRecursive(this->mContentItem, oldInstance); - - this->mContentItem->setParentItem(this->window->contentItem()); - this->mContentItem->setWidth(this->width()); - this->mContentItem->setHeight(this->height()); - - // without this the dangling screen pointer wont be updated to a real screen - emit this->screenChanged(); - - emit this->windowConnected(); - this->window->setVisible(this->mVisible); -} - -QQuickWindow* ProxyWindowBase::createWindow(QObject* oldInstance) { - auto* old = qobject_cast(oldInstance); - - if (old == nullptr || old->window == nullptr) { - return new QQuickWindow(); - } else { - return old->disownWindow(); - } -} - -void ProxyWindowBase::setupWindow() { // clang-format off QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged); QObject::connect(this->window, &QWindow::xChanged, this, &ProxyWindowBase::xChanged); @@ -76,12 +106,10 @@ void ProxyWindowBase::setupWindow() { QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged); QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged); QObject::connect(this->window, &QQuickWindow::colorChanged, this, &ProxyWindowBase::colorChanged); - - QObject::connect(this, &ProxyWindowBase::maskChanged, this, &ProxyWindowBase::onMaskChanged); - QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged); - QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged); // clang-format on +} +void ProxyWindowBase::completeWindow() { if (this->mScreen != nullptr && this->window->screen() != this->mScreen) { if (this->window->isVisible()) this->window->setVisible(false); this->window->setScreen(this->mScreen); @@ -95,16 +123,24 @@ void ProxyWindowBase::setupWindow() { // notify initial x and y positions emit this->xChanged(); emit this->yChanged(); + + this->mContentItem->setParentItem(this->window->contentItem()); + this->mContentItem->setWidth(this->width()); + this->mContentItem->setHeight(this->height()); + + // without this the dangling screen pointer wont be updated to a real screen + emit this->screenChanged(); } -QQuickWindow* ProxyWindowBase::disownWindow() { - QObject::disconnect(this->window, nullptr, this, nullptr); - - this->mContentItem->setParentItem(nullptr); - - auto* window = this->window; - this->window = nullptr; - return window; +bool ProxyWindowBase::deleteOnInvisible() const { +#ifdef NVIDIA_COMPAT + // Nvidia drivers and Qt do not play nice when hiding and showing a window + // so for nvidia compatibility we can never reuse windows if they have been + // hidden. + return true; +#else + return false; +#endif } QQuickWindow* ProxyWindowBase::backingWindow() const { return this->window; } @@ -112,14 +148,38 @@ QQuickItem* ProxyWindowBase::contentItem() const { return this->mContentItem; } bool ProxyWindowBase::isVisible() const { if (this->window == nullptr) return this->mVisible; + else return this->isVisibleDirect(); +} + +bool ProxyWindowBase::isVisibleDirect() const { + if (this->window == nullptr) return false; else return this->window->isVisible(); } void ProxyWindowBase::setVisible(bool visible) { - if (this->window == nullptr) { - this->mVisible = visible; - emit this->visibleChanged(); - } else this->window->setVisible(visible); + this->mVisible = visible; + this->setVisibleDirect(visible); +} + +void ProxyWindowBase::setVisibleDirect(bool visible) { + if (this->deleteOnInvisible()) { + if (visible == this->isVisibleDirect()) return; + + if (visible) { + this->createWindow(); + this->window->setVisible(true); + emit this->backerVisibilityChanged(); + } else { + if (this->window != nullptr) { + this->window->setVisible(false); + emit this->backerVisibilityChanged(); + this->deleteWindow(); + } + } + } else if (this->window != nullptr) { + this->window->setVisible(visible); + emit this->backerVisibilityChanged(); + } } qint32 ProxyWindowBase::x() const { @@ -138,8 +198,8 @@ qint32 ProxyWindowBase::width() const { } void ProxyWindowBase::setWidth(qint32 width) { + this->mWidth = width; if (this->window == nullptr) { - this->mWidth = width; emit this->widthChanged(); } else this->window->setWidth(width); } @@ -150,8 +210,8 @@ qint32 ProxyWindowBase::height() const { } void ProxyWindowBase::setHeight(qint32 height) { + this->mHeight = height; if (this->window == nullptr) { - this->mHeight = height; emit this->heightChanged(); } else this->window->setHeight(height); } @@ -170,10 +230,10 @@ void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) { this->mScreen = qscreen; emit this->screenChanged(); } else { - auto reshow = this->window->isVisible(); - if (reshow) this->window->setVisible(false); - this->window->setScreen(qscreen); - if (reshow) this->window->setVisible(true); + auto reshow = this->isVisibleDirect(); + if (reshow) this->setVisibleDirect(false); + if (this->window != nullptr) this->window->setScreen(qscreen); + if (reshow) this->setVisibleDirect(true); } } diff --git a/src/core/proxywindow.hpp b/src/core/proxywindow.hpp index 8259c38..64314ee 100644 --- a/src/core/proxywindow.hpp +++ b/src/core/proxywindow.hpp @@ -12,6 +12,7 @@ #include #include +#include "qmlglobal.hpp" #include "qmlscreen.hpp" #include "region.hpp" #include "reload.hpp" @@ -54,18 +55,26 @@ public: void operator=(ProxyWindowBase&&) = delete; void onReload(QObject* oldInstance) override; - - virtual QQuickWindow* createWindow(QObject* oldInstance); - virtual void setupWindow(); + void createWindow(); + void deleteWindow(); // Disown the backing window and delete all its children. virtual QQuickWindow* disownWindow(); + virtual QQuickWindow* retrieveWindow(QObject* oldInstance); + virtual QQuickWindow* createQQuickWindow(); + virtual void connectWindow(); + virtual void completeWindow(); + virtual void postCompleteWindow(); + [[nodiscard]] virtual bool deleteOnInvisible() const; + [[nodiscard]] QQuickWindow* backingWindow() const; [[nodiscard]] QQuickItem* contentItem() const; [[nodiscard]] virtual bool isVisible() const; + [[nodiscard]] virtual bool isVisibleDirect() const; virtual void setVisible(bool visible); + virtual void setVisibleDirect(bool visible); [[nodiscard]] virtual qint32 x() const; [[nodiscard]] virtual qint32 y() const; @@ -90,10 +99,12 @@ public: signals: void windowConnected(); void visibleChanged(); + void backerVisibilityChanged(); void xChanged(); void yChanged(); void widthChanged(); void heightChanged(); + void windowTransformChanged(); void screenChanged(); void colorChanged(); void maskChanged(); diff --git a/src/core/test/CMakeLists.txt b/src/core/test/CMakeLists.txt index 82d7ce0..766ba58 100644 --- a/src/core/test/CMakeLists.txt +++ b/src/core/test/CMakeLists.txt @@ -1,15 +1,7 @@ function (qs_test name) add_executable(${name} ${ARGN}) - target_link_libraries(${name} PRIVATE ${QT_DEPS} Qt6::Test) + target_link_libraries(${name} PRIVATE ${QT_DEPS} Qt6::Test quickshell-core) add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $) endfunction() -qs_test(popupwindow - popupwindow.cpp - ../popupwindow.cpp - ../proxywindow.cpp - ../qmlscreen.cpp - ../region.cpp - ../reload.cpp - ../windowinterface.cpp -) +qs_test(popupwindow popupwindow.cpp) diff --git a/src/core/test/popupwindow.cpp b/src/core/test/popupwindow.cpp index a7d90d1..91dcfa5 100644 --- a/src/core/test/popupwindow.cpp +++ b/src/core/test/popupwindow.cpp @@ -1,6 +1,5 @@ #include "popupwindow.hpp" -#include #include #include #include @@ -52,7 +51,6 @@ void TestPopupWindow::reloadReparent() { // NOLINT auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged); - qDebug() << "reload"; newParent.onReload(&parent); newPopup.onReload(&popup); diff --git a/src/wayland/wlr_layershell.cpp b/src/wayland/wlr_layershell.cpp index 7492ac7..5d7f674 100644 --- a/src/wayland/wlr_layershell.cpp +++ b/src/wayland/wlr_layershell.cpp @@ -18,33 +18,34 @@ WlrLayershell::WlrLayershell(QObject* parent) : ProxyWindowBase(parent) , ext(new LayershellWindowExtension(this)) {} -QQuickWindow* WlrLayershell::createWindow(QObject* oldInstance) { +QQuickWindow* WlrLayershell::retrieveWindow(QObject* oldInstance) { auto* old = qobject_cast(oldInstance); - QQuickWindow* window = nullptr; - - if (old == nullptr || old->window == nullptr) { - window = new QQuickWindow(); - } else { - window = old->disownWindow(); + auto* window = old == nullptr ? nullptr : old->disownWindow(); + if (window != nullptr) { if (this->ext->attach(window)) { return window; } else { window->deleteLater(); - window = new QQuickWindow(); } } + return this->createQQuickWindow(); +} + +QQuickWindow* WlrLayershell::createQQuickWindow() { + auto* window = new QQuickWindow(); + if (!this->ext->attach(window)) { - qWarning() << "Could not attach Layershell extension to new QQUickWindow. Layer will not " + qWarning() << "Could not attach Layershell extension to new QQuickWindow. Layer will not " "behave correctly."; } return window; } -void WlrLayershell::setupWindow() { - this->ProxyWindowBase::setupWindow(); +void WlrLayershell::connectWindow() { + this->ProxyWindowBase::connectWindow(); // clang-format off QObject::connect(this->ext, &LayershellWindowExtension::layerChanged, this, &WlrLayershell::layerChanged); @@ -61,6 +62,15 @@ void WlrLayershell::setupWindow() { this->updateAutoExclusion(); } +bool WlrLayershell::deleteOnInvisible() const { + // Qt windows behave weirdly when geometry is modified and setVisible(false) + // is subsequently called in the same frame. + // It will attach buffers to the wayland surface unconditionally before + // the surface recieves a configure event, causing a protocol error. + // To remedy this we forcibly disallow window reuse. + return true; +} + void WlrLayershell::setWidth(qint32 width) { this->mWidth = width; diff --git a/src/wayland/wlr_layershell.hpp b/src/wayland/wlr_layershell.hpp index b0ef383..b9e733f 100644 --- a/src/wayland/wlr_layershell.hpp +++ b/src/wayland/wlr_layershell.hpp @@ -61,8 +61,10 @@ class WlrLayershell: public ProxyWindowBase { public: explicit WlrLayershell(QObject* parent = nullptr); - QQuickWindow* createWindow(QObject* oldInstance) override; - void setupWindow() override; + QQuickWindow* retrieveWindow(QObject* oldInstance) override; + QQuickWindow* createQQuickWindow() override; + void connectWindow() override; + [[nodiscard]] bool deleteOnInvisible() const override; void setWidth(qint32 width) override; void setHeight(qint32 height) override; diff --git a/src/wayland/wlr_layershell/window.cpp b/src/wayland/wlr_layershell/window.cpp index b8dd360..5ef2825 100644 --- a/src/wayland/wlr_layershell/window.cpp +++ b/src/wayland/wlr_layershell/window.cpp @@ -78,7 +78,6 @@ bool LayershellWindowExtension::attach(QWindow* window) { waylandWindow->setShellIntegration(layershellIntegration); } - this->setParent(window); window->setProperty("layershell_ext", QVariant::fromValue(this)); return true; }