more unfinished changes, process tab for dashboard
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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"};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user