fix: fix nvidia update interval on config change + more robust read path for generic

This commit is contained in:
2026-09-30 14:26:50 +02:00
parent 3373f11193
commit 133d453f6f
7 changed files with 390 additions and 521 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include "common.hpp"
#include "enums.hpp"
@@ -20,7 +21,7 @@ class Services : public settings::ObjectNode {
CONFIG_PROPERTY(qreal, brightnessIncrement, 0.1)
CONFIG_PROPERTY(bool, ddcutilService, true)
CONFIG_PROPERTY(QString, defaultPlayer, QStringLiteral("Spotify"))
CONFIG_PROPERTY(QString, gpuType, QStringLiteral("NVIDIA"))
CONFIG_ENUM_PROPERTY(GpuType, gpuType, GpuType::Auto)
CONFIG_PROPERTY(LyricsBackend::Enum, lyricsBackend, LyricsBackend::Auto)
CONFIG_PROPERTY(qreal, maxVolume, 1)
CONFIG_PROPERTY(qreal, minBrightness, 0.01)
+342 -248
View File
@@ -2,97 +2,187 @@
#include "sensorslib.hpp"
#include <cmath>
#include <qcontainerfwd.h>
#include "config/rootnodes.hpp"
#include "config/services.hpp"
#include <qdir.h>
#include <qfile.h>
#include <qobject.h>
#include <qdiriterator.h>
#include <qregularexpression.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QFileSystemWatcher>
#include <QTimer>
namespace ZShell::services {
using Qt::StringLiterals::operator""_s;
namespace {
constexpr const char* kTypeDetectScript =
"if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null "
"2>&1; then echo NVIDIA;"
" elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep "
"-q .; then echo GENERIC;"
" else echo NONE; fi";
QStringList cardFiles(const QString& relative) {
static const QRegularExpression k_cardRe(u"^card\\d+$"_s);
constexpr const char* kNameDetectScript =
"nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null"
" || glxinfo -B 2>/dev/null | grep 'Device:' | cut -d':' -f2 | cut -d'(' "
"-f1"
" || lspci 2>/dev/null | grep -i 'vga\\|3d controller\\|display' | head -1";
QStringList files;
QDirIterator it(u"/sys/class/drm"_s, QDir::Dirs | QDir::NoDotAndDotDot);
while (it.hasNext()) {
const QString path = it.next();
if (!k_cardRe.match(it.fileName()).hasMatch()) continue;
const QString file = path + relative;
if (QFile::exists(file)) files << file;
}
return files;
}
std::optional<qreal> readNumber(const QString& path) {
QFile f(path);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return std::nullopt;
bool ok = false;
const qreal v = f.readAll().trimmed().toDouble(&ok);
if (!ok) return std::nullopt;
return v;
}
QString cleanName(QString s) {
static const QRegularExpression k_noise(
u"\\(R\\)|\\(TM\\)|Graphics"_s,
QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression k_spaces(u"\\s+"_s);
s.replace(k_noise, {});
s.replace(k_spaces, u" "_s);
return s.trimmed();
}
QString parseNvidiaName(const QByteArray& out) {
const QString first =
QString::fromUtf8(out).split(u'\n').value(0).trimmed();
return first.isEmpty() ? QString() : cleanName(first);
}
QString parseGlxinfoName(const QByteArray& out) {
const QStringList lines = QString::fromUtf8(out).split(u'\n');
for (const QString& line : lines) {
const qsizetype idx = line.indexOf(u"Device:"_s);
if (idx < 0) {
continue;
}
QString rest = line.mid(idx + 7);
const qsizetype paren = rest.indexOf(u'(');
if (paren >= 0) {
rest = rest.left(paren);
}
const QString cleaned = cleanName(rest);
if (!cleaned.isEmpty()) {
return cleaned;
}
}
return {};
}
QString parseLspciName(const QByteArray& out) {
static const QRegularExpression k_lineRe(
u"vga|3d controller|display"_s,
QRegularExpression::CaseInsensitiveOption);
const QStringList lines = QString::fromUtf8(out).split(u'\n');
QString match;
for (const QString& line : lines) {
if (k_lineRe.match(line).hasMatch()) {
match = line;
break;
}
}
if (match.isEmpty()) {
return {};
}
static const QRegularExpression k_bracketRe(u"\\[([^\\]]+)\\][^\\[]*$"_s);
const auto bracket = k_bracketRe.match(match);
if (bracket.hasMatch()) {
return cleanName(bracket.captured(1));
}
static const QRegularExpression k_colonRe(u":\\s+(.+)"_s);
const auto colon = k_colonRe.match(match);
if (colon.hasMatch()) {
return cleanName(colon.captured(1));
}
return {};
}
struct NameSource {
QString program;
QStringList args;
QString (*parse)(const QByteArray&);
};
const std::array<NameSource, 3>& nameSources() {
static const std::array<NameSource, 3> k_sources = {{
{
.program = u"nvidia-smi"_s,
.args = {u"--query-gpu=name"_s, u"--format=csv,noheader"_s},
.parse = &parseNvidiaName,
},
{
.program = u"glxinfo"_s,
.args = {u"-B"_s},
.parse = &parseGlxinfoName,
},
{
.program = u"lspci"_s,
.args = {},
.parse = &parseLspciName,
},
}};
return k_sources;
}
constexpr int k_nvidiaSource = 0;
constexpr int k_firstGenericSource = 1;
} // namespace
Gpu::Gpu(QObject* parent) : TickingService(parent) {
QString configPath =
QDir::homePath() + QStringLiteral("/.config/zshell/config.json");
auto* svc = config::ConfigSingleton::instance()->services();
m_userType = svc->gpuType();
QObject::connect(svc, &config::Services::gpuTypeChanged, this, [this, svc] {
const GpuType value = svc->gpuType();
if (value == m_userType) return;
m_userType = value;
resolveGpu();
});
auto reloadConfig = [this, configPath]() {
QFile file(configPath);
if (file.open(QIODevice::ReadOnly)) {
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
if (!doc.isNull()) {
QJsonObject services =
doc.object().value("services").toObject();
setUserType(parseType(services.value("gpuType").toString()));
}
}
};
resolveGpu();
reloadConfig();
m_restartTimer = new QTimer(this);
m_restartTimer->setSingleShot(true);
m_restartTimer->setInterval(500);
QObject::connect(m_restartTimer, &QTimer::timeout, this, [this] {
if (!m_nvidiaProc || m_type != GpuType::Nvidia) return;
static QFileSystemWatcher* watcher = new QFileSystemWatcher();
if (!watcher->files().contains(configPath)) {
QObject::connect(
watcher,
&QFileSystemWatcher::fileChanged,
this,
[this, configPath]() {
QTimer::singleShot(100, this, [this, configPath]() {
QFile file(configPath);
if (file.open(QIODevice::ReadOnly)) {
QJsonDocument doc =
QJsonDocument::fromJson(file.readAll());
if (!doc.isNull()) {
QJsonObject services =
doc.object().value("services").toObject();
setUserType(parseType(
services.value("gpuType").toString()));
}
}
});
});
watcher->addPath(configPath);
stopNvidiaUsage();
startNvidiaUsage();
});
QObject::connect(this, &TickingService::updateIntervalChanged, this, [this] {
if (!m_nvidiaProc) return;
m_restartTimer->start();
});
}
Gpu::~Gpu() {
if (!m_nvidiaProc) return;
m_nvidiaProc->disconnect(this);
m_nvidiaProc->terminate();
if (!m_nvidiaProc->waitForFinished(500)) {
m_nvidiaProc->kill();
m_nvidiaProc->waitForFinished(200);
}
// Detection must run before any ServiceRef appears: callers may gate the ref on
// `type !== Gpu.None`, which would otherwise deadlock the detection.
if (m_userType == Auto) {
detectTypeOnce();
}
detectNameOnce();
}
Gpu::Type Gpu::type() const {
return m_userType == Auto ? m_autoType : m_userType;
}
Gpu::Type Gpu::userType() const {
return m_userType;
}
Gpu::Type Gpu::autoType() const {
return m_autoType;
GpuType Gpu::type() const {
return m_type;
}
QString Gpu::name() const {
@@ -115,28 +205,13 @@ qreal Gpu::memoryTotal() const {
return m_memoryTotal;
}
void Gpu::setUserType(Type value) {
if (value == m_userType) {
return;
}
const Type prevDerived = type();
m_userType = value;
Q_EMIT userTypeChanged();
if (type() != prevDerived) {
Q_EMIT typeChanged();
}
}
void Gpu::setType(GpuType value) {
if (value == m_type) return;
void Gpu::setAutoType(Type value) {
if (value == m_userType) {
return;
}
const Type prevDerived = type();
m_autoType = value;
Q_EMIT autoTypeChanged();
if (type() != prevDerived) {
Q_EMIT typeChanged();
}
m_type = value;
if (m_type != GpuType::Nvidia) stopNvidiaUsage();
if (m_type == GpuType::None) resetUsage();
emit typeChanged();
}
void Gpu::setName(QString value) {
@@ -147,6 +222,12 @@ void Gpu::setName(QString value) {
Q_EMIT nameChanged();
}
void Gpu::setDetecting(bool value) {
if (value == m_detecting) return;
m_detecting = value;
emit detectingChanged();
}
void Gpu::setMemoryUsed(qreal value) {
if (std::abs(m_memoryUsed - value) < 0.001) return;
m_memoryUsed = value;
@@ -160,168 +241,192 @@ void Gpu::setMemoryTotal(qreal value) {
}
void Gpu::tick() {
const Type t = type();
if (t == Generic) {
if (m_type == GpuType::Generic) {
readGenericUsage();
readGenericMemory();
readGpuTemperature();
} else if (t == Nvidia) {
} else if (m_type == GpuType::Nvidia) {
startNvidiaUsage();
} else {
if (std::abs(m_percentage) > 0.0001) {
m_percentage = 0.0;
Q_EMIT percentageChanged();
}
if (std::abs(m_temperature) > 0.05) {
m_temperature = 0.0;
Q_EMIT temperatureChanged();
}
resetUsage();
}
}
void Gpu::detectTypeOnce() {
if (m_typeProc) {
return;
void Gpu::refreshSysfsCache() {
m_busyFiles = cardFiles(u"/device/gpu_busy_percent"_s);
m_vramFiles.clear();
const QStringList totals = cardFiles(u"/device/mem_info_vram_total"_s);
for (const QString& total : totals) {
QString used = total;
used.replace(u"mem_info_vram_total"_s, u"mem_info_vram_used"_s);
if (QFile::exists(used)) m_vramFiles.append({used, total});
}
m_typeProc = new QProcess(this);
QObject::connect(
m_typeProc,
&QProcess::finished,
this,
[this](int, QProcess::ExitStatus) {
const QByteArray out =
m_typeProc->readAllStandardOutput().trimmed();
if (!out.isEmpty()) {
setAutoType(parseType(QString::fromLatin1(out)));
}
m_typeProc->deleteLater();
m_typeProc = nullptr;
});
m_typeProc->start(
QStringLiteral("sh"),
{QStringLiteral("-c"), QString::fromLatin1(kTypeDetectScript)});
}
void Gpu::detectNameOnce() {
if (m_nameProc) {
void Gpu::resolveGpu() {
refreshSysfsCache();
const int generation = ++m_generation;
if (m_userType != GpuType::Auto) setType(m_userType);
if (m_userType == GpuType::None) {
setName({});
setDetecting(false);
return;
}
m_nameProc = new QProcess(this);
setName({});
setDetecting(true);
tryNameSource(
m_userType == GpuType::Generic ? k_firstGenericSource : k_nvidiaSource,
generation);
}
int Gpu::probeEnd() const {
return m_userType == GpuType::Nvidia
? k_firstGenericSource
: static_cast<int>(nameSources().size());
}
void Gpu::tryNameSource(int index, int generation) {
const NameSource& src = nameSources().at(static_cast<std::size_t>(index));
runProcess(
src.program,
src.args,
[this, index, generation, parse = src.parse](const QByteArray& out) {
finishNameSource(index, generation, parse(out));
});
}
void Gpu::finishNameSource(int index, int generation, QString name) {
if (generation != m_generation) {
return;
}
if (m_userType == GpuType::Auto && index == k_nvidiaSource) {
if (!name.isEmpty())
setType(GpuType::Nvidia);
else
setType(m_busyFiles.isEmpty() ? GpuType::None : GpuType::Generic);
if (m_type == GpuType::None) {
setName({});
setDetecting(false);
return;
}
}
if (!name.isEmpty()) {
setName(std::move(name));
setDetecting(false);
return;
}
const int next = index + 1;
if (next < probeEnd()) {
tryNameSource(next, generation);
} else {
setName({});
setDetecting(false);
}
}
void Gpu::runProcess(
const QString& program,
const QStringList& args,
std::function<void(const QByteArray&)> callback) {
auto* proc = new QProcess(this);
proc->setStandardErrorFile(QProcess::nullDevice());
const auto finish =
[proc, callback = std::move(callback)](const QByteArray& out) {
callback(out);
proc->deleteLater();
};
QObject::connect(
m_nameProc,
proc,
&QProcess::finished,
this,
[this](int, QProcess::ExitStatus) {
const QString output =
QString::fromUtf8(m_nameProc->readAllStandardOutput()).trimmed();
if (!output.isEmpty()) {
const QString lower = output.toLower();
if (lower.contains(QStringLiteral("nvidia")) ||
lower.contains(QStringLiteral("geforce")) ||
lower.contains(QStringLiteral("rtx")) ||
lower.contains(QStringLiteral("gtx")) ||
lower.contains(QStringLiteral("rx"))) {
setName(cleanName(output));
} else {
static const QRegularExpression bracketRe(
QStringLiteral("\\[([^\\]]+)\\][^\\[]*$"));
const auto bracket = bracketRe.match(output);
if (bracket.hasMatch()) {
setName(cleanName(bracket.captured(1)));
} else {
static const QRegularExpression colonRe(
QStringLiteral(":\\s*(.+)"));
const auto colon = colonRe.match(output);
if (colon.hasMatch()) {
setName(cleanName(colon.captured(1)));
}
}
}
}
m_nameProc->deleteLater();
m_nameProc = nullptr;
[finish, proc](int code, QProcess::ExitStatus status) {
const bool ok = status == QProcess::NormalExit && code == 0;
finish(ok ? proc->readAllStandardOutput() : QByteArray());
});
m_nameProc->start(
QStringLiteral("sh"),
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
QObject::connect(
proc,
&QProcess::errorOccurred,
this,
[finish](QProcess::ProcessError err) {
if (err == QProcess::FailedToStart) {
finish(QByteArray());
}
});
proc->start(program, args);
}
void Gpu::onStopped() {
stopNvidiaUsage();
}
void Gpu::readGenericMemory() {
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
.entryList(
QStringList() << QStringLiteral("card*"),
QDir::Dirs | QDir::NoDotAndDotDot);
qreal totalMem = 0.0;
qreal usedMem = 0.0;
for (const QString& card : paths) {
QFile total(
QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_total")
.arg(card));
if (!total.open(QIODevice::ReadOnly | QIODevice::Text)) {
continue;
}
bool ok = false;
const qreal v = total.readAll().trimmed().toDouble(&ok);
total.close();
if (ok) {
totalMem += v;
}
QFile used(QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_used")
.arg(card));
if (!used.open(QIODevice::ReadOnly | QIODevice::Text)) {
continue;
}
bool ok1 = false;
const qreal v1 = used.readAll().trimmed().toDouble(&ok1);
used.close();
if (ok1) {
usedMem += v1;
}
qreal total = 0.0;
qreal used = 0.0;
for (const VramFiles& v : std::as_const(m_vramFiles)) {
const auto t = readNumber(v.total);
const auto u = readNumber(v.used);
if (!t || !u) continue;
total += *t;
used += *u;
}
setMemoryTotal(totalMem);
setMemoryUsed(usedMem);
setMemoryTotal(total);
setMemoryUsed(used);
}
void Gpu::readGenericUsage() {
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
.entryList(
QStringList() << QStringLiteral("card*"),
QDir::Dirs | QDir::NoDotAndDotDot);
qreal sum = 0.0;
int count = 0;
for (const QString& card : paths) {
QFile f(QStringLiteral("/sys/class/drm/%1/device/gpu_busy_percent")
.arg(card));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
continue;
}
bool ok = false;
const qreal v = f.readAll().trimmed().toDouble(&ok);
f.close();
if (ok) {
sum += v;
for (const QString& path : std::as_const(m_busyFiles)) {
if (const auto v = readNumber(path)) {
sum += *v;
++count;
}
}
const qreal newPerc = count > 0 ? sum / count / 100.0 : 0.0;
if (std::abs(newPerc - m_percentage) > 0.0001) {
m_percentage = newPerc;
Q_EMIT percentageChanged();
emit percentageChanged();
}
}
void Gpu::startNvidiaUsage() {
if (m_nvidiaProc) {
void Gpu::stopNvidiaUsage() {
m_restartTimer->stop();
if (!m_nvidiaProc) return;
QProcess* proc = m_nvidiaProc;
m_nvidiaProc = nullptr;
proc->disconnect(this);
if (proc->state() == QProcess::NotRunning) {
proc->deleteLater();
return;
}
m_nvidiaProc = new QProcess(this);
QObject::connect(proc, &QProcess::finished, proc, &QObject::deleteLater);
proc->terminate();
}
void Gpu::startNvidiaUsage() {
if (m_nvidiaProc) return;
auto* proc = new QProcess(this);
m_nvidiaProc = proc;
QObject::connect(
m_nvidiaProc, &QProcess::readyReadStandardOutput, this, [this]() {
while (m_nvidiaProc->canReadLine()) {
const QByteArray out = m_nvidiaProc->readLine();
proc, &QProcess::readyReadStandardOutput, this, [this, proc]() {
while (proc->canReadLine()) {
const QByteArray out = proc->readLine();
const QString output = QString::fromUtf8(out).trimmed();
if (output.isEmpty()) continue;
@@ -357,21 +462,21 @@ void Gpu::startNvidiaUsage() {
}
});
QObject::connect(m_nvidiaProc, &QProcess::finished, this, [this]() {
m_nvidiaProc->deleteLater();
m_nvidiaProc = nullptr;
});
auto cleanup = [this, proc]() {
if (m_nvidiaProc == proc) m_nvidiaProc = nullptr;
proc->deleteLater();
};
QObject::connect(proc, &QProcess::finished, this, cleanup);
QObject::connect(
m_nvidiaProc,
proc,
&QProcess::errorOccurred,
this,
[this](QProcess::ProcessError) {
m_nvidiaProc->deleteLater();
m_nvidiaProc = nullptr;
});
[cleanup](QProcess::ProcessError) { cleanup(); });
m_nvidiaProc->start(
proc->start(
QStringLiteral("nvidia-smi"),
{QStringLiteral(
"--query-gpu=utilization.gpu,temperature.gpu,"
@@ -386,32 +491,21 @@ void Gpu::readGpuTemperature() {
const qreal newTemp = t.value_or(0.0);
if (std::abs(newTemp - m_temperature) > 0.05) {
m_temperature = newTemp;
Q_EMIT temperatureChanged();
emit temperatureChanged();
}
}
Gpu::Type Gpu::parseType(const QString& s) {
const QString u = s.trimmed().toUpper();
if (u.isEmpty()) {
return Auto;
void Gpu::resetUsage() {
if (std::abs(m_percentage) > 0.0001) {
m_percentage = 0.0;
emit percentageChanged();
}
if (u == QStringLiteral("NVIDIA")) {
return Nvidia;
if (std::abs(m_temperature) > 0.05) {
m_temperature = 0.0;
emit temperatureChanged();
}
if (u == QStringLiteral("GENERIC")) {
return Generic;
}
return None;
}
QString Gpu::cleanName(QString s) {
static const QRegularExpression noise(
QStringLiteral("\\(R\\)|\\(TM\\)|Graphics"),
QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression spaces(QStringLiteral("\\s+"));
s.replace(noise, QString());
s.replace(spaces, QStringLiteral(" "));
return s.trimmed();
setMemoryUsed(0.0);
setMemoryTotal(0.0);
}
} // namespace ZShell::services
+42 -28
View File
@@ -1,31 +1,29 @@
#pragma once
#include "tickingservice.hpp"
#include "config/enums.hpp"
#include <qprocess.h>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
namespace ZShell::services {
using GpuType = config::GpuType::Enum;
struct VramFiles {
QString used;
QString total;
};
class Gpu : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
enum Type {
Auto, // user override is empty (config "") — defer to detected autoType
None, // no usable GPU
Nvidia, // queried via nvidia-smi
Generic, // queried via /sys/class/drm/card*/device/gpu_busy_percent
};
Q_ENUM(Type)
private:
Q_PROPERTY(Type type READ type NOTIFY typeChanged)
Q_PROPERTY(Type userType READ userType NOTIFY userTypeChanged)
Q_PROPERTY(Type autoType READ autoType NOTIFY autoTypeChanged)
Q_PROPERTY(GpuType type READ type NOTIFY typeChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(bool detecting READ detecting NOTIFY detectingChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
Q_PROPERTY(qreal memoryUsed READ memoryUsed NOTIFY memoryUsedChanged)
@@ -33,11 +31,11 @@ class Gpu : public TickingService {
public:
explicit Gpu(QObject* parent = nullptr);
~Gpu() override;
[[nodiscard]] Type type() const;
[[nodiscard]] Type userType() const;
[[nodiscard]] Type autoType() const;
[[nodiscard]] GpuType type() const;
[[nodiscard]] QString name() const;
[[nodiscard]] bool detecting() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal temperature() const;
[[nodiscard]] qreal memoryUsed() const;
@@ -45,9 +43,8 @@ class Gpu : public TickingService {
signals:
void typeChanged();
void userTypeChanged();
void autoTypeChanged();
void nameChanged();
void detectingChanged();
void percentageChanged();
void temperatureChanged();
void memoryUsedChanged();
@@ -55,35 +52,52 @@ class Gpu : public TickingService {
protected:
void tick() override;
void onStopped() override;
private:
void detectTypeOnce();
void detectNameOnce();
void resolveGpu();
[[nodiscard]] int probeEnd() const;
void tryNameSource(int index, int generation);
void finishNameSource(int index, int generation, QString name);
void readGenericUsage();
void stopNvidiaUsage();
void startNvidiaUsage();
void readGpuTemperature();
void readGenericMemory();
void resetUsage();
void setUserType(Type value);
void setAutoType(Type value);
void runProcess(
const QString& program,
const QStringList& args,
std::function<void(const QByteArray&)> callback);
void setType(GpuType value);
void setName(QString value);
void setDetecting(bool value);
void setMemoryUsed(qreal value);
void setMemoryTotal(qreal value);
[[nodiscard]] static Type parseType(const QString& s);
[[nodiscard]] static QString cleanName(QString s);
void refreshSysfsCache();
Type m_userType = Auto;
Type m_autoType = None;
GpuType m_userType = GpuType::Auto;
GpuType m_type = GpuType::None;
QString m_name;
bool m_detecting = false;
qreal m_percentage = 0.0;
qreal m_temperature = 0.0;
qreal m_memoryUsed = 0.0;
qreal m_memoryTotal = 0.0;
QProcess* m_typeProc = nullptr;
QProcess* m_nameProc = nullptr;
QStringList m_busyFiles;
QList<VramFiles> m_vramFiles;
int m_generation = 0;
bool m_nvidiaQuerying = false;
QProcess* m_nvidiaProc = nullptr;
QTimer* m_restartTimer = nullptr;
};
} // namespace ZShell::services
@@ -37,6 +37,7 @@ void TickingService::start() {
void TickingService::stop() {
m_running = false;
m_timer->stop();
onStopped();
}
void TickingService::applyInterval(int ms) {
@@ -26,6 +26,7 @@ class TickingService : public Service {
void stop() final;
virtual void tick() = 0;
virtual void onStopped() {}
private:
void applyInterval(int ms);