mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2026-02-23 03:33:57 +11:00
core: switch to custom incubation controller
Some checks failed
Build / Nix (push) Has been cancelled
Build / Nix-1 (push) Has been cancelled
Build / Nix-2 (push) Has been cancelled
Build / Nix-3 (push) Has been cancelled
Build / Nix-4 (push) Has been cancelled
Build / Nix-5 (push) Has been cancelled
Build / Nix-6 (push) Has been cancelled
Build / Nix-7 (push) Has been cancelled
Build / Nix-8 (push) Has been cancelled
Build / Nix-9 (push) Has been cancelled
Build / Nix-10 (push) Has been cancelled
Build / Nix-11 (push) Has been cancelled
Build / Nix-12 (push) Has been cancelled
Build / Nix-13 (push) Has been cancelled
Build / Nix-14 (push) Has been cancelled
Build / Nix-15 (push) Has been cancelled
Build / Nix-16 (push) Has been cancelled
Build / Nix-17 (push) Has been cancelled
Build / Nix-18 (push) Has been cancelled
Build / Nix-19 (push) Has been cancelled
Build / Nix-20 (push) Has been cancelled
Build / Nix-21 (push) Has been cancelled
Build / Nix-22 (push) Has been cancelled
Build / Nix-23 (push) Has been cancelled
Build / Nix-24 (push) Has been cancelled
Build / Nix-25 (push) Has been cancelled
Build / Nix-26 (push) Has been cancelled
Build / Nix-27 (push) Has been cancelled
Build / Nix-28 (push) Has been cancelled
Build / Nix-29 (push) Has been cancelled
Build / Nix-30 (push) Has been cancelled
Build / Nix-31 (push) Has been cancelled
Build / Archlinux (push) Has been cancelled
Lint / Lint (push) Has been cancelled
Some checks failed
Build / Nix (push) Has been cancelled
Build / Nix-1 (push) Has been cancelled
Build / Nix-2 (push) Has been cancelled
Build / Nix-3 (push) Has been cancelled
Build / Nix-4 (push) Has been cancelled
Build / Nix-5 (push) Has been cancelled
Build / Nix-6 (push) Has been cancelled
Build / Nix-7 (push) Has been cancelled
Build / Nix-8 (push) Has been cancelled
Build / Nix-9 (push) Has been cancelled
Build / Nix-10 (push) Has been cancelled
Build / Nix-11 (push) Has been cancelled
Build / Nix-12 (push) Has been cancelled
Build / Nix-13 (push) Has been cancelled
Build / Nix-14 (push) Has been cancelled
Build / Nix-15 (push) Has been cancelled
Build / Nix-16 (push) Has been cancelled
Build / Nix-17 (push) Has been cancelled
Build / Nix-18 (push) Has been cancelled
Build / Nix-19 (push) Has been cancelled
Build / Nix-20 (push) Has been cancelled
Build / Nix-21 (push) Has been cancelled
Build / Nix-22 (push) Has been cancelled
Build / Nix-23 (push) Has been cancelled
Build / Nix-24 (push) Has been cancelled
Build / Nix-25 (push) Has been cancelled
Build / Nix-26 (push) Has been cancelled
Build / Nix-27 (push) Has been cancelled
Build / Nix-28 (push) Has been cancelled
Build / Nix-29 (push) Has been cancelled
Build / Nix-30 (push) Has been cancelled
Build / Nix-31 (push) Has been cancelled
Build / Archlinux (push) Has been cancelled
Lint / Lint (push) Has been cancelled
This change requires more QtPrivate usage but eliminates generation or cleanup related window incubation controller bugs. Additionally it enables async loads prior to rendering windows.
This commit is contained in:
parent
eecc2f88b3
commit
bcc3d4265e
7 changed files with 166 additions and 28 deletions
|
|
@ -36,6 +36,8 @@ set shell id.
|
||||||
- Fixed hyprland active toplevel not resetting after window closes.
|
- Fixed hyprland active toplevel not resetting after window closes.
|
||||||
- Fixed hyprland ipc window names and titles being reversed.
|
- Fixed hyprland ipc window names and titles being reversed.
|
||||||
- Fixed missing signals for system tray item title and description updates.
|
- Fixed missing signals for system tray item title and description updates.
|
||||||
|
- Fixed asynchronous loaders not working after reload.
|
||||||
|
- Fixed asynchronous loaders not working before window creation.
|
||||||
|
|
||||||
## Packaging Changes
|
## Packaging Changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ qt_add_qml_module(quickshell-core
|
||||||
|
|
||||||
install_qml_module(quickshell-core)
|
install_qml_module(quickshell-core)
|
||||||
|
|
||||||
target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::Widgets)
|
target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::QuickPrivate Qt::Widgets)
|
||||||
|
|
||||||
qs_module_pch(quickshell-core SET large)
|
qs_module_pch(quickshell-core SET large)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,8 @@ EngineGeneration::EngineGeneration(const QDir& rootPath, QmlScanner scanner)
|
||||||
this->engine->addImportPath("qs:@/");
|
this->engine->addImportPath("qs:@/");
|
||||||
|
|
||||||
this->engine->setNetworkAccessManagerFactory(&this->interceptNetFactory);
|
this->engine->setNetworkAccessManagerFactory(&this->interceptNetFactory);
|
||||||
this->engine->setIncubationController(&this->delayedIncubationController);
|
this->incubationController.initLoop();
|
||||||
|
this->engine->setIncubationController(&this->incubationController);
|
||||||
|
|
||||||
this->engine->addImageProvider("icon", new IconImageProvider());
|
this->engine->addImageProvider("icon", new IconImageProvider());
|
||||||
this->engine->addImageProvider("qsimage", new QsImageProvider());
|
this->engine->addImageProvider("qsimage", new QsImageProvider());
|
||||||
|
|
@ -134,7 +135,7 @@ void EngineGeneration::onReload(EngineGeneration* old) {
|
||||||
// new generation acquires it then incubators will hang intermittently
|
// new generation acquires it then incubators will hang intermittently
|
||||||
qCDebug(logIncubator) << "Locking incubation controllers of old generation" << old;
|
qCDebug(logIncubator) << "Locking incubation controllers of old generation" << old;
|
||||||
old->incubationControllersLocked = true;
|
old->incubationControllersLocked = true;
|
||||||
old->assignIncubationController();
|
old->updateIncubationMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(this->engine, &QQmlEngine::quit, this, &EngineGeneration::quit);
|
QObject::connect(this->engine, &QQmlEngine::quit, this, &EngineGeneration::quit);
|
||||||
|
|
@ -288,29 +289,18 @@ void EngineGeneration::trackWindowIncubationController(QQuickWindow* window) {
|
||||||
|
|
||||||
QObject::connect(window, &QObject::destroyed, this, &EngineGeneration::onTrackedWindowDestroyed);
|
QObject::connect(window, &QObject::destroyed, this, &EngineGeneration::onTrackedWindowDestroyed);
|
||||||
this->trackedWindows.append(window);
|
this->trackedWindows.append(window);
|
||||||
this->assignIncubationController();
|
this->updateIncubationMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineGeneration::onTrackedWindowDestroyed(QObject* object) {
|
void EngineGeneration::onTrackedWindowDestroyed(QObject* object) {
|
||||||
this->trackedWindows.removeAll(static_cast<QQuickWindow*>(object)); // NOLINT
|
this->trackedWindows.removeAll(static_cast<QQuickWindow*>(object)); // NOLINT
|
||||||
this->assignIncubationController();
|
this->updateIncubationMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineGeneration::assignIncubationController() {
|
void EngineGeneration::updateIncubationMode() {
|
||||||
QQmlIncubationController* controller = &this->delayedIncubationController;
|
// If we're in a situation with only hidden but tracked windows this might be wrong,
|
||||||
|
// but it seems to at least work.
|
||||||
for (auto* window: this->trackedWindows) {
|
this->incubationController.setIncubationMode(!this->trackedWindows.empty());
|
||||||
if (auto* wctl = window->incubationController()) {
|
|
||||||
controller = wctl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(logIncubator) << "Assigning incubation controller" << controller << "to generation"
|
|
||||||
<< this
|
|
||||||
<< "fallback:" << (controller == &this->delayedIncubationController);
|
|
||||||
|
|
||||||
this->engine->setIncubationController(controller);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EngineGeneration* EngineGeneration::currentGeneration() {
|
EngineGeneration* EngineGeneration::currentGeneration() {
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ public:
|
||||||
QFileSystemWatcher* watcher = nullptr;
|
QFileSystemWatcher* watcher = nullptr;
|
||||||
QVector<QString> deletedWatchedFiles;
|
QVector<QString> deletedWatchedFiles;
|
||||||
QVector<QString> extraWatchedFiles;
|
QVector<QString> extraWatchedFiles;
|
||||||
DelayedQmlIncubationController delayedIncubationController;
|
QsIncubationController incubationController;
|
||||||
bool reloadComplete = false;
|
bool reloadComplete = false;
|
||||||
QuickshellGlobal* qsgInstance = nullptr;
|
QuickshellGlobal* qsgInstance = nullptr;
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void postReload();
|
void postReload();
|
||||||
void assignIncubationController();
|
void updateIncubationMode();
|
||||||
QVector<QQuickWindow*> trackedWindows;
|
QVector<QQuickWindow*> trackedWindows;
|
||||||
bool incubationControllersLocked = false;
|
bool incubationControllersLocked = false;
|
||||||
QHash<const void*, EngineGenerationExt*> extensions;
|
QHash<const void*, EngineGenerationExt*> extensions;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
#include "incubator.hpp"
|
#include "incubator.hpp"
|
||||||
|
|
||||||
|
#include <private/qsgrenderloop_p.h>
|
||||||
|
#include <qabstractanimation.h>
|
||||||
|
#include <qguiapplication.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qminmax.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
#include <qqmlincubator.h>
|
#include <qqmlincubator.h>
|
||||||
|
#include <qscreen.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include "logcat.hpp"
|
#include "logcat.hpp"
|
||||||
|
|
@ -15,3 +24,112 @@ void QsQmlIncubator::statusChanged(QQmlIncubator::Status status) {
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::initLoop() {
|
||||||
|
auto* app = static_cast<QGuiApplication*>(QGuiApplication::instance()); // NOLINT
|
||||||
|
this->renderLoop = QSGRenderLoop::instance();
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
app,
|
||||||
|
&QGuiApplication::screenAdded,
|
||||||
|
this,
|
||||||
|
&QsIncubationController::updateIncubationTime
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
app,
|
||||||
|
&QGuiApplication::screenRemoved,
|
||||||
|
this,
|
||||||
|
&QsIncubationController::updateIncubationTime
|
||||||
|
);
|
||||||
|
|
||||||
|
this->updateIncubationTime();
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->renderLoop,
|
||||||
|
&QSGRenderLoop::timeToIncubate,
|
||||||
|
this,
|
||||||
|
&QsIncubationController::incubate
|
||||||
|
);
|
||||||
|
|
||||||
|
QAnimationDriver* animationDriver = this->renderLoop->animationDriver();
|
||||||
|
if (animationDriver) {
|
||||||
|
QObject::connect(
|
||||||
|
animationDriver,
|
||||||
|
&QAnimationDriver::stopped,
|
||||||
|
this,
|
||||||
|
&QsIncubationController::animationStopped
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
qCInfo(logIncubator) << "Render loop does not have animation driver, animationStopped cannot "
|
||||||
|
"be used to trigger incubation.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::setIncubationMode(bool render) {
|
||||||
|
if (render == this->followRenderloop) return;
|
||||||
|
this->followRenderloop = render;
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
qCDebug(logIncubator) << "Incubation mode changed: render loop driven";
|
||||||
|
} else {
|
||||||
|
qCDebug(logIncubator) << "Incubation mode changed: event loop driven";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!render && this->incubatingObjectCount()) this->incubateLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::timerEvent(QTimerEvent* /*event*/) {
|
||||||
|
this->killTimer(this->timerId);
|
||||||
|
this->timerId = 0;
|
||||||
|
this->incubate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::incubateLater() {
|
||||||
|
if (this->followRenderloop) {
|
||||||
|
if (this->timerId != 0) {
|
||||||
|
this->killTimer(this->timerId);
|
||||||
|
this->timerId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incubate again at the end of the event processing queue
|
||||||
|
QMetaObject::invokeMethod(this, &QsIncubationController::incubate, Qt::QueuedConnection);
|
||||||
|
} else if (this->timerId == 0) {
|
||||||
|
// Wait for a while before processing the next batch. Using a
|
||||||
|
// timer to avoid starvation of system events.
|
||||||
|
this->timerId = this->startTimer(this->incubationTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::incubate() {
|
||||||
|
if ((!this->followRenderloop || this->renderLoop) && this->incubatingObjectCount()) {
|
||||||
|
if (!this->followRenderloop) {
|
||||||
|
this->incubateFor(10);
|
||||||
|
if (this->incubatingObjectCount()) this->incubateLater();
|
||||||
|
} else if (this->renderLoop->interleaveIncubation()) {
|
||||||
|
this->incubateFor(this->incubationTime);
|
||||||
|
} else {
|
||||||
|
this->incubateFor(this->incubationTime * 2);
|
||||||
|
if (this->incubatingObjectCount()) this->incubateLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::animationStopped() { this->incubate(); }
|
||||||
|
|
||||||
|
void QsIncubationController::incubatingObjectCountChanged(int count) {
|
||||||
|
if (count
|
||||||
|
&& (!this->followRenderloop
|
||||||
|
|| (this->renderLoop && !this->renderLoop->interleaveIncubation())))
|
||||||
|
{
|
||||||
|
this->incubateLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIncubationController::updateIncubationTime() {
|
||||||
|
auto* screen = QGuiApplication::primaryScreen();
|
||||||
|
if (!screen) return;
|
||||||
|
|
||||||
|
// 1/3 frame on primary screen
|
||||||
|
this->incubationTime = qMax(1, static_cast<int>(1000 / screen->refreshRate() / 3));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qpointer.h>
|
||||||
#include <qqmlincubator.h>
|
#include <qqmlincubator.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
|
@ -25,7 +26,37 @@ signals:
|
||||||
void failed();
|
void failed();
|
||||||
};
|
};
|
||||||
|
|
||||||
class DelayedQmlIncubationController: public QQmlIncubationController {
|
class QSGRenderLoop;
|
||||||
// Do nothing.
|
|
||||||
// This ensures lazy loaders don't start blocking before onReload creates windows.
|
class QsIncubationController
|
||||||
|
: public QObject
|
||||||
|
, public QQmlIncubationController {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void initLoop();
|
||||||
|
void setIncubationMode(bool render);
|
||||||
|
void incubateLater();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent* event) override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void incubate();
|
||||||
|
void animationStopped();
|
||||||
|
void updateIncubationTime();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void incubatingObjectCountChanged(int count) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// QPointer did not work with forward declarations prior to 6.7
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||||
|
QPointer<QSGRenderLoop> renderLoop = nullptr;
|
||||||
|
#else
|
||||||
|
QSGRenderLoop* renderLoop = nullptr;
|
||||||
|
#endif
|
||||||
|
int incubationTime = 0;
|
||||||
|
int timerId = 0;
|
||||||
|
bool followRenderloop = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,6 @@
|
||||||
/// > Notably, @@Variants does not corrently support asynchronous
|
/// > Notably, @@Variants does not corrently support asynchronous
|
||||||
/// > loading, meaning using it inside a LazyLoader will block similarly to not
|
/// > loading, meaning using it inside a LazyLoader will block similarly to not
|
||||||
/// > having a loader to start with.
|
/// > having a loader to start with.
|
||||||
///
|
|
||||||
/// > [!WARNING] LazyLoaders do not start loading before the first window is created,
|
|
||||||
/// > meaning if you create all windows inside of lazy loaders, none of them will ever load.
|
|
||||||
class LazyLoader: public Reloadable {
|
class LazyLoader: public Reloadable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// The fully loaded item if the loader is @@loading or @@active, or `null`
|
/// The fully loaded item if the loader is @@loading or @@active, or `null`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue