#pragma once #include "diskinfo.hpp" #include "tickingservice.hpp" #include #include #include #include #include #include #include #include template class QFutureWatcher; namespace ZShell::services { struct StorageEntry { // Key for the entry: sysfs block device name, or mount point for a mount with // no backing block device (network mount). This is what DiskInfo reports as // its mount, so the UI can show either. QString mount; quint64 usedBytes = 0; quint64 totalBytes = 0; bool hasRoot = false; }; using StorageScan = QVector; class Storage : public TickingService { Q_OBJECT QML_ELEMENT QML_SINGLETON Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged) Q_PROPERTY( QQmlListProperty disks READ disksProp NOTIFY disksChanged) Q_PROPERTY( ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk WRITE setManualPrimaryDisk NOTIFY manualPrimaryDiskChanged) Q_PROPERTY( ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY primaryDiskChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) public: explicit Storage(QObject* parent = nullptr); [[nodiscard]] qreal percentage() const; [[nodiscard]] QQmlListProperty disksProp(); [[nodiscard]] DiskInfo* manualPrimaryDisk() const; void setManualPrimaryDisk(DiskInfo* disk); [[nodiscard]] DiskInfo* primaryDisk() const; [[nodiscard]] bool loading() const; signals: void disksChanged(); void percentageChanged(); void manualPrimaryDiskChanged(); void primaryDiskChanged(); void loadingChanged(); protected: void tick() override; private: [[nodiscard]] static QStringList resolveToPhysicalDisks( const QString& devicePath); [[nodiscard]] static bool isPseudoFs(QByteArrayView fsType); [[nodiscard]] static bool sameOrder( const QList& a, const QList& b); static qsizetype disksCount(QQmlListProperty* prop); static DiskInfo* disksAt(QQmlListProperty* prop, qsizetype i); // Runs on a worker thread. statvfs on a mount whose backing storage is // unreachable can block for the whole network timeout, so the scan must not // run on the GUI thread. The result is plain data, applied on this object's // thread once it arrives. [[nodiscard]] static StorageScan scanVolumes(); void beginScan(); void onScanFinished(); void applyScan(const StorageScan& scan); void setLoading(bool value); QList m_disks; QPointer m_manualPrimaryDisk; QFutureWatcher* m_watcher = nullptr; QTimer m_loadingTimer; bool m_loading = false; }; } // namespace ZShell::services