mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2025-11-04 19:04:56 +11:00
core: derive incubation controllers from tracked windows list
Replaces the attempts to track incubation controllers directly with a list of all known windows, then pulls the first usable incubation controller when an assignment is requested. This should finally fix incubation controller related use after free crashes.
This commit is contained in:
parent
59f5744f30
commit
e9a574d919
3 changed files with 27 additions and 103 deletions
|
|
@ -11,12 +11,12 @@
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qnamespace.h>
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlcontext.h>
|
#include <qqmlcontext.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qqmlerror.h>
|
#include <qqmlerror.h>
|
||||||
#include <qqmlincubator.h>
|
#include <qqmlincubator.h>
|
||||||
|
#include <qquickwindow.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include "iconimageprovider.hpp"
|
#include "iconimageprovider.hpp"
|
||||||
|
|
@ -242,90 +242,6 @@ void EngineGeneration::onDirectoryChanged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineGeneration::registerIncubationController(QQmlIncubationController* controller) {
|
|
||||||
// We only want controllers that we can swap out if destroyed.
|
|
||||||
// This happens if the window owning the active controller dies.
|
|
||||||
auto* obj = dynamic_cast<QObject*>(controller);
|
|
||||||
if (!obj) {
|
|
||||||
qCWarning(logIncubator) << "Could not register incubation controller as it is not a QObject"
|
|
||||||
<< controller;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(
|
|
||||||
obj,
|
|
||||||
&QObject::destroyed,
|
|
||||||
this,
|
|
||||||
&EngineGeneration::incubationControllerDestroyed,
|
|
||||||
Qt::UniqueConnection
|
|
||||||
);
|
|
||||||
|
|
||||||
this->incubationControllers.push_back(obj);
|
|
||||||
qCDebug(logIncubator) << "Registered incubation controller" << obj << "to generation" << this;
|
|
||||||
|
|
||||||
// This function can run during destruction.
|
|
||||||
if (this->engine == nullptr) return;
|
|
||||||
|
|
||||||
if (this->engine->incubationController() == &this->delayedIncubationController) {
|
|
||||||
this->assignIncubationController();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple controllers may be destroyed at once. Dynamic casts must be performed before working
|
|
||||||
// with any controllers. The QQmlIncubationController destructor will already have run by the
|
|
||||||
// point QObject::destroyed is called, so we can't cast to that.
|
|
||||||
void EngineGeneration::deregisterIncubationController(QQmlIncubationController* controller) {
|
|
||||||
auto* obj = dynamic_cast<QObject*>(controller);
|
|
||||||
if (!obj) {
|
|
||||||
qCCritical(logIncubator) << "Deregistering incubation controller which is not a QObject, "
|
|
||||||
"however only QObject controllers should be registered.";
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::disconnect(obj, nullptr, this, nullptr);
|
|
||||||
|
|
||||||
if (this->incubationControllers.removeOne(obj)) {
|
|
||||||
qCDebug(logIncubator) << "Deregistered incubation controller" << obj << "from" << this;
|
|
||||||
} else {
|
|
||||||
qCCritical(logIncubator) << "Failed to deregister incubation controller" << obj << "from"
|
|
||||||
<< this << "as it was not registered to begin with";
|
|
||||||
qCCritical(logIncubator) << "Current registered incuabation controllers"
|
|
||||||
<< this->incubationControllers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function can run during destruction.
|
|
||||||
if (this->engine == nullptr) return;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (this->incubationControllers.removeAll(sender) != 0) {
|
|
||||||
qCDebug(logIncubator) << "Destroyed incubation controller" << sender << "deregistered from"
|
|
||||||
<< this;
|
|
||||||
} else {
|
|
||||||
qCCritical(logIncubator) << "Destroyed incubation controller" << sender
|
|
||||||
<< "was not registered, but its destruction was observed by" << this;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function can run during destruction.
|
|
||||||
if (this->engine == nullptr) return;
|
|
||||||
|
|
||||||
if (dynamic_cast<QObject*>(this->engine->incubationController()) == sender) {
|
|
||||||
qCDebug(logIncubator
|
|
||||||
) << "Destroyed incubation controller was currently active, reassigning from pool";
|
|
||||||
this->assignIncubationController();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EngineGeneration::onEngineWarnings(const QList<QQmlError>& warnings) {
|
void EngineGeneration::onEngineWarnings(const QList<QQmlError>& warnings) {
|
||||||
for (const auto& error: warnings) {
|
for (const auto& error: warnings) {
|
||||||
const auto& url = error.url();
|
const auto& url = error.url();
|
||||||
|
|
@ -367,13 +283,27 @@ void EngineGeneration::exit(int code) {
|
||||||
this->destroy();
|
this->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineGeneration::assignIncubationController() {
|
void EngineGeneration::trackWindowIncubationController(QQuickWindow* window) {
|
||||||
QQmlIncubationController* controller = nullptr;
|
if (this->trackedWindows.contains(window)) return;
|
||||||
|
|
||||||
if (this->incubationControllersLocked || this->incubationControllers.isEmpty()) {
|
QObject::connect(window, &QObject::destroyed, this, &EngineGeneration::onTrackedWindowDestroyed);
|
||||||
controller = &this->delayedIncubationController;
|
this->trackedWindows.append(window);
|
||||||
} else {
|
this->assignIncubationController();
|
||||||
controller = dynamic_cast<QQmlIncubationController*>(this->incubationControllers.first());
|
}
|
||||||
|
|
||||||
|
void EngineGeneration::onTrackedWindowDestroyed(QObject* object) {
|
||||||
|
this->trackedWindows.removeAll(static_cast<QQuickWindow*>(object)); // NOLINT
|
||||||
|
this->assignIncubationController();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineGeneration::assignIncubationController() {
|
||||||
|
QQmlIncubationController* controller = &this->delayedIncubationController;
|
||||||
|
|
||||||
|
for (auto* window: this->trackedWindows) {
|
||||||
|
if (auto* wctl = window->incubationController()) {
|
||||||
|
controller = wctl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(logIncubator) << "Assigning incubation controller" << controller << "to generation"
|
qCDebug(logIncubator) << "Assigning incubation controller" << controller << "to generation"
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qqmlerror.h>
|
#include <qqmlerror.h>
|
||||||
#include <qqmlincubator.h>
|
#include <qqmlincubator.h>
|
||||||
|
#include <qquickwindow.h>
|
||||||
#include <qtclasshelpermacros.h>
|
#include <qtclasshelpermacros.h>
|
||||||
|
|
||||||
#include "incubator.hpp"
|
#include "incubator.hpp"
|
||||||
|
|
@ -40,8 +41,7 @@ public:
|
||||||
void setWatchingFiles(bool watching);
|
void setWatchingFiles(bool watching);
|
||||||
bool setExtraWatchedFiles(const QVector<QString>& files);
|
bool setExtraWatchedFiles(const QVector<QString>& files);
|
||||||
|
|
||||||
void registerIncubationController(QQmlIncubationController* controller);
|
void trackWindowIncubationController(QQuickWindow* window);
|
||||||
void deregisterIncubationController(QQmlIncubationController* controller);
|
|
||||||
|
|
||||||
// takes ownership
|
// takes ownership
|
||||||
void registerExtension(const void* key, EngineGenerationExt* extension);
|
void registerExtension(const void* key, EngineGenerationExt* extension);
|
||||||
|
|
@ -84,13 +84,13 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void onFileChanged(const QString& name);
|
void onFileChanged(const QString& name);
|
||||||
void onDirectoryChanged();
|
void onDirectoryChanged();
|
||||||
void incubationControllerDestroyed();
|
void onTrackedWindowDestroyed(QObject* object);
|
||||||
static void onEngineWarnings(const QList<QQmlError>& warnings);
|
static void onEngineWarnings(const QList<QQmlError>& warnings);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void postReload();
|
void postReload();
|
||||||
void assignIncubationController();
|
void assignIncubationController();
|
||||||
QVector<QObject*> incubationControllers;
|
QVector<QQuickWindow*> trackedWindows;
|
||||||
bool incubationControllersLocked = false;
|
bool incubationControllersLocked = false;
|
||||||
QHash<const void*, EngineGenerationExt*> extensions;
|
QHash<const void*, EngineGenerationExt*> extensions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,13 +153,7 @@ void ProxyWindowBase::createWindow() {
|
||||||
|
|
||||||
void ProxyWindowBase::deleteWindow(bool keepItemOwnership) {
|
void ProxyWindowBase::deleteWindow(bool keepItemOwnership) {
|
||||||
if (this->window != nullptr) emit this->windowDestroyed();
|
if (this->window != nullptr) emit this->windowDestroyed();
|
||||||
if (auto* window = this->disownWindow(keepItemOwnership)) {
|
if (auto* window = this->disownWindow(keepItemOwnership)) window->deleteLater();
|
||||||
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
|
|
||||||
generation->deregisterIncubationController(window->incubationController());
|
|
||||||
}
|
|
||||||
|
|
||||||
window->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxiedWindow* ProxyWindowBase::disownWindow(bool keepItemOwnership) {
|
ProxiedWindow* ProxyWindowBase::disownWindow(bool keepItemOwnership) {
|
||||||
|
|
@ -185,7 +179,7 @@ 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->trackWindowIncubationController(this->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->window->setProxy(this);
|
this->window->setProxy(this);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue