forked from quickshell/quickshell
		
	reload: encapsulate each engine generation more
This commit is contained in:
		
							parent
							
								
									211f454de9
								
							
						
					
					
						commit
						1687ff3614
					
				
					 7 changed files with 125 additions and 79 deletions
				
			
		| 
						 | 
					@ -17,6 +17,7 @@ qt_add_library(quickshell-core STATIC
 | 
				
			||||||
	panelinterface.cpp
 | 
						panelinterface.cpp
 | 
				
			||||||
	popupwindow.cpp
 | 
						popupwindow.cpp
 | 
				
			||||||
	singleton.cpp
 | 
						singleton.cpp
 | 
				
			||||||
 | 
						generation.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
					set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/core/generation.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/core/generation.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					#include "generation.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qcoreapplication.h>
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qqmlcontext.h>
 | 
				
			||||||
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
 | 
					#include <qtimer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "plugin.hpp"
 | 
				
			||||||
 | 
					#include "reload.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static QHash<QQmlEngine*, EngineGeneration*> g_generations; // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EngineGeneration::EngineGeneration() { g_generations.insert(&this->engine, this); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EngineGeneration::~EngineGeneration() {
 | 
				
			||||||
 | 
						g_generations.remove(&this->engine);
 | 
				
			||||||
 | 
						if (this->root != nullptr) this->root->deleteLater();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EngineGeneration::onReload(EngineGeneration* old) {
 | 
				
			||||||
 | 
						auto* app = QCoreApplication::instance();
 | 
				
			||||||
 | 
						QObject::connect(&this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit);
 | 
				
			||||||
 | 
						QObject::connect(&this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->root->onReload(old == nullptr ? nullptr : old->root);
 | 
				
			||||||
 | 
						this->singletonRegistry.onReload(old == nullptr ? nullptr : &old->singletonRegistry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete old;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old != nullptr) {
 | 
				
			||||||
 | 
							QTimer::singleShot(0, [this]() {
 | 
				
			||||||
 | 
								QuickshellPlugin::runOnReload();
 | 
				
			||||||
 | 
								PostReloadHook::postReloadTree(this->root);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							QuickshellPlugin::runOnReload();
 | 
				
			||||||
 | 
							PostReloadHook::postReloadTree(this->root);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {
 | 
				
			||||||
 | 
						while (object != nullptr) {
 | 
				
			||||||
 | 
							auto* context = QQmlEngine::contextForObject(object);
 | 
				
			||||||
 | 
							if (auto* generation = g_generations.value(context->engine())) {
 | 
				
			||||||
 | 
								return generation;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/core/generation.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/core/generation.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qtclasshelpermacros.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "shell.hpp"
 | 
				
			||||||
 | 
					#include "singleton.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EngineGeneration {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit EngineGeneration();
 | 
				
			||||||
 | 
						~EngineGeneration();
 | 
				
			||||||
 | 
						Q_DISABLE_COPY_MOVE(EngineGeneration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// assumes root has been initialized, consumes old generation
 | 
				
			||||||
 | 
						void onReload(EngineGeneration* old);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static EngineGeneration* findObjectGeneration(QObject* object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QQmlEngine engine;
 | 
				
			||||||
 | 
						ShellRoot* root = nullptr;
 | 
				
			||||||
 | 
						SingletonRegistry singletonRegistry;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -2,21 +2,17 @@
 | 
				
			||||||
#include <cstdlib>
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <qcoreapplication.h>
 | 
					 | 
				
			||||||
#include <qdir.h>
 | 
					#include <qdir.h>
 | 
				
			||||||
#include <qfileinfo.h>
 | 
					#include <qfileinfo.h>
 | 
				
			||||||
#include <qlogging.h>
 | 
					#include <qlogging.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qqmlcomponent.h>
 | 
					#include <qqmlcomponent.h>
 | 
				
			||||||
#include <qqmlengine.h>
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
#include <qtimer.h>
 | 
					 | 
				
			||||||
#include <qurl.h>
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "plugin.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "qmlglobal.hpp"
 | 
					#include "qmlglobal.hpp"
 | 
				
			||||||
#include "reload.hpp"
 | 
					 | 
				
			||||||
#include "shell.hpp"
 | 
					#include "shell.hpp"
 | 
				
			||||||
#include "singleton.hpp"
 | 
					 | 
				
			||||||
#include "watcher.hpp"
 | 
					#include "watcher.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RootWrapper::RootWrapper(QString rootPath)
 | 
					RootWrapper::RootWrapper(QString rootPath)
 | 
				
			||||||
| 
						 | 
					@ -29,7 +25,7 @@ RootWrapper::RootWrapper(QString rootPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->reloadGraph(true);
 | 
						this->reloadGraph(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (this->root == nullptr) {
 | 
						if (this->generation == nullptr) {
 | 
				
			||||||
		qCritical() << "could not create scene graph, exiting";
 | 
							qCritical() << "could not create scene graph, exiting";
 | 
				
			||||||
		exit(-1); // NOLINT
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -37,31 +33,32 @@ RootWrapper::RootWrapper(QString rootPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RootWrapper::~RootWrapper() {
 | 
					RootWrapper::~RootWrapper() {
 | 
				
			||||||
	// event loop may no longer be running so deleteLater is not an option
 | 
						// event loop may no longer be running so deleteLater is not an option
 | 
				
			||||||
	delete this->root;
 | 
						if (this->generation != nullptr) {
 | 
				
			||||||
 | 
							delete this->generation->root;
 | 
				
			||||||
 | 
							this->generation->root = nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete this->generation;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RootWrapper::reloadGraph(bool hard) {
 | 
					void RootWrapper::reloadGraph(bool hard) {
 | 
				
			||||||
	auto* oldEngine = this->engine;
 | 
						auto* generation = new EngineGeneration();
 | 
				
			||||||
	this->engine = new QQmlEngine(this);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto* app = QCoreApplication::instance();
 | 
						// todo: move into EngineGeneration
 | 
				
			||||||
	QObject::connect(this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit);
 | 
						if (this->generation != nullptr) {
 | 
				
			||||||
	QObject::connect(this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (this->root != nullptr) {
 | 
					 | 
				
			||||||
		QuickshellSettings::reset();
 | 
							QuickshellSettings::reset();
 | 
				
			||||||
		SingletonRegistry::instance()->flip();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QDir::setCurrent(this->originalWorkingDirectory);
 | 
						QDir::setCurrent(this->originalWorkingDirectory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto component = QQmlComponent(this->engine, QUrl::fromLocalFile(this->rootPath));
 | 
						auto component = QQmlComponent(&generation->engine, QUrl::fromLocalFile(this->rootPath));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto* obj = component.beginCreate(this->engine->rootContext());
 | 
						auto* obj = component.beginCreate(generation->engine.rootContext());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (obj == nullptr) {
 | 
						if (obj == nullptr) {
 | 
				
			||||||
		qWarning() << component.errorString().toStdString().c_str();
 | 
							qWarning() << component.errorString().toStdString().c_str();
 | 
				
			||||||
		qWarning() << "failed to create root component";
 | 
							qWarning() << "failed to create root component";
 | 
				
			||||||
 | 
							delete generation;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,31 +66,19 @@ void RootWrapper::reloadGraph(bool hard) {
 | 
				
			||||||
	if (newRoot == nullptr) {
 | 
						if (newRoot == nullptr) {
 | 
				
			||||||
		qWarning() << "root component was not a Quickshell.ShellRoot";
 | 
							qWarning() << "root component was not a Quickshell.ShellRoot";
 | 
				
			||||||
		delete obj;
 | 
							delete obj;
 | 
				
			||||||
 | 
							delete generation;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						generation->root = newRoot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	component.completeCreate();
 | 
						component.completeCreate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto* oldRoot = this->root;
 | 
						generation->onReload(hard ? nullptr : this->generation);
 | 
				
			||||||
	this->root = newRoot;
 | 
						if (hard) delete this->generation;
 | 
				
			||||||
 | 
						this->generation = generation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->root->onReload(hard ? nullptr : oldRoot);
 | 
						qInfo() << "Configuration Loaded";
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (oldRoot != nullptr) {
 | 
					 | 
				
			||||||
		oldRoot->deleteLater();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		QTimer::singleShot(0, [this, newRoot]() {
 | 
					 | 
				
			||||||
			if (this->root == newRoot) {
 | 
					 | 
				
			||||||
				QuickshellPlugin::runOnReload();
 | 
					 | 
				
			||||||
				PostReloadHook::postReloadTree(this->root);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		PostReloadHook::postReloadTree(newRoot);
 | 
					 | 
				
			||||||
		QuickshellPlugin::runOnReload();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	delete oldEngine;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->onWatchFilesChanged();
 | 
						this->onWatchFilesChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
#include <qurl.h>
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "shell.hpp"
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "watcher.hpp"
 | 
					#include "watcher.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RootWrapper: public QObject {
 | 
					class RootWrapper: public QObject {
 | 
				
			||||||
| 
						 | 
					@ -25,8 +25,7 @@ private slots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	QString rootPath;
 | 
						QString rootPath;
 | 
				
			||||||
	QQmlEngine* engine = nullptr;
 | 
						EngineGeneration* generation = nullptr;
 | 
				
			||||||
	ShellRoot* root = nullptr;
 | 
					 | 
				
			||||||
	FiletreeWatcher* configWatcher = nullptr;
 | 
						FiletreeWatcher* configWatcher = nullptr;
 | 
				
			||||||
	QString originalWorkingDirectory;
 | 
						QString originalWorkingDirectory;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,58 +7,48 @@
 | 
				
			||||||
#include <qqmlengine.h>
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
#include <qurl.h>
 | 
					#include <qurl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "generation.hpp"
 | 
				
			||||||
#include "reload.hpp"
 | 
					#include "reload.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Singleton::componentComplete() {
 | 
					void Singleton::componentComplete() {
 | 
				
			||||||
	auto* context = QQmlEngine::contextForObject(this);
 | 
						auto* context = QQmlEngine::contextForObject(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (context == nullptr) {
 | 
						if (context == nullptr) {
 | 
				
			||||||
		qWarning() << "not registering singleton not created in the qml context:" << this;
 | 
							qWarning() << "Not registering singleton not created in the qml context:" << this;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto url = context->baseUrl();
 | 
						auto url = context->baseUrl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (this->parent() != nullptr || context->contextObject() != this) {
 | 
						if (this->parent() != nullptr || context->contextObject() != this) {
 | 
				
			||||||
		qWarning() << "tried to register singleton" << this
 | 
							qWarning() << "Tried to register singleton" << this
 | 
				
			||||||
		           << "which is not the root component of its file" << url;
 | 
							           << "which is not the root component of its file" << url;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SingletonRegistry::instance()->install(url, this);
 | 
						auto* generation = EngineGeneration::findObjectGeneration(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (generation == nullptr) {
 | 
				
			||||||
 | 
							qWarning() << "Tried to register singleton" << this
 | 
				
			||||||
 | 
							           << "which has no associated engine generation" << url;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						generation->singletonRegistry.registerSingleton(url, this);
 | 
				
			||||||
	this->ReloadPropagator::componentComplete();
 | 
						this->ReloadPropagator::componentComplete();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SingletonRegistry::~SingletonRegistry() {
 | 
					void SingletonRegistry::registerSingleton(const QUrl& url, Singleton* singleton) {
 | 
				
			||||||
	delete this->previousRegistry;
 | 
						if (this->registry.contains(url)) {
 | 
				
			||||||
	delete this->currentRegistry;
 | 
							qWarning() << "Tried to register singleton twice for the same file" << url;
 | 
				
			||||||
}
 | 
							return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
void SingletonRegistry::install(const QUrl& url, Singleton* singleton) {
 | 
					 | 
				
			||||||
	QObject* old = nullptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (this->previousRegistry != nullptr) {
 | 
					 | 
				
			||||||
		old = this->previousRegistry->value(url);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (this->currentRegistry == nullptr) {
 | 
						this->registry.insert(url, singleton);
 | 
				
			||||||
		this->currentRegistry = new QMap<QUrl, QObject*>();
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SingletonRegistry::onReload(SingletonRegistry* old) {
 | 
				
			||||||
 | 
						for (auto [url, singleton]: this->registry.asKeyValueRange()) {
 | 
				
			||||||
 | 
							singleton->onReload(old == nullptr ? nullptr : old->registry.value(url));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	this->currentRegistry->insert(url, singleton);
 | 
					 | 
				
			||||||
	singleton->onReload(old);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SingletonRegistry::flip() {
 | 
					 | 
				
			||||||
	delete this->previousRegistry;
 | 
					 | 
				
			||||||
	this->previousRegistry = this->currentRegistry;
 | 
					 | 
				
			||||||
	this->currentRegistry = nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SingletonRegistry* SingletonRegistry::instance() {
 | 
					 | 
				
			||||||
	static SingletonRegistry* instance = nullptr; // NOLINT
 | 
					 | 
				
			||||||
	if (instance == nullptr) {
 | 
					 | 
				
			||||||
		instance = new SingletonRegistry();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return instance;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,15 +21,10 @@ public:
 | 
				
			||||||
class SingletonRegistry {
 | 
					class SingletonRegistry {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	SingletonRegistry() = default;
 | 
						SingletonRegistry() = default;
 | 
				
			||||||
	~SingletonRegistry();
 | 
					 | 
				
			||||||
	Q_DISABLE_COPY_MOVE(SingletonRegistry);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void install(const QUrl& url, Singleton* singleton);
 | 
						void registerSingleton(const QUrl& url, Singleton* singleton);
 | 
				
			||||||
	void flip();
 | 
						void onReload(SingletonRegistry* old);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	static SingletonRegistry* instance();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	QMap<QUrl, QObject*>* previousRegistry = nullptr;
 | 
						QMap<QUrl, Singleton*> registry;
 | 
				
			||||||
	QMap<QUrl, QObject*>* currentRegistry = nullptr;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue