forked from quickshell/quickshell
core/window: backing windows can now be destroyed and recreated
This fixes a crash in layershells and the setVisible crash on nvidia.
This commit is contained in:
parent
b6dc6967a1
commit
3a0381dcbe
|
@ -5,14 +5,18 @@ set(QT_MIN_VERSION "6.6.0")
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
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(SOCKETS "Enable unix socket support" ON)
|
||||||
option(WAYLAND "Enable wayland support" ON)
|
option(WAYLAND "Enable wayland support" ON)
|
||||||
option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" 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)
|
option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON)
|
||||||
|
|
||||||
message(STATUS "Quickshell configuration")
|
message(STATUS "Quickshell configuration")
|
||||||
|
message(STATUS " NVIDIA workarounds: ${NVIDIA_COMPAT}")
|
||||||
message(STATUS " Build tests: ${BUILD_TESTING}")
|
message(STATUS " Build tests: ${BUILD_TESTING}")
|
||||||
message(STATUS " Sockets: ${SOCKETS}")
|
message(STATUS " Sockets: ${SOCKETS}")
|
||||||
message(STATUS " Wayland: ${WAYLAND}")
|
message(STATUS " Wayland: ${WAYLAND}")
|
||||||
|
@ -31,6 +35,15 @@ endif()
|
||||||
|
|
||||||
add_compile_options(-Wall -Wextra)
|
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
|
# nix workaround
|
||||||
if (CMAKE_EXPORT_COMPILE_COMMANDS)
|
if (CMAKE_EXPORT_COMPILE_COMMANDS)
|
||||||
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
|
@ -92,4 +105,8 @@ function (qs_pch target)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
if (NVIDIA_COMPAT)
|
||||||
|
add_compile_definitions(NVIDIA_COMPAT)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
12
README.md
12
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.<system>.default` to be added to
|
Quickshell's binary is available at `quickshell.packages.<system>.default` to be added to
|
||||||
lists such as `environment.systemPackages` or `home.packages`.
|
lists such as `environment.systemPackages` or `home.packages`.
|
||||||
|
|
||||||
|
`quickshell.packages.<system>.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.
|
Note: by default this package is built with clang as it is significantly faster.
|
||||||
|
|
||||||
## Manual
|
## Manual
|
||||||
|
@ -75,6 +78,15 @@ To make a release build of quickshell run:
|
||||||
$ just release
|
$ 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
|
If you have all the dependencies installed and they are in expected
|
||||||
locations this will build correctly.
|
locations this will build correctly.
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
then builtins.readFile ./.git/refs/heads/${builtins.elemAt matches 0}
|
then builtins.readFile ./.git/refs/heads/${builtins.elemAt matches 0}
|
||||||
else headContent)
|
else headContent)
|
||||||
else "unknown"),
|
else "unknown"),
|
||||||
|
|
||||||
debug ? false,
|
debug ? false,
|
||||||
enableWayland ? true,
|
enableWayland ? true,
|
||||||
|
nvidiaCompat ? false,
|
||||||
}: buildStdenv.mkDerivation {
|
}: buildStdenv.mkDerivation {
|
||||||
pname = "quickshell${lib.optionalString debug "-debug"}";
|
pname = "quickshell${lib.optionalString debug "-debug"}";
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
|
@ -56,7 +58,8 @@
|
||||||
|
|
||||||
cmakeFlags = [
|
cmakeFlags = [
|
||||||
"-DGIT_REVISION=${gitRev}"
|
"-DGIT_REVISION=${gitRev}"
|
||||||
] ++ lib.optional (!enableWayland) "-DWAYLAND=OFF";
|
] ++ lib.optional (!enableWayland) "-DWAYLAND=OFF"
|
||||||
|
++ lib.optional nvidiaCompat "-DNVIDIA_COMPAT=ON";
|
||||||
|
|
||||||
buildPhase = "ninjaBuildPhase";
|
buildPhase = "ninjaBuildPhase";
|
||||||
enableParallelBuilding = true;
|
enableParallelBuilding = true;
|
||||||
|
|
|
@ -9,8 +9,11 @@
|
||||||
(system: fn system nixpkgs.legacyPackages.${system});
|
(system: fn system nixpkgs.legacyPackages.${system});
|
||||||
in {
|
in {
|
||||||
packages = forEachSystem (system: pkgs: rec {
|
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;
|
default = quickshell;
|
||||||
|
nvidia = quickshell-nvidia;
|
||||||
});
|
});
|
||||||
|
|
||||||
devShells = forEachSystem (system: pkgs: rec {
|
devShells = forEachSystem (system: pkgs: rec {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{ pkgs ? import <nixpkgs> {}, ... }: pkgs.callPackage ./default.nix {}
|
|
|
@ -122,6 +122,32 @@ void EngineGeneration::registerIncubationController(QQmlIncubationController* co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EngineGeneration::deregisterIncubationController(QQmlIncubationController* controller) {
|
||||||
|
QObject* obj = nullptr;
|
||||||
|
this->incubationControllers.removeIf([&](QPair<QQmlIncubationController*, QObject*> 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() {
|
void EngineGeneration::incubationControllerDestroyed() {
|
||||||
auto* sender = this->sender();
|
auto* sender = this->sender();
|
||||||
QQmlIncubationController* controller = nullptr;
|
QQmlIncubationController* controller = nullptr;
|
||||||
|
@ -150,8 +176,9 @@ void EngineGeneration::incubationControllerDestroyed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineGeneration::assignIncubationController() {
|
void EngineGeneration::assignIncubationController() {
|
||||||
auto* controller = this->incubationControllers.first().first;
|
QQmlIncubationController* controller = nullptr;
|
||||||
if (controller == nullptr) controller = &this->delayedIncubationController;
|
if (this->incubationControllers.isEmpty()) controller = &this->delayedIncubationController;
|
||||||
|
else controller = this->incubationControllers.first().first;
|
||||||
|
|
||||||
qCDebug(logIncubator) << "Assigning incubation controller to engine:" << controller
|
qCDebug(logIncubator) << "Assigning incubation controller to engine:" << controller
|
||||||
<< "fallback:" << (controller == &this->delayedIncubationController);
|
<< "fallback:" << (controller == &this->delayedIncubationController);
|
||||||
|
@ -162,9 +189,14 @@ void EngineGeneration::assignIncubationController() {
|
||||||
EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {
|
EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {
|
||||||
while (object != nullptr) {
|
while (object != nullptr) {
|
||||||
auto* context = QQmlEngine::contextForObject(object);
|
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;
|
return nullptr;
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
void setWatchingFiles(bool watching);
|
void setWatchingFiles(bool watching);
|
||||||
|
|
||||||
void registerIncubationController(QQmlIncubationController* controller);
|
void registerIncubationController(QQmlIncubationController* controller);
|
||||||
|
void deregisterIncubationController(QQmlIncubationController* controller);
|
||||||
|
|
||||||
static EngineGeneration* findObjectGeneration(QObject* object);
|
static EngineGeneration* findObjectGeneration(QObject* object);
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,19 @@ ProxyPopupWindow::ProxyPopupWindow(QObject* parent): ProxyWindowBase(parent) {
|
||||||
this->mVisible = false;
|
this->mVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyPopupWindow::setupWindow() {
|
void ProxyPopupWindow::completeWindow() {
|
||||||
this->ProxyWindowBase::setupWindow();
|
this->ProxyWindowBase::completeWindow();
|
||||||
|
|
||||||
this->window->setFlag(Qt::ToolTip);
|
this->window->setFlag(Qt::ToolTip);
|
||||||
this->updateTransientParent();
|
this->updateTransientParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyPopupWindow::postCompleteWindow() {}
|
||||||
|
|
||||||
qint32 ProxyPopupWindow::x() const {
|
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) {
|
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::xChanged, this, &ProxyPopupWindow::updateX);
|
||||||
QObject::connect(this->mParentProxyWindow, &ProxyWindowBase::yChanged, this, &ProxyPopupWindow::updateY);
|
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
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,18 +72,19 @@ void ProxyPopupWindow::setParentWindow(QObject* parent) {
|
||||||
QObject* ProxyPopupWindow::parentWindow() const { return this->mParentWindow; }
|
QObject* ProxyPopupWindow::parentWindow() const { return this->mParentWindow; }
|
||||||
|
|
||||||
void ProxyPopupWindow::updateTransientParent() {
|
void ProxyPopupWindow::updateTransientParent() {
|
||||||
if (this->window == nullptr) return;
|
|
||||||
this->updateX();
|
this->updateX();
|
||||||
this->updateY();
|
this->updateY();
|
||||||
|
|
||||||
this->window->setTransientParent(
|
if (this->window != nullptr) {
|
||||||
this->mParentProxyWindow == nullptr ? nullptr : this->mParentProxyWindow->backingWindow()
|
this->window->setTransientParent(
|
||||||
);
|
this->mParentProxyWindow == nullptr ? nullptr : this->mParentProxyWindow->backingWindow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this->updateVisible();
|
this->updateVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyPopupWindow::onParentConnected() { this->updateTransientParent(); }
|
void ProxyPopupWindow::onParentUpdated() { this->updateTransientParent(); }
|
||||||
|
|
||||||
void ProxyPopupWindow::onParentDestroyed() {
|
void ProxyPopupWindow::onParentDestroyed() {
|
||||||
this->mParentWindow = nullptr;
|
this->mParentWindow = nullptr;
|
||||||
|
@ -99,7 +104,8 @@ void ProxyPopupWindow::setVisible(bool visible) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyPopupWindow::updateVisible() {
|
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()) {
|
if (target && this->window != nullptr && !this->window->isVisible()) {
|
||||||
this->updateX(); // QTBUG-121550
|
this->updateX(); // QTBUG-121550
|
||||||
|
@ -127,13 +133,12 @@ qint32 ProxyPopupWindow::relativeY() const { return this->mRelativeY; }
|
||||||
void ProxyPopupWindow::updateX() {
|
void ProxyPopupWindow::updateX() {
|
||||||
if (this->mParentWindow == nullptr || this->window == nullptr) return;
|
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->x() - 1; // QTBUG-121550
|
||||||
auto target = this->mParentProxyWindow->backingWindow()->x() + this->relativeX();
|
|
||||||
|
|
||||||
auto reshow = this->window->isVisible() && (this->window->x() != target && this->x() != target);
|
auto reshow = this->isVisibleDirect() && (this->window->x() != target && this->x() != target);
|
||||||
if (reshow) this->window->setVisible(false);
|
if (reshow) this->setVisibleDirect(false);
|
||||||
this->window->setX(target - 1); // -1 due to QTBUG-121550
|
if (this->window != nullptr) this->window->setX(target);
|
||||||
if (reshow && this->wantsVisible) this->window->setVisible(true);
|
if (reshow && this->wantsVisible) this->setVisibleDirect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyPopupWindow::updateY() {
|
void ProxyPopupWindow::updateY() {
|
||||||
|
@ -141,11 +146,11 @@ void ProxyPopupWindow::updateY() {
|
||||||
|
|
||||||
auto target = this->mParentProxyWindow->y() + this->relativeY();
|
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) {
|
if (reshow) {
|
||||||
this->window->setVisible(false);
|
this->setVisibleDirect(false);
|
||||||
this->updateX(); // QTBUG-121550
|
this->updateX(); // QTBUG-121550
|
||||||
}
|
}
|
||||||
this->window->setY(target);
|
if (this->window != nullptr) this->window->setY(target);
|
||||||
if (reshow && this->wantsVisible) this->window->setVisible(true);
|
if (reshow && this->wantsVisible) this->setVisibleDirect(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,8 @@ class ProxyPopupWindow: public ProxyWindowBase {
|
||||||
public:
|
public:
|
||||||
explicit ProxyPopupWindow(QObject* parent = nullptr);
|
explicit ProxyPopupWindow(QObject* parent = nullptr);
|
||||||
|
|
||||||
void setupWindow() override;
|
void completeWindow() override;
|
||||||
|
void postCompleteWindow() override;
|
||||||
|
|
||||||
void setScreen(QuickshellScreenInfo* screen) override;
|
void setScreen(QuickshellScreenInfo* screen) override;
|
||||||
void setVisible(bool visible) override;
|
void setVisible(bool visible) override;
|
||||||
|
@ -84,7 +85,7 @@ signals:
|
||||||
void relativeYChanged();
|
void relativeYChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onParentConnected();
|
void onParentUpdated();
|
||||||
void onParentDestroyed();
|
void onParentDestroyed();
|
||||||
void updateX();
|
void updateX();
|
||||||
void updateY();
|
void updateY();
|
||||||
|
|
|
@ -23,51 +23,81 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
|
||||||
QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
|
QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
|
||||||
this->mContentItem->setParent(this);
|
this->mContentItem->setParent(this);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
|
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
|
||||||
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onHeightChanged);
|
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() {
|
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(); }
|
||||||
if (this->window != nullptr) {
|
|
||||||
this->window->deleteLater();
|
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) {
|
QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||||
this->window = this->createWindow(oldInstance);
|
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<ProxyWindowBase*>(oldInstance);
|
||||||
|
return old == nullptr ? nullptr : old->disownWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::connectWindow() {
|
||||||
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
|
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
|
||||||
// All windows have effectively the same incubation controller so it dosen't matter
|
// 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.
|
// which window it belongs to. We do want to replace the delay one though.
|
||||||
generation->registerIncubationController(this->window->incubationController());
|
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<ProxyWindowBase*>(oldInstance);
|
|
||||||
|
|
||||||
if (old == nullptr || old->window == nullptr) {
|
|
||||||
return new QQuickWindow();
|
|
||||||
} else {
|
|
||||||
return old->disownWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyWindowBase::setupWindow() {
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
|
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
|
||||||
QObject::connect(this->window, &QWindow::xChanged, this, &ProxyWindowBase::xChanged);
|
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::heightChanged, this, &ProxyWindowBase::heightChanged);
|
||||||
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged);
|
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged);
|
||||||
QObject::connect(this->window, &QQuickWindow::colorChanged, this, &ProxyWindowBase::colorChanged);
|
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
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::completeWindow() {
|
||||||
if (this->mScreen != nullptr && this->window->screen() != this->mScreen) {
|
if (this->mScreen != nullptr && this->window->screen() != this->mScreen) {
|
||||||
if (this->window->isVisible()) this->window->setVisible(false);
|
if (this->window->isVisible()) this->window->setVisible(false);
|
||||||
this->window->setScreen(this->mScreen);
|
this->window->setScreen(this->mScreen);
|
||||||
|
@ -95,16 +123,24 @@ void ProxyWindowBase::setupWindow() {
|
||||||
// notify initial x and y positions
|
// notify initial x and y positions
|
||||||
emit this->xChanged();
|
emit this->xChanged();
|
||||||
emit this->yChanged();
|
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() {
|
bool ProxyWindowBase::deleteOnInvisible() const {
|
||||||
QObject::disconnect(this->window, nullptr, this, nullptr);
|
#ifdef NVIDIA_COMPAT
|
||||||
|
// Nvidia drivers and Qt do not play nice when hiding and showing a window
|
||||||
this->mContentItem->setParentItem(nullptr);
|
// so for nvidia compatibility we can never reuse windows if they have been
|
||||||
|
// hidden.
|
||||||
auto* window = this->window;
|
return true;
|
||||||
this->window = nullptr;
|
#else
|
||||||
return window;
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickWindow* ProxyWindowBase::backingWindow() const { return this->window; }
|
QQuickWindow* ProxyWindowBase::backingWindow() const { return this->window; }
|
||||||
|
@ -112,14 +148,38 @@ QQuickItem* ProxyWindowBase::contentItem() const { return this->mContentItem; }
|
||||||
|
|
||||||
bool ProxyWindowBase::isVisible() const {
|
bool ProxyWindowBase::isVisible() const {
|
||||||
if (this->window == nullptr) return this->mVisible;
|
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();
|
else return this->window->isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::setVisible(bool visible) {
|
void ProxyWindowBase::setVisible(bool visible) {
|
||||||
if (this->window == nullptr) {
|
this->mVisible = visible;
|
||||||
this->mVisible = visible;
|
this->setVisibleDirect(visible);
|
||||||
emit this->visibleChanged();
|
}
|
||||||
} else this->window->setVisible(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 {
|
qint32 ProxyWindowBase::x() const {
|
||||||
|
@ -138,8 +198,8 @@ qint32 ProxyWindowBase::width() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::setWidth(qint32 width) {
|
void ProxyWindowBase::setWidth(qint32 width) {
|
||||||
|
this->mWidth = width;
|
||||||
if (this->window == nullptr) {
|
if (this->window == nullptr) {
|
||||||
this->mWidth = width;
|
|
||||||
emit this->widthChanged();
|
emit this->widthChanged();
|
||||||
} else this->window->setWidth(width);
|
} else this->window->setWidth(width);
|
||||||
}
|
}
|
||||||
|
@ -150,8 +210,8 @@ qint32 ProxyWindowBase::height() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::setHeight(qint32 height) {
|
void ProxyWindowBase::setHeight(qint32 height) {
|
||||||
|
this->mHeight = height;
|
||||||
if (this->window == nullptr) {
|
if (this->window == nullptr) {
|
||||||
this->mHeight = height;
|
|
||||||
emit this->heightChanged();
|
emit this->heightChanged();
|
||||||
} else this->window->setHeight(height);
|
} else this->window->setHeight(height);
|
||||||
}
|
}
|
||||||
|
@ -170,10 +230,10 @@ void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) {
|
||||||
this->mScreen = qscreen;
|
this->mScreen = qscreen;
|
||||||
emit this->screenChanged();
|
emit this->screenChanged();
|
||||||
} else {
|
} else {
|
||||||
auto reshow = this->window->isVisible();
|
auto reshow = this->isVisibleDirect();
|
||||||
if (reshow) this->window->setVisible(false);
|
if (reshow) this->setVisibleDirect(false);
|
||||||
this->window->setScreen(qscreen);
|
if (this->window != nullptr) this->window->setScreen(qscreen);
|
||||||
if (reshow) this->window->setVisible(true);
|
if (reshow) this->setVisibleDirect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "qmlglobal.hpp"
|
||||||
#include "qmlscreen.hpp"
|
#include "qmlscreen.hpp"
|
||||||
#include "region.hpp"
|
#include "region.hpp"
|
||||||
#include "reload.hpp"
|
#include "reload.hpp"
|
||||||
|
@ -54,18 +55,26 @@ public:
|
||||||
void operator=(ProxyWindowBase&&) = delete;
|
void operator=(ProxyWindowBase&&) = delete;
|
||||||
|
|
||||||
void onReload(QObject* oldInstance) override;
|
void onReload(QObject* oldInstance) override;
|
||||||
|
void createWindow();
|
||||||
virtual QQuickWindow* createWindow(QObject* oldInstance);
|
void deleteWindow();
|
||||||
virtual void setupWindow();
|
|
||||||
|
|
||||||
// Disown the backing window and delete all its children.
|
// Disown the backing window and delete all its children.
|
||||||
virtual QQuickWindow* disownWindow();
|
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]] QQuickWindow* backingWindow() const;
|
||||||
[[nodiscard]] QQuickItem* contentItem() const;
|
[[nodiscard]] QQuickItem* contentItem() const;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool isVisible() const;
|
[[nodiscard]] virtual bool isVisible() const;
|
||||||
|
[[nodiscard]] virtual bool isVisibleDirect() const;
|
||||||
virtual void setVisible(bool visible);
|
virtual void setVisible(bool visible);
|
||||||
|
virtual void setVisibleDirect(bool visible);
|
||||||
|
|
||||||
[[nodiscard]] virtual qint32 x() const;
|
[[nodiscard]] virtual qint32 x() const;
|
||||||
[[nodiscard]] virtual qint32 y() const;
|
[[nodiscard]] virtual qint32 y() const;
|
||||||
|
@ -90,10 +99,12 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void windowConnected();
|
void windowConnected();
|
||||||
void visibleChanged();
|
void visibleChanged();
|
||||||
|
void backerVisibilityChanged();
|
||||||
void xChanged();
|
void xChanged();
|
||||||
void yChanged();
|
void yChanged();
|
||||||
void widthChanged();
|
void widthChanged();
|
||||||
void heightChanged();
|
void heightChanged();
|
||||||
|
void windowTransformChanged();
|
||||||
void screenChanged();
|
void screenChanged();
|
||||||
void colorChanged();
|
void colorChanged();
|
||||||
void maskChanged();
|
void maskChanged();
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
function (qs_test name)
|
function (qs_test name)
|
||||||
add_executable(${name} ${ARGN})
|
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 $<TARGET_FILE:${name}>)
|
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
qs_test(popupwindow
|
qs_test(popupwindow popupwindow.cpp)
|
||||||
popupwindow.cpp
|
|
||||||
../popupwindow.cpp
|
|
||||||
../proxywindow.cpp
|
|
||||||
../qmlscreen.cpp
|
|
||||||
../region.cpp
|
|
||||||
../reload.cpp
|
|
||||||
../windowinterface.cpp
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include "popupwindow.hpp"
|
#include "popupwindow.hpp"
|
||||||
|
|
||||||
#include <qlogging.h>
|
|
||||||
#include <qquickwindow.h>
|
#include <qquickwindow.h>
|
||||||
#include <qsignalspy.h>
|
#include <qsignalspy.h>
|
||||||
#include <qtest.h>
|
#include <qtest.h>
|
||||||
|
@ -52,7 +51,6 @@ void TestPopupWindow::reloadReparent() { // NOLINT
|
||||||
|
|
||||||
auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged);
|
auto spy = QSignalSpy(oldWindow, &QWindow::visibleChanged);
|
||||||
|
|
||||||
qDebug() << "reload";
|
|
||||||
newParent.onReload(&parent);
|
newParent.onReload(&parent);
|
||||||
newPopup.onReload(&popup);
|
newPopup.onReload(&popup);
|
||||||
|
|
||||||
|
|
|
@ -18,33 +18,34 @@ WlrLayershell::WlrLayershell(QObject* parent)
|
||||||
: ProxyWindowBase(parent)
|
: ProxyWindowBase(parent)
|
||||||
, ext(new LayershellWindowExtension(this)) {}
|
, ext(new LayershellWindowExtension(this)) {}
|
||||||
|
|
||||||
QQuickWindow* WlrLayershell::createWindow(QObject* oldInstance) {
|
QQuickWindow* WlrLayershell::retrieveWindow(QObject* oldInstance) {
|
||||||
auto* old = qobject_cast<WlrLayershell*>(oldInstance);
|
auto* old = qobject_cast<WlrLayershell*>(oldInstance);
|
||||||
QQuickWindow* window = nullptr;
|
auto* window = old == nullptr ? nullptr : old->disownWindow();
|
||||||
|
|
||||||
if (old == nullptr || old->window == nullptr) {
|
|
||||||
window = new QQuickWindow();
|
|
||||||
} else {
|
|
||||||
window = old->disownWindow();
|
|
||||||
|
|
||||||
|
if (window != nullptr) {
|
||||||
if (this->ext->attach(window)) {
|
if (this->ext->attach(window)) {
|
||||||
return window;
|
return window;
|
||||||
} else {
|
} else {
|
||||||
window->deleteLater();
|
window->deleteLater();
|
||||||
window = new QQuickWindow();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this->createQQuickWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickWindow* WlrLayershell::createQQuickWindow() {
|
||||||
|
auto* window = new QQuickWindow();
|
||||||
|
|
||||||
if (!this->ext->attach(window)) {
|
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.";
|
"behave correctly.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WlrLayershell::setupWindow() {
|
void WlrLayershell::connectWindow() {
|
||||||
this->ProxyWindowBase::setupWindow();
|
this->ProxyWindowBase::connectWindow();
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(this->ext, &LayershellWindowExtension::layerChanged, this, &WlrLayershell::layerChanged);
|
QObject::connect(this->ext, &LayershellWindowExtension::layerChanged, this, &WlrLayershell::layerChanged);
|
||||||
|
@ -61,6 +62,15 @@ void WlrLayershell::setupWindow() {
|
||||||
this->updateAutoExclusion();
|
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) {
|
void WlrLayershell::setWidth(qint32 width) {
|
||||||
this->mWidth = width;
|
this->mWidth = width;
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,10 @@ class WlrLayershell: public ProxyWindowBase {
|
||||||
public:
|
public:
|
||||||
explicit WlrLayershell(QObject* parent = nullptr);
|
explicit WlrLayershell(QObject* parent = nullptr);
|
||||||
|
|
||||||
QQuickWindow* createWindow(QObject* oldInstance) override;
|
QQuickWindow* retrieveWindow(QObject* oldInstance) override;
|
||||||
void setupWindow() override;
|
QQuickWindow* createQQuickWindow() override;
|
||||||
|
void connectWindow() override;
|
||||||
|
[[nodiscard]] bool deleteOnInvisible() const override;
|
||||||
|
|
||||||
void setWidth(qint32 width) override;
|
void setWidth(qint32 width) override;
|
||||||
void setHeight(qint32 height) override;
|
void setHeight(qint32 height) override;
|
||||||
|
|
|
@ -78,7 +78,6 @@ bool LayershellWindowExtension::attach(QWindow* window) {
|
||||||
waylandWindow->setShellIntegration(layershellIntegration);
|
waylandWindow->setShellIntegration(layershellIntegration);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->setParent(window);
|
|
||||||
window->setProperty("layershell_ext", QVariant::fromValue(this));
|
window->setProperty("layershell_ext", QVariant::fromValue(this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue