more unfinished changes, process tab for dashboard

This commit is contained in:
2026-09-30 14:26:50 +02:00
parent 0bfda04e91
commit 359f832609
98 changed files with 8633 additions and 2983 deletions
+2
View File
@@ -18,6 +18,7 @@ qml_module(ZShell-services
memory.hpp memory.cpp
diskinfo.hpp diskinfo.cpp
storage.hpp storage.cpp
processes.hpp processes.cpp
lyriccandidate.hpp lyriccandidate.cpp
lyrics.hpp lyrics.cpp
LIBRARIES
@@ -28,6 +29,7 @@ qml_module(ZShell-services
PkgConfig::Cava
Sensors::Sensors
ZShell-config
ZShell-settings
DEPENDENCIES
ZShell.Config
)
+5 -5
View File
@@ -7,7 +7,7 @@
#include <qsavefile.h>
#include <qurlquery.h>
#include "config/config.hpp"
#include "config/rootnodes.hpp"
#include "config/general.hpp"
#include "config/services.hpp"
@@ -75,7 +75,7 @@ Lyrics::Lyrics(QObject* parent)
m_loadDebounce->setInterval(k_loadDebounceMs);
QObject::connect(m_loadDebounce, &QTimer::timeout, this, &Lyrics::doLoad);
const auto* cfg = config::Config::instance();
const auto* cfg = config::ConfigSingleton::instance();
const auto* svcCfg = cfg->services();
const auto* genCfg = cfg->general();
@@ -114,7 +114,7 @@ void Lyrics::setPreferredBackend(LyricsBackend value) {
m_preferredBackend = value;
emit preferredBackendChanged();
config::Config::instance()->services()->set_lyricsBackend(value);
config::ConfigSingleton::instance()->services()->set_lyricsBackend(value);
scheduleLoad();
}
@@ -870,7 +870,7 @@ QNetworkReply* Lyrics::getJson(
void Lyrics::onPreferredBackendConfigChanged() {
const LyricsBackend desired =
config::Config::instance()->services()->lyricsBackend();
config::ConfigSingleton::instance()->services()->lyricsBackend();
if (desired == m_preferredBackend) {
return;
}
@@ -942,7 +942,7 @@ void Lyrics::persistTrackPrefs() {
}
QString Lyrics::lyricsDir() {
QString dir = config::Config::instance()->general()->lyricsDir();
QString dir = config::ConfigSingleton::instance()->general()->lyricsDir();
if (dir.isEmpty()) {
return {};
}
+53 -1
View File
@@ -3,6 +3,8 @@
#include <qfile.h>
#include <qregularexpression.h>
#include <unistd.h>
namespace ZShell::services {
Memory::Memory(QObject* parent) : TickingService(parent) {}
@@ -15,6 +17,18 @@ qreal Memory::total() const {
return m_total;
}
qreal Memory::cached() const {
return m_cached;
}
qreal Memory::readTotal() const {
return m_readTotal;
}
qreal Memory::writeTotal() const {
return m_writeTotal;
}
qreal Memory::percentage() const {
return m_total > 0.0 ? m_used / m_total : 0.0;
}
@@ -31,6 +45,8 @@ void Memory::tick() {
QStringLiteral("MemTotal: *(\\d+)"));
static const QRegularExpression reAvail(
QStringLiteral("MemAvailable: *(\\d+)"));
static const QRegularExpression reCached(QStringLiteral(
"Cached: *(\\d+)"));
const QString text = QString::fromLatin1(data);
const auto totalMatch = reTotal.match(text);
@@ -45,14 +61,50 @@ void Memory::tick() {
return;
}
const quint64 usedKib = totalKib > availKib ? totalKib - availKib : 0;
const auto cachedMatch = reCached.match(text);
const quint64 cachedKib =
cachedMatch.hasMatch() ? cachedMatch.captured(1).toULongLong() : 0;
if (totalKib == m_lastTotal && usedKib == m_lastUsed) {
static const QRegularExpression rePgin(QStringLiteral("pgpgin (\\d+)"));
static const QRegularExpression rePgout(QStringLiteral("pgpgout (\\d+)"));
quint64 readKib = 0;
quint64 writeKib = 0;
{
QFile vf(QStringLiteral("/proc/vmstat"));
if (vf.open(QIODevice::ReadOnly | QIODevice::Text)) {
const QString vtext = QString::fromLatin1(vf.readAll());
vf.close();
const double pageKib = getpagesize() / 1024.0;
const auto pgin = rePgin.match(vtext);
const auto pgout = rePgout.match(vtext);
if (pgin.hasMatch()) {
readKib = static_cast<quint64>(static_cast<double>(
pgin.captured(1).toULongLong()) *
pageKib);
}
if (pgout.hasMatch()) {
writeKib = static_cast<quint64>(static_cast<double>(
pgout.captured(1).toULongLong()) *
pageKib);
}
}
}
if (totalKib == m_lastTotal && usedKib == m_lastUsed &&
cachedKib == m_lastCached && readKib == m_lastReadKib &&
writeKib == m_lastWriteKib) {
return;
}
m_lastTotal = totalKib;
m_lastUsed = usedKib;
m_lastCached = cachedKib;
m_lastReadKib = readKib;
m_lastWriteKib = writeKib;
m_total = static_cast<qreal>(totalKib);
m_used = static_cast<qreal>(usedKib);
m_cached = static_cast<qreal>(cachedKib);
m_readTotal = static_cast<qreal>(readKib);
m_writeTotal = static_cast<qreal>(writeKib);
Q_EMIT changed();
}
+12
View File
@@ -14,14 +14,20 @@ class Memory : public TickingService {
Q_PROPERTY(qreal used READ used NOTIFY changed)
Q_PROPERTY(qreal total READ total NOTIFY changed)
Q_PROPERTY(qreal cached READ cached NOTIFY changed)
Q_PROPERTY(qreal percentage READ percentage NOTIFY changed)
Q_PROPERTY(qreal readTotal READ readTotal NOTIFY changed)
Q_PROPERTY(qreal writeTotal READ writeTotal NOTIFY changed)
public:
explicit Memory(QObject* parent = nullptr);
[[nodiscard]] qreal used() const;
[[nodiscard]] qreal total() const;
[[nodiscard]] qreal cached() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal readTotal() const;
[[nodiscard]] qreal writeTotal() const;
signals:
void changed();
@@ -32,8 +38,14 @@ class Memory : public TickingService {
private:
qreal m_used = 0.0;
qreal m_total = 1.0;
qreal m_cached = 0.0;
qreal m_readTotal = 0.0;
qreal m_writeTotal = 0.0;
quint64 m_lastUsed = 0;
quint64 m_lastTotal = 0;
quint64 m_lastCached = 0;
quint64 m_lastReadKib = 0;
quint64 m_lastWriteKib = 0;
};
} // namespace ZShell::services
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
#pragma once
#include "tickingservice.hpp"
#include <optional>
#include <qabstractitemmodel.h>
#include <qhash.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include <sys/types.h>
template <typename T> class QFutureWatcher;
namespace ZShell::services {
class ProcessModel : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
enum Roles : quint16 {
PidRole = Qt::UserRole + 1,
PpidRole,
CommandRole,
CommandLineRole,
StateRole,
SleepingRole,
CpuUsageRole,
MemoryUsageRole,
ElapsedRole,
UidRole,
UserRole,
IoReadBytesRole,
IoWriteBytesRole,
IconNameRole,
};
Q_ENUM(Roles)
explicit ProcessModel(QObject* parent = nullptr);
[[nodiscard]] int rowCount(
const QModelIndex& parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(
const QModelIndex& index, int role) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] int count() const;
void refresh();
bool killProcess(qint64 pid, int signal = 15) const;
void setDesktopEntryIcons(
QHash<QString, QString> execPathToIcon,
QHash<QString, QString> execNameToIcon);
[[nodiscard]] QString searchText() const;
void setSearchText(const QString& text);
[[nodiscard]] bool currentUserOnly() const;
void setCurrentUserOnly(bool enabled);
[[nodiscard]] int sortField() const;
void setSortField(int field);
[[nodiscard]] bool sortAscending() const;
void setSortAscending(bool ascending);
signals:
void countChanged();
void searchTextChanged();
void currentUserOnlyChanged();
void sortFieldChanged();
void sortAscendingChanged();
private:
struct RawStat {
pid_t pid = 0;
pid_t ppid = 0;
QString comm;
char state = '?';
quint64 utime = 0;
quint64 stime = 0;
quint64 starttime = 0;
};
struct ProcessEntry {
pid_t pid = 0;
pid_t ppid = 0;
QString command;
QString commandLine;
char state = '?';
double cpuPercent = 0.0;
quint64 rssKb = 0;
double elapsedSecs = 0.0;
uid_t uid = 0;
QString username;
QString iconName;
quint64 ioReadBytes = 0;
quint64 ioWriteBytes = 0;
int searchScore = -1;
quint64 lastUtime = 0;
quint64 lastStime = 0;
quint64 starttime = 0;
};
struct StatusInfo {
quint64 rssKb = 0;
uid_t uid = 0;
};
struct IoInfo {
quint64 readBytes = 0;
quint64 writeBytes = 0;
};
struct ScanResult {
QVector<ProcessEntry> entries;
QHash<uid_t, QString> newUidNames;
quint64 iconVersion = 0;
};
static std::optional<RawStat> readStat(pid_t pid);
static QString readCommandLine(pid_t pid, const QString& fallbackComm);
static StatusInfo readStatus(pid_t pid);
static IoInfo readIo(pid_t pid);
static std::optional<double> readUptimeSeconds();
static QString resolveUsername(uid_t uid, QHash<uid_t, QString>& cache);
static QString resolveProcessExe(pid_t pid);
static ScanResult scanProcesses(
double uptime,
double deltaSecs,
QHash<pid_t, int> prevPidToAll,
QVector<ProcessEntry> prevAll,
QHash<uid_t, QString> uidCache,
long clockTicksPerSec,
QHash<QString, QString> execPathToIcon,
QHash<QString, QString> execNameToIcon,
QStringList pathDirs,
bool desktopEntryIconsChanged,
quint64 iconVersion);
void doRefresh();
void onScanFinished();
void applyView();
void converge(const QVector<ProcessEntry>& desired);
[[nodiscard]] int compareEntries(
const ProcessEntry& a, const ProcessEntry& b) const;
[[nodiscard]] static bool isSleeping(const ProcessEntry& e);
QVector<ProcessEntry> mAll;
QHash<pid_t, int> mPidToAll;
QVector<ProcessEntry> mProcesses;
QHash<uid_t, QString> mUidToName;
double mLastUptimeSecs = -1.0;
long mClockTicksPerSec = 100;
QFutureWatcher<ScanResult>* mWatcher = nullptr;
bool mRefreshPending = false;
double mPendingUptime = -1.0;
QString m_searchText;
bool m_currentUserOnly = true;
uid_t m_currentUid = 0;
int m_sortField = 0;
bool m_sortAscending = true;
QHash<QString, QString> mExecPathToIcon;
QHash<QString, QString> mExecNameToIcon;
quint64 m_iconVersion = 0;
quint64 m_scannedIconVersion = 0;
};
class Processes : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
enum SortField : int {
Unsorted = 0,
Pid,
Ppid,
Command,
CommandLine,
State,
Sleeping,
CpuUsage,
MemoryUsage,
Elapsed,
Uid,
User,
IoReadBytes,
IoWriteBytes
};
Q_ENUM(SortField)
private:
Q_PROPERTY(QAbstractItemModel* list READ list CONSTANT)
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(
SortField sortField READ sortField WRITE setSortField NOTIFY
sortFieldChanged)
Q_PROPERTY(
bool sortAscending READ sortAscending WRITE setSortAscending NOTIFY
sortAscendingChanged)
Q_PROPERTY(
QString searchText READ searchText WRITE setSearchText NOTIFY
searchTextChanged)
Q_PROPERTY(
bool currentUserOnly READ currentUserOnly WRITE setCurrentUserOnly
NOTIFY currentUserOnlyChanged)
public:
explicit Processes(QObject* parent = nullptr);
[[nodiscard]] QAbstractItemModel* list() const;
[[nodiscard]] int count() const;
[[nodiscard]] SortField sortField() const;
void setSortField(SortField field);
[[nodiscard]] bool sortAscending() const;
void setSortAscending(bool ascending);
[[nodiscard]] QString searchText() const;
void setSearchText(const QString& text);
[[nodiscard]] bool currentUserOnly() const;
void setCurrentUserOnly(bool enabled);
Q_INVOKABLE void sortBy(SortField field);
Q_INVOKABLE void refresh();
Q_INVOKABLE bool killProcess(qint64 pid, int signal = 15) const;
Q_INVOKABLE void setDesktopEntries(const QVariantList& entries);
signals:
void countChanged();
void sortFieldChanged();
void sortAscendingChanged();
void searchTextChanged();
void currentUserOnlyChanged();
protected:
void tick() override;
private:
ProcessModel* m_model;
};
} // namespace ZShell::services
+4 -1
View File
@@ -12,7 +12,10 @@ namespace ZShell::services {
TickingService::TickingService(QObject* parent)
: Service(parent), m_timer(new QTimer(this)) {
m_timer->setSingleShot(false);
QObject::connect(m_timer, &QTimer::timeout, this, [this] { tick(); });
QObject::connect(m_timer, &QTimer::timeout, this, [this] {
tick();
updateTriggered();
});
QString configPath =
QDir::homePath() + QStringLiteral("/.config/zshell/config.json");
@@ -18,6 +18,7 @@ class TickingService : public Service {
signals:
void updateIntervalChanged();
void updateTriggered();
protected:
void start() final;
+18
View File
@@ -14,6 +14,24 @@ bool finitePositive(qreal v) {
namespace ZShell::services::usagefmt {
FormatResult UsageFmt::formatKib(qreal kib) const {
if (!finitePositive(kib)) {
return {0.0, 0.0, "KiB"};
}
if (kib >= kGib) {
return {kib / kGib, 0.0, "GiB"};
}
if (kib >= kMib) {
return {kib / kMib, 0.0, "MiB"};
}
if (kib >= kKib) {
return {kib / kKib, 0.0, "KiB"};
}
return {kib, 0.0, "KiB"};
}
FormatResult UsageFmt::formatKib(qreal kib, qreal total) const {
if (!finitePositive(kib) || !finitePositive(total)) {
return {0.0, 0.0, "KiB"};
+2
View File
@@ -1,5 +1,6 @@
#pragma once
#include <qaccessible_base.h>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qqmlintegration.h>
@@ -27,6 +28,7 @@ class UsageFmt : public QObject {
QML_SINGLETON
public:
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(qreal kib) const;
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(
qreal kib, qreal total) const;
};