fix: fix nvidia update interval on config change + more robust read path for generic
This commit is contained in:
@@ -1,242 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import ZShell.Internal
|
|
||||||
import ZShell.Config
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property real _downloadSpeed: 0
|
|
||||||
property real _downloadTotal: 0
|
|
||||||
|
|
||||||
property real _initialRxBytes: 0
|
|
||||||
property real _initialTxBytes: 0
|
|
||||||
property bool _initialized: false
|
|
||||||
|
|
||||||
property real _prevRxBytes: 0
|
|
||||||
property real _prevTimestamp: 0
|
|
||||||
property real _prevTxBytes: 0
|
|
||||||
property real _uploadSpeed: 0
|
|
||||||
property real _uploadTotal: 0
|
|
||||||
|
|
||||||
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
|
||||||
|
|
||||||
readonly property real downloadSpeed: _downloadSpeed
|
|
||||||
|
|
||||||
readonly property real downloadTotal: _downloadTotal
|
|
||||||
readonly property int historyLength: Config.dashboard.performance.showVram ? 70 : 30
|
|
||||||
property int refCount: 0
|
|
||||||
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
|
||||||
readonly property real uploadSpeed: _uploadSpeed
|
|
||||||
readonly property real uploadTotal: _uploadTotal
|
|
||||||
|
|
||||||
function resetSampling(): void {
|
|
||||||
netDevFile.reload();
|
|
||||||
|
|
||||||
const content = netDevFile.text();
|
|
||||||
if (!content)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const data = root.parseNetDev(content);
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
root._prevRxBytes = data.rx;
|
|
||||||
root._prevTxBytes = data.tx;
|
|
||||||
root._prevTimestamp = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatBytes(bytes: real): var {
|
|
||||||
// Handle negative or invalid values
|
|
||||||
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
|
|
||||||
return {
|
|
||||||
value: 0,
|
|
||||||
unit: "B/s"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes < 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes,
|
|
||||||
unit: "B/s"
|
|
||||||
};
|
|
||||||
} else if (bytes < 1024 * 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes / 1024,
|
|
||||||
unit: "KB/s"
|
|
||||||
};
|
|
||||||
} else if (bytes < 1024 * 1024 * 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes / (1024 * 1024),
|
|
||||||
unit: "MB/s"
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
value: bytes / (1024 * 1024 * 1024),
|
|
||||||
unit: "GB/s"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatBytesTotal(bytes: real): var {
|
|
||||||
// Handle negative or invalid values
|
|
||||||
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
|
|
||||||
return {
|
|
||||||
value: 0,
|
|
||||||
unit: "B"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes < 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes,
|
|
||||||
unit: "B"
|
|
||||||
};
|
|
||||||
} else if (bytes < 1024 * 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes / 1024,
|
|
||||||
unit: "KB"
|
|
||||||
};
|
|
||||||
} else if (bytes < 1024 * 1024 * 1024) {
|
|
||||||
return {
|
|
||||||
value: bytes / (1024 * 1024),
|
|
||||||
unit: "MB"
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
value: bytes / (1024 * 1024 * 1024),
|
|
||||||
unit: "GB"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseNetDev(content: string): var {
|
|
||||||
const lines = content.split("\n");
|
|
||||||
let totalRx = 0;
|
|
||||||
let totalTx = 0;
|
|
||||||
|
|
||||||
for (let i = 2; i < lines.length; i++) {
|
|
||||||
const line = lines[i].trim();
|
|
||||||
if (!line)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const parts = line.split(/\s+/);
|
|
||||||
if (parts.length < 10)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const iface = parts[0].replace(":", "");
|
|
||||||
// Skip loopback interface
|
|
||||||
if (iface === "lo")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const rxBytes = parseFloat(parts[1]) || 0;
|
|
||||||
const txBytes = parseFloat(parts[9]) || 0;
|
|
||||||
|
|
||||||
totalRx += rxBytes;
|
|
||||||
totalTx += txBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
rx: totalRx,
|
|
||||||
tx: totalTx
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularBuffer {
|
|
||||||
id: _downloadBuffer
|
|
||||||
|
|
||||||
capacity: root.historyLength + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularBuffer {
|
|
||||||
id: _uploadBuffer
|
|
||||||
|
|
||||||
capacity: root.historyLength + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: netDevFile
|
|
||||||
|
|
||||||
path: "/proc/net/dev"
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: Config.dashboard.resourceUpdateInterval
|
|
||||||
repeat: true
|
|
||||||
running: true
|
|
||||||
triggeredOnStart: true
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
if (root.refCount <= 0) {
|
|
||||||
root.resetSampling();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
netDevFile.reload();
|
|
||||||
const content = netDevFile.text();
|
|
||||||
if (!content)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const data = root.parseNetDev(content);
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
if (!root._initialized) {
|
|
||||||
root._initialRxBytes = data.rx;
|
|
||||||
root._initialTxBytes = data.tx;
|
|
||||||
root._prevRxBytes = data.rx;
|
|
||||||
root._prevTxBytes = data.tx;
|
|
||||||
root._prevTimestamp = now;
|
|
||||||
root._initialized = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeDelta = (now - root._prevTimestamp) / 1000; // seconds
|
|
||||||
if (timeDelta > 0) {
|
|
||||||
// Calculate byte deltas
|
|
||||||
let rxDelta = data.rx - root._prevRxBytes;
|
|
||||||
let txDelta = data.tx - root._prevTxBytes;
|
|
||||||
|
|
||||||
// Handle counter overflow (when counters wrap around from max to 0)
|
|
||||||
// This happens when counters exceed 32-bit or 64-bit limits
|
|
||||||
if (rxDelta < 0) {
|
|
||||||
// Counter wrapped around - assume 64-bit counter
|
|
||||||
rxDelta += Math.pow(2, 64);
|
|
||||||
}
|
|
||||||
if (txDelta < 0) {
|
|
||||||
txDelta += Math.pow(2, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate speeds
|
|
||||||
root._downloadSpeed = rxDelta / timeDelta;
|
|
||||||
root._uploadSpeed = txDelta / timeDelta;
|
|
||||||
|
|
||||||
if (root._downloadSpeed >= 0 && isFinite(root._downloadSpeed))
|
|
||||||
_downloadBuffer.push(root._downloadSpeed);
|
|
||||||
|
|
||||||
if (root._uploadSpeed >= 0 && isFinite(root._uploadSpeed))
|
|
||||||
_uploadBuffer.push(root._uploadSpeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate totals with overflow handling
|
|
||||||
let downTotal = data.rx - root._initialRxBytes;
|
|
||||||
let upTotal = data.tx - root._initialTxBytes;
|
|
||||||
|
|
||||||
// Handle counter overflow for totals
|
|
||||||
if (downTotal < 0) {
|
|
||||||
downTotal += Math.pow(2, 64);
|
|
||||||
}
|
|
||||||
if (upTotal < 0) {
|
|
||||||
upTotal += Math.pow(2, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
root._downloadTotal = downTotal;
|
|
||||||
root._uploadTotal = upTotal;
|
|
||||||
|
|
||||||
root._prevRxBytes = data.rx;
|
|
||||||
root._prevTxBytes = data.tx;
|
|
||||||
root._prevTimestamp = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -78,12 +78,12 @@ CustomRect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onValuesChanged(): void {
|
function onUpdateTriggered(): void {
|
||||||
sparkline.targetMax = Math.max(NetworkUsage.downloadBuffer.maximum, NetworkUsage.uploadBuffer.maximum, 1024);
|
sparkline.targetMax = Math.max(NetworkUsage.downloadBuffer.maximum, NetworkUsage.uploadBuffer.maximum, 1024);
|
||||||
slideAnim.restart();
|
slideAnim.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: NetworkUsage.downloadBuffer
|
target: NetworkUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "enums.hpp"
|
#include "enums.hpp"
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ class Services : public settings::ObjectNode {
|
|||||||
CONFIG_PROPERTY(qreal, brightnessIncrement, 0.1)
|
CONFIG_PROPERTY(qreal, brightnessIncrement, 0.1)
|
||||||
CONFIG_PROPERTY(bool, ddcutilService, true)
|
CONFIG_PROPERTY(bool, ddcutilService, true)
|
||||||
CONFIG_PROPERTY(QString, defaultPlayer, QStringLiteral("Spotify"))
|
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(LyricsBackend::Enum, lyricsBackend, LyricsBackend::Auto)
|
||||||
CONFIG_PROPERTY(qreal, maxVolume, 1)
|
CONFIG_PROPERTY(qreal, maxVolume, 1)
|
||||||
CONFIG_PROPERTY(qreal, minBrightness, 0.01)
|
CONFIG_PROPERTY(qreal, minBrightness, 0.01)
|
||||||
|
|||||||
+342
-248
@@ -2,97 +2,187 @@
|
|||||||
|
|
||||||
#include "sensorslib.hpp"
|
#include "sensorslib.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include "config/rootnodes.hpp"
|
||||||
#include <qcontainerfwd.h>
|
#include "config/services.hpp"
|
||||||
|
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
#include <qobject.h>
|
#include <qdiriterator.h>
|
||||||
#include <qregularexpression.h>
|
#include <qregularexpression.h>
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonValue>
|
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
|
using Qt::StringLiterals::operator""_s;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr const char* kTypeDetectScript =
|
QStringList cardFiles(const QString& relative) {
|
||||||
"if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null "
|
static const QRegularExpression k_cardRe(u"^card\\d+$"_s);
|
||||||
"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";
|
|
||||||
|
|
||||||
constexpr const char* kNameDetectScript =
|
QStringList files;
|
||||||
"nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null"
|
QDirIterator it(u"/sys/class/drm"_s, QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
" || glxinfo -B 2>/dev/null | grep 'Device:' | cut -d':' -f2 | cut -d'(' "
|
while (it.hasNext()) {
|
||||||
"-f1"
|
const QString path = it.next();
|
||||||
" || lspci 2>/dev/null | grep -i 'vga\\|3d controller\\|display' | head -1";
|
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
|
} // namespace
|
||||||
|
|
||||||
Gpu::Gpu(QObject* parent) : TickingService(parent) {
|
Gpu::Gpu(QObject* parent) : TickingService(parent) {
|
||||||
QString configPath =
|
auto* svc = config::ConfigSingleton::instance()->services();
|
||||||
QDir::homePath() + QStringLiteral("/.config/zshell/config.json");
|
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]() {
|
resolveGpu();
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
stopNvidiaUsage();
|
||||||
if (!watcher->files().contains(configPath)) {
|
startNvidiaUsage();
|
||||||
QObject::connect(
|
});
|
||||||
watcher,
|
|
||||||
&QFileSystemWatcher::fileChanged,
|
QObject::connect(this, &TickingService::updateIntervalChanged, this, [this] {
|
||||||
this,
|
if (!m_nvidiaProc) return;
|
||||||
[this, configPath]() {
|
m_restartTimer->start();
|
||||||
QTimer::singleShot(100, this, [this, configPath]() {
|
});
|
||||||
QFile file(configPath);
|
}
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
QJsonDocument doc =
|
Gpu::~Gpu() {
|
||||||
QJsonDocument::fromJson(file.readAll());
|
if (!m_nvidiaProc) return;
|
||||||
if (!doc.isNull()) {
|
m_nvidiaProc->disconnect(this);
|
||||||
QJsonObject services =
|
m_nvidiaProc->terminate();
|
||||||
doc.object().value("services").toObject();
|
if (!m_nvidiaProc->waitForFinished(500)) {
|
||||||
setUserType(parseType(
|
m_nvidiaProc->kill();
|
||||||
services.value("gpuType").toString()));
|
m_nvidiaProc->waitForFinished(200);
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
watcher->addPath(configPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
GpuType Gpu::type() const {
|
||||||
return m_userType == Auto ? m_autoType : m_userType;
|
return m_type;
|
||||||
}
|
|
||||||
|
|
||||||
Gpu::Type Gpu::userType() const {
|
|
||||||
return m_userType;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gpu::Type Gpu::autoType() const {
|
|
||||||
return m_autoType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Gpu::name() const {
|
QString Gpu::name() const {
|
||||||
@@ -115,28 +205,13 @@ qreal Gpu::memoryTotal() const {
|
|||||||
return m_memoryTotal;
|
return m_memoryTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::setUserType(Type value) {
|
void Gpu::setType(GpuType value) {
|
||||||
if (value == m_userType) {
|
if (value == m_type) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Type prevDerived = type();
|
|
||||||
m_userType = value;
|
|
||||||
Q_EMIT userTypeChanged();
|
|
||||||
if (type() != prevDerived) {
|
|
||||||
Q_EMIT typeChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gpu::setAutoType(Type value) {
|
m_type = value;
|
||||||
if (value == m_userType) {
|
if (m_type != GpuType::Nvidia) stopNvidiaUsage();
|
||||||
return;
|
if (m_type == GpuType::None) resetUsage();
|
||||||
}
|
emit typeChanged();
|
||||||
const Type prevDerived = type();
|
|
||||||
m_autoType = value;
|
|
||||||
Q_EMIT autoTypeChanged();
|
|
||||||
if (type() != prevDerived) {
|
|
||||||
Q_EMIT typeChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::setName(QString value) {
|
void Gpu::setName(QString value) {
|
||||||
@@ -147,6 +222,12 @@ void Gpu::setName(QString value) {
|
|||||||
Q_EMIT nameChanged();
|
Q_EMIT nameChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gpu::setDetecting(bool value) {
|
||||||
|
if (value == m_detecting) return;
|
||||||
|
m_detecting = value;
|
||||||
|
emit detectingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void Gpu::setMemoryUsed(qreal value) {
|
void Gpu::setMemoryUsed(qreal value) {
|
||||||
if (std::abs(m_memoryUsed - value) < 0.001) return;
|
if (std::abs(m_memoryUsed - value) < 0.001) return;
|
||||||
m_memoryUsed = value;
|
m_memoryUsed = value;
|
||||||
@@ -160,168 +241,192 @@ void Gpu::setMemoryTotal(qreal value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::tick() {
|
void Gpu::tick() {
|
||||||
const Type t = type();
|
if (m_type == GpuType::Generic) {
|
||||||
if (t == Generic) {
|
|
||||||
readGenericUsage();
|
readGenericUsage();
|
||||||
readGenericMemory();
|
readGenericMemory();
|
||||||
readGpuTemperature();
|
readGpuTemperature();
|
||||||
} else if (t == Nvidia) {
|
} else if (m_type == GpuType::Nvidia) {
|
||||||
startNvidiaUsage();
|
startNvidiaUsage();
|
||||||
} else {
|
} else {
|
||||||
if (std::abs(m_percentage) > 0.0001) {
|
resetUsage();
|
||||||
m_percentage = 0.0;
|
|
||||||
Q_EMIT percentageChanged();
|
|
||||||
}
|
|
||||||
if (std::abs(m_temperature) > 0.05) {
|
|
||||||
m_temperature = 0.0;
|
|
||||||
Q_EMIT temperatureChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::detectTypeOnce() {
|
void Gpu::refreshSysfsCache() {
|
||||||
if (m_typeProc) {
|
m_busyFiles = cardFiles(u"/device/gpu_busy_percent"_s);
|
||||||
return;
|
|
||||||
|
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() {
|
void Gpu::resolveGpu() {
|
||||||
if (m_nameProc) {
|
refreshSysfsCache();
|
||||||
|
const int generation = ++m_generation;
|
||||||
|
|
||||||
|
if (m_userType != GpuType::Auto) setType(m_userType);
|
||||||
|
|
||||||
|
if (m_userType == GpuType::None) {
|
||||||
|
setName({});
|
||||||
|
setDetecting(false);
|
||||||
return;
|
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(
|
QObject::connect(
|
||||||
m_nameProc,
|
proc,
|
||||||
&QProcess::finished,
|
&QProcess::finished,
|
||||||
this,
|
this,
|
||||||
[this](int, QProcess::ExitStatus) {
|
[finish, proc](int code, QProcess::ExitStatus status) {
|
||||||
const QString output =
|
const bool ok = status == QProcess::NormalExit && code == 0;
|
||||||
QString::fromUtf8(m_nameProc->readAllStandardOutput()).trimmed();
|
finish(ok ? proc->readAllStandardOutput() : QByteArray());
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
m_nameProc->start(
|
QObject::connect(
|
||||||
QStringLiteral("sh"),
|
proc,
|
||||||
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
&QProcess::errorOccurred,
|
||||||
|
this,
|
||||||
|
[finish](QProcess::ProcessError err) {
|
||||||
|
if (err == QProcess::FailedToStart) {
|
||||||
|
finish(QByteArray());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
proc->start(program, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gpu::onStopped() {
|
||||||
|
stopNvidiaUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::readGenericMemory() {
|
void Gpu::readGenericMemory() {
|
||||||
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
qreal total = 0.0;
|
||||||
.entryList(
|
qreal used = 0.0;
|
||||||
QStringList() << QStringLiteral("card*"),
|
for (const VramFiles& v : std::as_const(m_vramFiles)) {
|
||||||
QDir::Dirs | QDir::NoDotAndDotDot);
|
const auto t = readNumber(v.total);
|
||||||
|
const auto u = readNumber(v.used);
|
||||||
qreal totalMem = 0.0;
|
if (!t || !u) continue;
|
||||||
qreal usedMem = 0.0;
|
total += *t;
|
||||||
for (const QString& card : paths) {
|
used += *u;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setMemoryTotal(total);
|
||||||
setMemoryTotal(totalMem);
|
setMemoryUsed(used);
|
||||||
setMemoryUsed(usedMem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::readGenericUsage() {
|
void Gpu::readGenericUsage() {
|
||||||
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
|
||||||
.entryList(
|
|
||||||
QStringList() << QStringLiteral("card*"),
|
|
||||||
QDir::Dirs | QDir::NoDotAndDotDot);
|
|
||||||
qreal sum = 0.0;
|
qreal sum = 0.0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (const QString& card : paths) {
|
for (const QString& path : std::as_const(m_busyFiles)) {
|
||||||
QFile f(QStringLiteral("/sys/class/drm/%1/device/gpu_busy_percent")
|
if (const auto v = readNumber(path)) {
|
||||||
.arg(card));
|
sum += *v;
|
||||||
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;
|
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const qreal newPerc = count > 0 ? sum / count / 100.0 : 0.0;
|
const qreal newPerc = count > 0 ? sum / count / 100.0 : 0.0;
|
||||||
if (std::abs(newPerc - m_percentage) > 0.0001) {
|
if (std::abs(newPerc - m_percentage) > 0.0001) {
|
||||||
m_percentage = newPerc;
|
m_percentage = newPerc;
|
||||||
Q_EMIT percentageChanged();
|
emit percentageChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gpu::startNvidiaUsage() {
|
void Gpu::stopNvidiaUsage() {
|
||||||
if (m_nvidiaProc) {
|
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;
|
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(
|
QObject::connect(
|
||||||
m_nvidiaProc, &QProcess::readyReadStandardOutput, this, [this]() {
|
proc, &QProcess::readyReadStandardOutput, this, [this, proc]() {
|
||||||
while (m_nvidiaProc->canReadLine()) {
|
while (proc->canReadLine()) {
|
||||||
const QByteArray out = m_nvidiaProc->readLine();
|
const QByteArray out = proc->readLine();
|
||||||
const QString output = QString::fromUtf8(out).trimmed();
|
const QString output = QString::fromUtf8(out).trimmed();
|
||||||
if (output.isEmpty()) continue;
|
if (output.isEmpty()) continue;
|
||||||
|
|
||||||
@@ -357,21 +462,21 @@ void Gpu::startNvidiaUsage() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(m_nvidiaProc, &QProcess::finished, this, [this]() {
|
auto cleanup = [this, proc]() {
|
||||||
m_nvidiaProc->deleteLater();
|
if (m_nvidiaProc == proc) m_nvidiaProc = nullptr;
|
||||||
m_nvidiaProc = nullptr;
|
|
||||||
});
|
proc->deleteLater();
|
||||||
|
};
|
||||||
|
|
||||||
|
QObject::connect(proc, &QProcess::finished, this, cleanup);
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
m_nvidiaProc,
|
proc,
|
||||||
&QProcess::errorOccurred,
|
&QProcess::errorOccurred,
|
||||||
this,
|
this,
|
||||||
[this](QProcess::ProcessError) {
|
[cleanup](QProcess::ProcessError) { cleanup(); });
|
||||||
m_nvidiaProc->deleteLater();
|
|
||||||
m_nvidiaProc = nullptr;
|
|
||||||
});
|
|
||||||
|
|
||||||
m_nvidiaProc->start(
|
proc->start(
|
||||||
QStringLiteral("nvidia-smi"),
|
QStringLiteral("nvidia-smi"),
|
||||||
{QStringLiteral(
|
{QStringLiteral(
|
||||||
"--query-gpu=utilization.gpu,temperature.gpu,"
|
"--query-gpu=utilization.gpu,temperature.gpu,"
|
||||||
@@ -386,32 +491,21 @@ void Gpu::readGpuTemperature() {
|
|||||||
const qreal newTemp = t.value_or(0.0);
|
const qreal newTemp = t.value_or(0.0);
|
||||||
if (std::abs(newTemp - m_temperature) > 0.05) {
|
if (std::abs(newTemp - m_temperature) > 0.05) {
|
||||||
m_temperature = newTemp;
|
m_temperature = newTemp;
|
||||||
Q_EMIT temperatureChanged();
|
emit temperatureChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gpu::Type Gpu::parseType(const QString& s) {
|
void Gpu::resetUsage() {
|
||||||
const QString u = s.trimmed().toUpper();
|
if (std::abs(m_percentage) > 0.0001) {
|
||||||
if (u.isEmpty()) {
|
m_percentage = 0.0;
|
||||||
return Auto;
|
emit percentageChanged();
|
||||||
}
|
}
|
||||||
if (u == QStringLiteral("NVIDIA")) {
|
if (std::abs(m_temperature) > 0.05) {
|
||||||
return Nvidia;
|
m_temperature = 0.0;
|
||||||
|
emit temperatureChanged();
|
||||||
}
|
}
|
||||||
if (u == QStringLiteral("GENERIC")) {
|
setMemoryUsed(0.0);
|
||||||
return Generic;
|
setMemoryTotal(0.0);
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZShell::services
|
} // namespace ZShell::services
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tickingservice.hpp"
|
#include "tickingservice.hpp"
|
||||||
|
#include "config/enums.hpp"
|
||||||
|
|
||||||
#include <qprocess.h>
|
#include <qprocess.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
|
using GpuType = config::GpuType::Enum;
|
||||||
|
|
||||||
|
struct VramFiles {
|
||||||
|
QString used;
|
||||||
|
QString total;
|
||||||
|
};
|
||||||
|
|
||||||
class Gpu : public TickingService {
|
class Gpu : public TickingService {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
public:
|
Q_PROPERTY(GpuType type READ type NOTIFY typeChanged)
|
||||||
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(QString name READ name NOTIFY nameChanged)
|
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 percentage READ percentage NOTIFY percentageChanged)
|
||||||
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
|
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
|
||||||
Q_PROPERTY(qreal memoryUsed READ memoryUsed NOTIFY memoryUsedChanged)
|
Q_PROPERTY(qreal memoryUsed READ memoryUsed NOTIFY memoryUsedChanged)
|
||||||
@@ -33,11 +31,11 @@ class Gpu : public TickingService {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Gpu(QObject* parent = nullptr);
|
explicit Gpu(QObject* parent = nullptr);
|
||||||
|
~Gpu() override;
|
||||||
|
|
||||||
[[nodiscard]] Type type() const;
|
[[nodiscard]] GpuType type() const;
|
||||||
[[nodiscard]] Type userType() const;
|
|
||||||
[[nodiscard]] Type autoType() const;
|
|
||||||
[[nodiscard]] QString name() const;
|
[[nodiscard]] QString name() const;
|
||||||
|
[[nodiscard]] bool detecting() const;
|
||||||
[[nodiscard]] qreal percentage() const;
|
[[nodiscard]] qreal percentage() const;
|
||||||
[[nodiscard]] qreal temperature() const;
|
[[nodiscard]] qreal temperature() const;
|
||||||
[[nodiscard]] qreal memoryUsed() const;
|
[[nodiscard]] qreal memoryUsed() const;
|
||||||
@@ -45,9 +43,8 @@ class Gpu : public TickingService {
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void typeChanged();
|
void typeChanged();
|
||||||
void userTypeChanged();
|
|
||||||
void autoTypeChanged();
|
|
||||||
void nameChanged();
|
void nameChanged();
|
||||||
|
void detectingChanged();
|
||||||
void percentageChanged();
|
void percentageChanged();
|
||||||
void temperatureChanged();
|
void temperatureChanged();
|
||||||
void memoryUsedChanged();
|
void memoryUsedChanged();
|
||||||
@@ -55,35 +52,52 @@ class Gpu : public TickingService {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void tick() override;
|
void tick() override;
|
||||||
|
void onStopped() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void detectTypeOnce();
|
void resolveGpu();
|
||||||
void detectNameOnce();
|
|
||||||
|
[[nodiscard]] int probeEnd() const;
|
||||||
|
|
||||||
|
void tryNameSource(int index, int generation);
|
||||||
|
void finishNameSource(int index, int generation, QString name);
|
||||||
|
|
||||||
void readGenericUsage();
|
void readGenericUsage();
|
||||||
|
void stopNvidiaUsage();
|
||||||
void startNvidiaUsage();
|
void startNvidiaUsage();
|
||||||
void readGpuTemperature();
|
void readGpuTemperature();
|
||||||
void readGenericMemory();
|
void readGenericMemory();
|
||||||
|
void resetUsage();
|
||||||
|
|
||||||
void setUserType(Type value);
|
void runProcess(
|
||||||
void setAutoType(Type value);
|
const QString& program,
|
||||||
|
const QStringList& args,
|
||||||
|
std::function<void(const QByteArray&)> callback);
|
||||||
|
|
||||||
|
void setType(GpuType value);
|
||||||
void setName(QString value);
|
void setName(QString value);
|
||||||
|
void setDetecting(bool value);
|
||||||
void setMemoryUsed(qreal value);
|
void setMemoryUsed(qreal value);
|
||||||
void setMemoryTotal(qreal value);
|
void setMemoryTotal(qreal value);
|
||||||
|
|
||||||
[[nodiscard]] static Type parseType(const QString& s);
|
void refreshSysfsCache();
|
||||||
[[nodiscard]] static QString cleanName(QString s);
|
|
||||||
|
|
||||||
Type m_userType = Auto;
|
GpuType m_userType = GpuType::Auto;
|
||||||
Type m_autoType = None;
|
GpuType m_type = GpuType::None;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
|
bool m_detecting = false;
|
||||||
qreal m_percentage = 0.0;
|
qreal m_percentage = 0.0;
|
||||||
qreal m_temperature = 0.0;
|
qreal m_temperature = 0.0;
|
||||||
qreal m_memoryUsed = 0.0;
|
qreal m_memoryUsed = 0.0;
|
||||||
qreal m_memoryTotal = 0.0;
|
qreal m_memoryTotal = 0.0;
|
||||||
|
|
||||||
QProcess* m_typeProc = nullptr;
|
QStringList m_busyFiles;
|
||||||
QProcess* m_nameProc = nullptr;
|
QList<VramFiles> m_vramFiles;
|
||||||
|
int m_generation = 0;
|
||||||
|
bool m_nvidiaQuerying = false;
|
||||||
|
|
||||||
QProcess* m_nvidiaProc = nullptr;
|
QProcess* m_nvidiaProc = nullptr;
|
||||||
|
QTimer* m_restartTimer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::services
|
} // namespace ZShell::services
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ void TickingService::start() {
|
|||||||
void TickingService::stop() {
|
void TickingService::stop() {
|
||||||
m_running = false;
|
m_running = false;
|
||||||
m_timer->stop();
|
m_timer->stop();
|
||||||
|
onStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TickingService::applyInterval(int ms) {
|
void TickingService::applyInterval(int ms) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class TickingService : public Service {
|
|||||||
void stop() final;
|
void stop() final;
|
||||||
|
|
||||||
virtual void tick() = 0;
|
virtual void tick() = 0;
|
||||||
|
virtual void onStopped() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void applyInterval(int ms);
|
void applyInterval(int ms);
|
||||||
|
|||||||
Reference in New Issue
Block a user