mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(sysmon): added classic (no nvidia) gpu_temp in bar and desktop widgets + some settings polishing with "visibleWhen"
This commit is contained in:
@@ -917,6 +917,7 @@
|
||||
"always": "Always",
|
||||
"cpu-temp": "CPU Temperature",
|
||||
"cpu-usage": "CPU Usage",
|
||||
"gpu-temp": "GPU Temperature",
|
||||
"disk-percent": "Disk Percent",
|
||||
"full": "Full",
|
||||
"gauge": "Gauge",
|
||||
|
||||
@@ -304,6 +304,8 @@ std::unique_ptr<Widget> WidgetFactory::create(const std::string& name, wl_output
|
||||
SysmonStat stat = SysmonStat::CpuUsage;
|
||||
if (statStr == "cpu_temp") {
|
||||
stat = SysmonStat::CpuTemp;
|
||||
} else if (statStr == "gpu_temp") {
|
||||
stat = SysmonStat::GpuTemp;
|
||||
} else if (statStr == "ram_used") {
|
||||
stat = SysmonStat::RamUsed;
|
||||
} else if (statStr == "ram_pct") {
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace {
|
||||
constexpr auto kInitialSampleRetryDelay = std::chrono::milliseconds(250);
|
||||
|
||||
bool needsCpuTemp(SysmonStat stat) { return stat == SysmonStat::CpuTemp; }
|
||||
bool needsGpuTemp(SysmonStat stat) { return stat == SysmonStat::GpuTemp; }
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -44,6 +45,9 @@ SysmonWidget::SysmonWidget(SystemMonitorService* monitor, wl_output* output, Sys
|
||||
if (needsCpuTemp(m_stat)) {
|
||||
m_monitor->retainCpuTemp();
|
||||
}
|
||||
if (needsGpuTemp(m_stat)) {
|
||||
m_monitor->retainGpuTemp();
|
||||
}
|
||||
if (m_stat == SysmonStat::DiskPct && !m_diskPath.empty()) {
|
||||
m_monitor->retainDiskPath(m_diskPath);
|
||||
}
|
||||
@@ -55,6 +59,9 @@ SysmonWidget::~SysmonWidget() {
|
||||
if (needsCpuTemp(m_stat)) {
|
||||
m_monitor->releaseCpuTemp();
|
||||
}
|
||||
if (needsGpuTemp(m_stat)) {
|
||||
m_monitor->releaseGpuTemp();
|
||||
}
|
||||
if (m_stat == SysmonStat::DiskPct && !m_diskPath.empty()) {
|
||||
m_monitor->releaseDiskPath(m_diskPath);
|
||||
}
|
||||
@@ -412,6 +419,23 @@ double SysmonWidget::normalizedFromStats(SysmonStat stat, const SystemStats& sta
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
case SysmonStat::GpuTemp:
|
||||
if (stats.gpuTempC.has_value()) {
|
||||
const double temp = *stats.gpuTempC;
|
||||
if (temp < tempMin) {
|
||||
tempMin = temp;
|
||||
}
|
||||
if (temp > tempMax) {
|
||||
tempMax = temp;
|
||||
}
|
||||
const double range = tempMax - tempMin;
|
||||
if (range <= 0.0) {
|
||||
return 0.5;
|
||||
}
|
||||
return std::clamp((temp - tempMin) / range, 0.0, 1.0);
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
case SysmonStat::RamUsed:
|
||||
if (stats.ramTotalMb > 0) {
|
||||
return static_cast<double>(stats.ramUsedMb) / static_cast<double>(stats.ramTotalMb);
|
||||
@@ -466,6 +490,12 @@ std::string SysmonWidget::formatValue() const {
|
||||
}
|
||||
return "--";
|
||||
|
||||
case SysmonStat::GpuTemp:
|
||||
if (stats.gpuTempC.has_value()) {
|
||||
return std::format("{:.0f}°C", *stats.gpuTempC);
|
||||
}
|
||||
return "--";
|
||||
|
||||
case SysmonStat::RamUsed:
|
||||
if (stats.ramUsedMb >= 1024) {
|
||||
return std::format("{:.1f}G", static_cast<double>(stats.ramUsedMb) / 1024.0);
|
||||
@@ -495,6 +525,8 @@ const char* SysmonWidget::glyphName(SysmonStat stat) {
|
||||
return "cpu-usage";
|
||||
case SysmonStat::CpuTemp:
|
||||
return "cpu-temperature";
|
||||
case SysmonStat::GpuTemp:
|
||||
return "temperature";
|
||||
case SysmonStat::RamUsed:
|
||||
case SysmonStat::RamPct:
|
||||
return "memory";
|
||||
|
||||
@@ -15,7 +15,7 @@ class SystemMonitorService;
|
||||
struct SystemStats;
|
||||
struct wl_output;
|
||||
|
||||
enum class SysmonStat { CpuUsage, CpuTemp, RamUsed, RamPct, SwapPct, DiskPct };
|
||||
enum class SysmonStat { CpuUsage, CpuTemp, GpuTemp, RamUsed, RamPct, SwapPct, DiskPct };
|
||||
enum class SysmonDisplayMode { Text, Graph, Gauge };
|
||||
|
||||
class SysmonWidget : public Widget {
|
||||
|
||||
@@ -175,6 +175,8 @@ DesktopWidgetFactory::create(const std::string& type,
|
||||
auto parseStat = [](const std::string& s) -> DesktopSysmonStat {
|
||||
if (s == "cpu_temp")
|
||||
return DesktopSysmonStat::CpuTemp;
|
||||
if (s == "gpu_temp")
|
||||
return DesktopSysmonStat::GpuTemp;
|
||||
if (s == "ram_pct")
|
||||
return DesktopSysmonStat::RamPct;
|
||||
if (s == "swap_pct")
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace {
|
||||
const auto kSampleInterval = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::seconds(1));
|
||||
|
||||
bool needsCpuTemp(DesktopSysmonStat stat) { return stat == DesktopSysmonStat::CpuTemp; }
|
||||
bool needsGpuTemp(DesktopSysmonStat stat) { return stat == DesktopSysmonStat::GpuTemp; }
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -31,8 +32,12 @@ DesktopSysmonWidget::DesktopSysmonWidget(SystemMonitorService* monitor, DesktopS
|
||||
if (m_monitor != nullptr) {
|
||||
if (needsCpuTemp(m_stat))
|
||||
m_monitor->retainCpuTemp();
|
||||
if (needsGpuTemp(m_stat))
|
||||
m_monitor->retainGpuTemp();
|
||||
if (m_stat2.has_value() && needsCpuTemp(*m_stat2))
|
||||
m_monitor->retainCpuTemp();
|
||||
if (m_stat2.has_value() && needsGpuTemp(*m_stat2))
|
||||
m_monitor->retainGpuTemp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +45,12 @@ DesktopSysmonWidget::~DesktopSysmonWidget() {
|
||||
if (m_monitor != nullptr) {
|
||||
if (needsCpuTemp(m_stat))
|
||||
m_monitor->releaseCpuTemp();
|
||||
if (needsGpuTemp(m_stat))
|
||||
m_monitor->releaseGpuTemp();
|
||||
if (m_stat2.has_value() && needsCpuTemp(*m_stat2))
|
||||
m_monitor->releaseCpuTemp();
|
||||
if (m_stat2.has_value() && needsGpuTemp(*m_stat2))
|
||||
m_monitor->releaseGpuTemp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +204,20 @@ double DesktopSysmonWidget::normalizedFromStats(DesktopSysmonStat stat, const Sy
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
case DesktopSysmonStat::GpuTemp:
|
||||
if (stats.gpuTempC.has_value()) {
|
||||
const double temp = *stats.gpuTempC;
|
||||
if (temp < tempMin)
|
||||
tempMin = temp;
|
||||
if (temp > tempMax)
|
||||
tempMax = temp;
|
||||
const double range = tempMax - tempMin;
|
||||
if (range <= 0.0)
|
||||
return 0.5;
|
||||
return std::clamp((temp - tempMin) / range, 0.0, 1.0);
|
||||
}
|
||||
return 0.0;
|
||||
|
||||
case DesktopSysmonStat::RamPct:
|
||||
return stats.ramUsagePercent / 100.0;
|
||||
|
||||
@@ -225,6 +248,12 @@ std::string DesktopSysmonWidget::formatValueFor(DesktopSysmonStat stat) const {
|
||||
}
|
||||
return "--";
|
||||
|
||||
case DesktopSysmonStat::GpuTemp:
|
||||
if (stats.gpuTempC.has_value()) {
|
||||
return std::format("{:.0f}°C", *stats.gpuTempC);
|
||||
}
|
||||
return "--";
|
||||
|
||||
case DesktopSysmonStat::RamPct:
|
||||
return std::format("{:.0f}%", stats.ramUsagePercent);
|
||||
|
||||
@@ -323,6 +352,8 @@ const char* DesktopSysmonWidget::glyphName(DesktopSysmonStat stat) {
|
||||
return "cpu-usage";
|
||||
case DesktopSysmonStat::CpuTemp:
|
||||
return "cpu-temperature";
|
||||
case DesktopSysmonStat::GpuTemp:
|
||||
return "temperature";
|
||||
case DesktopSysmonStat::RamPct:
|
||||
return "memory";
|
||||
case DesktopSysmonStat::SwapPct:
|
||||
|
||||
@@ -15,7 +15,7 @@ class GraphNode;
|
||||
class Label;
|
||||
class SystemMonitorService;
|
||||
|
||||
enum class DesktopSysmonStat : std::uint8_t { CpuUsage, CpuTemp, RamPct, SwapPct };
|
||||
enum class DesktopSysmonStat : std::uint8_t { CpuUsage, CpuTemp, GpuTemp, RamPct, SwapPct };
|
||||
|
||||
class DesktopSysmonWidget : public DesktopWidget {
|
||||
public:
|
||||
|
||||
@@ -566,6 +566,47 @@ namespace settings {
|
||||
return spec.defaultValue;
|
||||
}
|
||||
|
||||
std::string settingCurrentString(const Config& cfg, std::string_view widgetName, const std::string& key,
|
||||
const std::vector<WidgetSettingSpec>& allSpecs) {
|
||||
if (const auto it = cfg.widgets.find(std::string(widgetName)); it != cfg.widgets.end()) {
|
||||
if (const auto settingIt = it->second.settings.find(key); settingIt != it->second.settings.end()) {
|
||||
if (const auto* s = std::get_if<std::string>(&settingIt->second)) {
|
||||
return *s;
|
||||
}
|
||||
if (const auto* b = std::get_if<bool>(&settingIt->second)) {
|
||||
return *b ? "true" : "false";
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& s : allSpecs) {
|
||||
if (s.key == key) {
|
||||
if (const auto* str = std::get_if<std::string>(&s.defaultValue)) {
|
||||
return *str;
|
||||
}
|
||||
if (const auto* b = std::get_if<bool>(&s.defaultValue)) {
|
||||
return *b ? "true" : "false";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isSettingVisible(const Config& cfg, std::string_view widgetName, const WidgetSettingSpec& spec,
|
||||
const std::vector<WidgetSettingSpec>& allSpecs) {
|
||||
if (!spec.visibleWhen.has_value()) {
|
||||
return true;
|
||||
}
|
||||
const auto& cond = *spec.visibleWhen;
|
||||
const auto currentValue = settingCurrentString(cfg, widgetName, cond.key, allSpecs);
|
||||
for (const auto& v : cond.values) {
|
||||
if (v == currentValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool settingValueAsBool(const WidgetSettingValue& value) {
|
||||
if (const auto* v = std::get_if<bool>(&value)) {
|
||||
return *v;
|
||||
@@ -871,6 +912,9 @@ namespace settings {
|
||||
if (spec.key == "capsule_group" && managedCapsuleGroups.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (!isSettingVisible(ctx.config, widgetName, spec, specs)) {
|
||||
continue;
|
||||
}
|
||||
if (spec.advanced && !ctx.showAdvanced) {
|
||||
continue;
|
||||
}
|
||||
@@ -895,6 +939,7 @@ namespace settings {
|
||||
.control = TextSetting{},
|
||||
.advanced = spec.advanced,
|
||||
.searchText = {},
|
||||
.visibleWhen = std::nullopt,
|
||||
};
|
||||
|
||||
switch (spec.valueType) {
|
||||
@@ -1164,15 +1209,18 @@ namespace settings {
|
||||
inspector->addChild(std::move(groupRow));
|
||||
}
|
||||
|
||||
if (!currentLaneInherited && !currentLaneKey.empty()) {
|
||||
auto moveRow = std::make_unique<Flex>();
|
||||
moveRow->setDirection(FlexDirection::Horizontal);
|
||||
moveRow->setAlign(FlexAlign::Center);
|
||||
moveRow->setGap(Style::spaceXs * ctx.scale);
|
||||
const bool pendingDelete = guiManaged && ctx.pendingDeleteWidgetName == widgetName;
|
||||
const bool renaming = guiManaged && ctx.renamingWidgetName == widgetName;
|
||||
|
||||
auto moveSpacer = std::make_unique<Flex>();
|
||||
moveSpacer->setFlexGrow(1.0f);
|
||||
moveRow->addChild(std::move(moveSpacer));
|
||||
if (!pendingDelete && !renaming && !currentLaneInherited && !currentLaneKey.empty()) {
|
||||
auto actionRow = std::make_unique<Flex>();
|
||||
actionRow->setDirection(FlexDirection::Horizontal);
|
||||
actionRow->setAlign(FlexAlign::Center);
|
||||
actionRow->setGap(Style::spaceXs * ctx.scale);
|
||||
|
||||
auto actionSpacer = std::make_unique<Flex>();
|
||||
actionSpacer->setFlexGrow(1.0f);
|
||||
actionRow->addChild(std::move(actionSpacer));
|
||||
|
||||
for (const auto targetLane : kLaneKeys) {
|
||||
if (targetLane == currentLaneKey) {
|
||||
@@ -1200,17 +1248,48 @@ namespace settings {
|
||||
targetItems.push_back(widgetName);
|
||||
setOverrides({{sourcePath, sourceItems}, {targetPath, targetItems}});
|
||||
});
|
||||
moveRow->addChild(std::move(moveBtn));
|
||||
actionRow->addChild(std::move(moveBtn));
|
||||
}
|
||||
|
||||
inspector->addChild(std::move(moveRow));
|
||||
if (guiManaged) {
|
||||
auto renameBtn = std::make_unique<Button>();
|
||||
renameBtn->setText(i18n::tr("settings.entities.widget.instance.rename"));
|
||||
renameBtn->setVariant(ButtonVariant::Ghost);
|
||||
renameBtn->setFontSize(Style::fontSizeCaption * ctx.scale);
|
||||
renameBtn->setMinHeight(Style::controlHeightSm * ctx.scale);
|
||||
renameBtn->setPadding(Style::spaceXs * ctx.scale, Style::spaceSm * ctx.scale);
|
||||
renameBtn->setRadius(Style::radiusSm * ctx.scale);
|
||||
renameBtn->setOnClick(
|
||||
[&renamingWidgetName = ctx.renamingWidgetName, widgetName, requestRebuild = ctx.requestRebuild]() {
|
||||
renamingWidgetName = widgetName;
|
||||
requestRebuild();
|
||||
});
|
||||
actionRow->addChild(std::move(renameBtn));
|
||||
|
||||
auto deleteBtn = std::make_unique<Button>();
|
||||
deleteBtn->setGlyph("trash");
|
||||
deleteBtn->setText(i18n::tr("settings.entities.widget.instance.delete"));
|
||||
deleteBtn->setVariant(ButtonVariant::Ghost);
|
||||
deleteBtn->setFontSize(Style::fontSizeCaption * ctx.scale);
|
||||
deleteBtn->setGlyphSize(Style::fontSizeCaption * ctx.scale);
|
||||
deleteBtn->setMinHeight(Style::controlHeightSm * ctx.scale);
|
||||
deleteBtn->setPadding(Style::spaceXs * ctx.scale, Style::spaceSm * ctx.scale);
|
||||
deleteBtn->setRadius(Style::radiusSm * ctx.scale);
|
||||
deleteBtn->setOnClick([&pendingDeleteWidgetName = ctx.pendingDeleteWidgetName,
|
||||
&renamingWidgetName = ctx.renamingWidgetName, widgetName,
|
||||
requestRebuild = ctx.requestRebuild]() {
|
||||
pendingDeleteWidgetName = widgetName;
|
||||
renamingWidgetName.clear();
|
||||
requestRebuild();
|
||||
});
|
||||
actionRow->addChild(std::move(deleteBtn));
|
||||
}
|
||||
|
||||
inspector->addChild(std::move(actionRow));
|
||||
}
|
||||
|
||||
addWidgetSettingsPanel(*inspector, widgetName, managedCapsuleGroupOptions(ctx.config, currentLanePath), ctx);
|
||||
|
||||
const bool pendingDelete = guiManaged && ctx.pendingDeleteWidgetName == widgetName;
|
||||
const bool renaming = guiManaged && ctx.renamingWidgetName == widgetName;
|
||||
|
||||
if (renaming) {
|
||||
auto renameRow = std::make_unique<Flex>();
|
||||
renameRow->setDirection(FlexDirection::Horizontal);
|
||||
@@ -1339,57 +1418,6 @@ namespace settings {
|
||||
|
||||
confirmPanel->addChild(std::move(confirmRow));
|
||||
inspector->addChild(std::move(confirmPanel));
|
||||
} else if (guiManaged && !currentLaneInherited && !currentLaneKey.empty()) {
|
||||
auto actionRow = std::make_unique<Flex>();
|
||||
actionRow->setDirection(FlexDirection::Horizontal);
|
||||
actionRow->setAlign(FlexAlign::Center);
|
||||
actionRow->setGap(Style::spaceXs * ctx.scale);
|
||||
|
||||
auto actionSpacer = std::make_unique<Flex>();
|
||||
actionSpacer->setFlexGrow(1.0f);
|
||||
actionRow->addChild(std::move(actionSpacer));
|
||||
|
||||
if (guiManaged && !renaming) {
|
||||
auto renameBtn = std::make_unique<Button>();
|
||||
renameBtn->setText(i18n::tr("settings.entities.widget.instance.rename"));
|
||||
renameBtn->setVariant(ButtonVariant::Ghost);
|
||||
renameBtn->setFontSize(Style::fontSizeCaption * ctx.scale);
|
||||
renameBtn->setMinHeight(Style::controlHeightSm * ctx.scale);
|
||||
renameBtn->setPadding(Style::spaceXs * ctx.scale, Style::spaceSm * ctx.scale);
|
||||
renameBtn->setRadius(Style::radiusSm * ctx.scale);
|
||||
renameBtn->setOnClick(
|
||||
[&renamingWidgetName = ctx.renamingWidgetName, widgetName, requestRebuild = ctx.requestRebuild]() {
|
||||
renamingWidgetName = widgetName;
|
||||
requestRebuild();
|
||||
});
|
||||
actionRow->addChild(std::move(renameBtn));
|
||||
}
|
||||
|
||||
if (guiManaged) {
|
||||
auto deleteBtn = std::make_unique<Button>();
|
||||
deleteBtn->setGlyph("trash");
|
||||
deleteBtn->setText(i18n::tr("settings.entities.widget.instance.delete"));
|
||||
deleteBtn->setVariant(ButtonVariant::Ghost);
|
||||
deleteBtn->setFontSize(Style::fontSizeCaption * ctx.scale);
|
||||
deleteBtn->setGlyphSize(Style::fontSizeCaption * ctx.scale);
|
||||
deleteBtn->setMinHeight(Style::controlHeightSm * ctx.scale);
|
||||
deleteBtn->setPadding(Style::spaceXs * ctx.scale, Style::spaceSm * ctx.scale);
|
||||
deleteBtn->setRadius(Style::radiusSm * ctx.scale);
|
||||
deleteBtn->setOnClick([&pendingDeleteWidgetName = ctx.pendingDeleteWidgetName,
|
||||
&renamingWidgetName = ctx.renamingWidgetName, widgetName,
|
||||
requestRebuild = ctx.requestRebuild]() {
|
||||
pendingDeleteWidgetName = widgetName;
|
||||
renamingWidgetName.clear();
|
||||
requestRebuild();
|
||||
});
|
||||
actionRow->addChild(std::move(deleteBtn));
|
||||
}
|
||||
|
||||
if (actionRow->children().empty()) {
|
||||
// nothing to add — leave inspector without action row
|
||||
} else {
|
||||
inspector->addChild(std::move(actionRow));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string targetLaneKey;
|
||||
|
||||
@@ -1532,6 +1532,30 @@ namespace settings {
|
||||
const ListSetting& list) { makeListBlock(section, entry, list); },
|
||||
};
|
||||
|
||||
auto isEntryVisible = [&](const SettingEntry& e) -> bool {
|
||||
if (!e.visibleWhen.has_value()) {
|
||||
return true;
|
||||
}
|
||||
const auto& cond = *e.visibleWhen;
|
||||
for (const auto& other : registry) {
|
||||
if (other.path == cond.path) {
|
||||
std::string currentValue;
|
||||
if (const auto* toggle = std::get_if<ToggleSetting>(&other.control)) {
|
||||
currentValue = toggle->checked ? "true" : "false";
|
||||
} else if (const auto* select = std::get_if<SelectSetting>(&other.control)) {
|
||||
currentValue = select->selectedValue;
|
||||
}
|
||||
for (const auto& v : cond.values) {
|
||||
if (v == currentValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const auto& entry : registry) {
|
||||
if (ctx.searchQuery.empty() && !ctx.selectedSection.empty() && entry.section != ctx.selectedSection) {
|
||||
continue;
|
||||
@@ -1539,6 +1563,9 @@ namespace settings {
|
||||
if (!ctx.showAdvanced && entry.advanced) {
|
||||
continue;
|
||||
}
|
||||
if (!isEntryVisible(entry)) {
|
||||
continue;
|
||||
}
|
||||
if (ctx.showOverriddenOnly && ctx.configService != nullptr &&
|
||||
!ctx.configService->hasEffectiveOverride(entry.path)) {
|
||||
continue;
|
||||
|
||||
@@ -158,6 +158,7 @@ namespace settings {
|
||||
.control = std::move(control),
|
||||
.advanced = advanced,
|
||||
.searchText = lower(searchText),
|
||||
.visibleWhen = std::nullopt,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -942,28 +943,52 @@ namespace settings {
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.widget-capsules.label"),
|
||||
tr("settings.schema.bar.widget-capsules.description"), path("capsule"),
|
||||
ToggleSetting{selectedBar->widgetCapsuleDefault}, "pill"));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-groups.label"),
|
||||
tr("settings.schema.bar.capsule-groups.description"), path("capsule_groups"),
|
||||
ListSetting{.items = selectedBar->widgetCapsuleGroups}, "grouped capsules"));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-fill.label"),
|
||||
tr("settings.schema.bar.capsule-fill.description"), path("capsule_fill"),
|
||||
colorRolePicker(selectedBar->widgetCapsuleFill), "color role pill", true));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-foreground.label"),
|
||||
tr("settings.schema.bar.capsule-foreground.description"), path("capsule_foreground"),
|
||||
optionalColorRolePicker(selectedBar->widgetCapsuleForeground),
|
||||
"color role foreground pill", true));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-border.label"),
|
||||
tr("settings.schema.bar.capsule-border.description"), path("capsule_border"),
|
||||
capsuleBorderRolePicker(selectedBar->widgetCapsuleBorder), "color role pill outline",
|
||||
true));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-padding.label"),
|
||||
tr("settings.schema.bar.capsule-padding.description"), path("capsule_padding"),
|
||||
SliderSetting{selectedBar->widgetCapsulePadding, 0.0f, 48.0f, 1.0f, false},
|
||||
"pill inset", true));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-opacity.label"),
|
||||
tr("settings.schema.bar.capsule-opacity.description"), path("capsule_opacity"),
|
||||
SliderSetting{selectedBar->widgetCapsuleOpacity, 0.0f, 1.0f, 0.01f, false},
|
||||
"pill alpha", true));
|
||||
const SettingVisibility capsuleOn{path("capsule"), {"true"}};
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-groups.label"),
|
||||
tr("settings.schema.bar.capsule-groups.description"), path("capsule_groups"),
|
||||
ListSetting{.items = selectedBar->widgetCapsuleGroups}, "grouped capsules");
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-fill.label"),
|
||||
tr("settings.schema.bar.capsule-fill.description"), path("capsule_fill"),
|
||||
colorRolePicker(selectedBar->widgetCapsuleFill), "color role pill", true);
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-foreground.label"),
|
||||
tr("settings.schema.bar.capsule-foreground.description"), path("capsule_foreground"),
|
||||
optionalColorRolePicker(selectedBar->widgetCapsuleForeground), "color role foreground pill",
|
||||
true);
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-border.label"),
|
||||
tr("settings.schema.bar.capsule-border.description"), path("capsule_border"),
|
||||
capsuleBorderRolePicker(selectedBar->widgetCapsuleBorder), "color role pill outline", true);
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-padding.label"),
|
||||
tr("settings.schema.bar.capsule-padding.description"), path("capsule_padding"),
|
||||
SliderSetting{selectedBar->widgetCapsulePadding, 0.0f, 48.0f, 1.0f, false}, "pill inset", true);
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-opacity.label"),
|
||||
tr("settings.schema.bar.capsule-opacity.description"), path("capsule_opacity"),
|
||||
SliderSetting{selectedBar->widgetCapsuleOpacity, 0.0f, 1.0f, 0.01f, false}, "pill alpha", true);
|
||||
e.visibleWhen = capsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
entries.push_back(makeEntry(section, "widget-list", tr("settings.schema.bar.start-widgets.label"),
|
||||
tr("settings.schema.bar.start-widgets.description"), path("start"),
|
||||
ListSetting{.items = selectedBar->startWidgets}, "left"));
|
||||
@@ -1071,37 +1096,62 @@ namespace settings {
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.widget-capsules.label"),
|
||||
tr("settings.schema.bar.widget-capsules.description"), mpath("capsule"),
|
||||
ToggleSetting{ovr.widgetCapsuleDefault.value_or(bar.widgetCapsuleDefault)}, "pill"));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-groups.label"),
|
||||
tr("settings.schema.bar.capsule-groups.description"), mpath("capsule_groups"),
|
||||
ListSetting{.items = ovr.widgetCapsuleGroups.value_or(bar.widgetCapsuleGroups)},
|
||||
"grouped capsules"));
|
||||
entries.push_back(makeEntry(section, "capsules", tr("settings.schema.bar.capsule-fill.label"),
|
||||
tr("settings.schema.bar.capsule-fill.description"), mpath("capsule_fill"),
|
||||
colorRolePicker(ovr.widgetCapsuleFill.value_or(bar.widgetCapsuleFill)),
|
||||
"color role pill", true));
|
||||
entries.push_back(
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-foreground.label"),
|
||||
tr("settings.schema.bar.capsule-foreground.description"), mpath("capsule_foreground"),
|
||||
optionalColorRolePicker(ovr.widgetCapsuleForeground.has_value() ? ovr.widgetCapsuleForeground
|
||||
: bar.widgetCapsuleForeground),
|
||||
"color role foreground pill", true));
|
||||
entries.push_back(makeEntry(
|
||||
section, "capsules", tr("settings.schema.bar.capsule-border.label"),
|
||||
tr("settings.schema.bar.capsule-border.description"), mpath("capsule_border"),
|
||||
capsuleBorderRolePicker(ovr.widgetCapsuleBorderSpecified ? ovr.widgetCapsuleBorder : bar.widgetCapsuleBorder),
|
||||
"color role pill outline", true));
|
||||
entries.push_back(
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-padding.label"),
|
||||
tr("settings.schema.bar.capsule-padding.description"), mpath("capsule_padding"),
|
||||
SliderSetting{static_cast<float>(ovr.widgetCapsulePadding.value_or(bar.widgetCapsulePadding)), 0.0f,
|
||||
48.0f, 1.0f, false},
|
||||
"pill inset", true));
|
||||
entries.push_back(
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-opacity.label"),
|
||||
tr("settings.schema.bar.capsule-opacity.description"), mpath("capsule_opacity"),
|
||||
SliderSetting{static_cast<float>(ovr.widgetCapsuleOpacity.value_or(bar.widgetCapsuleOpacity)), 0.0f,
|
||||
1.0f, 0.01f, false},
|
||||
"pill alpha", true));
|
||||
const SettingVisibility mCapsuleOn{mpath("capsule"), {"true"}};
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-groups.label"),
|
||||
tr("settings.schema.bar.capsule-groups.description"), mpath("capsule_groups"),
|
||||
ListSetting{.items = ovr.widgetCapsuleGroups.value_or(bar.widgetCapsuleGroups)},
|
||||
"grouped capsules");
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-fill.label"),
|
||||
tr("settings.schema.bar.capsule-fill.description"), mpath("capsule_fill"),
|
||||
colorRolePicker(ovr.widgetCapsuleFill.value_or(bar.widgetCapsuleFill)), "color role pill", true);
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-foreground.label"),
|
||||
tr("settings.schema.bar.capsule-foreground.description"), mpath("capsule_foreground"),
|
||||
optionalColorRolePicker(ovr.widgetCapsuleForeground.has_value() ? ovr.widgetCapsuleForeground
|
||||
: bar.widgetCapsuleForeground),
|
||||
"color role foreground pill", true);
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e = makeEntry(section, "capsules", tr("settings.schema.bar.capsule-border.label"),
|
||||
tr("settings.schema.bar.capsule-border.description"), mpath("capsule_border"),
|
||||
capsuleBorderRolePicker(ovr.widgetCapsuleBorderSpecified ? ovr.widgetCapsuleBorder
|
||||
: bar.widgetCapsuleBorder),
|
||||
"color role pill outline", true);
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-padding.label"),
|
||||
tr("settings.schema.bar.capsule-padding.description"), mpath("capsule_padding"),
|
||||
SliderSetting{static_cast<float>(ovr.widgetCapsulePadding.value_or(bar.widgetCapsulePadding)),
|
||||
0.0f, 48.0f, 1.0f, false},
|
||||
"pill inset", true);
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
{
|
||||
auto e =
|
||||
makeEntry(section, "capsules", tr("settings.schema.bar.capsule-opacity.label"),
|
||||
tr("settings.schema.bar.capsule-opacity.description"), mpath("capsule_opacity"),
|
||||
SliderSetting{static_cast<float>(ovr.widgetCapsuleOpacity.value_or(bar.widgetCapsuleOpacity)),
|
||||
0.0f, 1.0f, 0.01f, false},
|
||||
"pill alpha", true);
|
||||
e.visibleWhen = mCapsuleOn;
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
entries.push_back(makeEntry(section, "widget-list", tr("settings.schema.bar.start-widgets.label"),
|
||||
tr("settings.schema.bar.start-widgets.description"), mpath("start"),
|
||||
ListSetting{.items = ovr.startWidgets.value_or(bar.startWidgets)}, "left"));
|
||||
|
||||
@@ -112,6 +112,11 @@ namespace settings {
|
||||
ListSetting, ShortcutListSetting, SessionPanelActionsSetting, ColorSetting,
|
||||
MultiSelectSetting, ButtonSetting, ColorRolePickerSetting, SearchPickerSetting>;
|
||||
|
||||
struct SettingVisibility {
|
||||
std::vector<std::string> path;
|
||||
std::vector<std::string> values;
|
||||
};
|
||||
|
||||
struct SettingEntry {
|
||||
std::string section;
|
||||
std::string group;
|
||||
@@ -121,6 +126,7 @@ namespace settings {
|
||||
SettingControl control;
|
||||
bool advanced = false;
|
||||
std::string searchText;
|
||||
std::optional<SettingVisibility> visibleWhen;
|
||||
};
|
||||
|
||||
// Runtime conditions that gate optional sections (e.g. compositor-specific features).
|
||||
|
||||
@@ -1470,6 +1470,7 @@ void SettingsWindow::buildScene(std::uint32_t width, std::uint32_t height) {
|
||||
.control = settings::ButtonSetting{i18n::tr("settings.schema.desktop.widgets-editor.button"),
|
||||
m_openDesktopWidgetEditor},
|
||||
.searchText = "desktop widgets editor edit",
|
||||
.visibleWhen = std::nullopt,
|
||||
};
|
||||
m_settingsRegistry.insert(it, std::move(btn));
|
||||
}
|
||||
|
||||
@@ -276,16 +276,23 @@ namespace settings {
|
||||
}
|
||||
|
||||
std::vector<WidgetSettingSpec> commonWidgetSettingSpecs() {
|
||||
const WidgetSettingVisibility capsuleOn{"capsule", {"true"}};
|
||||
auto capsuleGroup = stringSpec("capsule_group");
|
||||
capsuleGroup.visibleWhen = capsuleOn;
|
||||
auto capsuleFill = colorRoleSpec("capsule_fill", "surface_variant");
|
||||
capsuleFill.visibleWhen = capsuleOn;
|
||||
auto capsuleBorder = colorRoleSpec("capsule_border", {}, true);
|
||||
capsuleBorder.visibleWhen = capsuleOn;
|
||||
auto capsuleForeground = colorRoleSpec("capsule_foreground", {}, true);
|
||||
capsuleForeground.visibleWhen = capsuleOn;
|
||||
auto capsulePadding = doubleSpec("capsule_padding", static_cast<double>(Style::barCapsulePadding), 0.0, 48.0, 1.0);
|
||||
capsulePadding.visibleWhen = capsuleOn;
|
||||
auto capsuleOpacity = doubleSpec("capsule_opacity", 1.0, 0.0, 1.0, 0.01);
|
||||
capsuleOpacity.visibleWhen = capsuleOn;
|
||||
return {
|
||||
boolSpec("anchor", false, true),
|
||||
colorRoleSpec("color", {}, true),
|
||||
boolSpec("capsule", false),
|
||||
stringSpec("capsule_group"),
|
||||
colorRoleSpec("capsule_fill", "surface_variant"),
|
||||
colorRoleSpec("capsule_border", {}, true),
|
||||
colorRoleSpec("capsule_foreground", {}, true),
|
||||
doubleSpec("capsule_padding", static_cast<double>(Style::barCapsulePadding), 0.0, 48.0, 1.0),
|
||||
doubleSpec("capsule_opacity", 1.0, 0.0, 1.0, 0.01),
|
||||
boolSpec("anchor", false, true), colorRoleSpec("color", {}, true), boolSpec("capsule", false),
|
||||
std::move(capsuleGroup), std::move(capsuleFill), std::move(capsuleBorder),
|
||||
std::move(capsuleForeground), std::move(capsulePadding), std::move(capsuleOpacity),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -299,8 +306,9 @@ namespace settings {
|
||||
};
|
||||
const std::vector<WidgetSettingSelectOption> sysmonStats = {
|
||||
{"cpu_usage", "settings.widgets.options.cpu-usage"}, {"cpu_temp", "settings.widgets.options.cpu-temp"},
|
||||
{"ram_used", "settings.widgets.options.ram-used"}, {"ram_pct", "settings.widgets.options.ram-percent"},
|
||||
{"swap_pct", "settings.widgets.options.swap-percent"}, {"disk_pct", "settings.widgets.options.disk-percent"},
|
||||
{"gpu_temp", "settings.widgets.options.gpu-temp"}, {"ram_used", "settings.widgets.options.ram-used"},
|
||||
{"ram_pct", "settings.widgets.options.ram-percent"}, {"swap_pct", "settings.widgets.options.swap-percent"},
|
||||
{"disk_pct", "settings.widgets.options.disk-percent"},
|
||||
};
|
||||
const std::vector<WidgetSettingSelectOption> sysmonDisplay = {
|
||||
{"gauge", "settings.widgets.options.gauge"},
|
||||
@@ -383,7 +391,11 @@ namespace settings {
|
||||
add(doubleSpec("length", 8.0, 0.0, 400.0, 1.0));
|
||||
} else if (type == "sysmon") {
|
||||
add(selectSpec("stat", "cpu_usage", sysmonStats));
|
||||
add(stringSpec("path", "/"));
|
||||
{
|
||||
auto path = stringSpec("path", "/");
|
||||
path.visibleWhen = WidgetSettingVisibility{"stat", {"disk_pct"}};
|
||||
add(std::move(path));
|
||||
}
|
||||
add(segmentedSpec("display", "gauge", sysmonDisplay));
|
||||
add(boolSpec("show_label", true));
|
||||
} else if (type == "taskbar") {
|
||||
@@ -393,7 +405,11 @@ namespace settings {
|
||||
add(stringListSpec("hidden"));
|
||||
add(stringListSpec("pinned"));
|
||||
add(boolSpec("drawer", false));
|
||||
add(intSpec("drawer_columns", 3, 1.0, 5.0, 1.0));
|
||||
{
|
||||
auto cols = intSpec("drawer_columns", 3, 1.0, 5.0, 1.0);
|
||||
cols.visibleWhen = WidgetSettingVisibility{"drawer", {"true"}};
|
||||
add(std::move(cols));
|
||||
}
|
||||
} else if (type == "volume") {
|
||||
add(segmentedSpec("device", "output", volumeDeviceOptions));
|
||||
add(boolSpec("show_label", true));
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace settings {
|
||||
std::string_view labelKey;
|
||||
};
|
||||
|
||||
struct WidgetSettingVisibility {
|
||||
std::string key;
|
||||
std::vector<std::string> values;
|
||||
};
|
||||
|
||||
struct WidgetSettingSpec {
|
||||
std::string key;
|
||||
std::string labelKey;
|
||||
@@ -64,6 +69,7 @@ namespace settings {
|
||||
std::vector<WidgetSettingSelectOption> options;
|
||||
bool advanced = false;
|
||||
bool segmented = false; // applies when valueType == Select
|
||||
std::optional<WidgetSettingVisibility> visibleWhen;
|
||||
};
|
||||
|
||||
[[nodiscard]] const std::vector<WidgetTypeSpec>& widgetTypeSpecs();
|
||||
|
||||
@@ -105,6 +105,47 @@ namespace {
|
||||
t.find("soc") != std::string::npos || t.find("package") != std::string::npos;
|
||||
}
|
||||
|
||||
int scoreGpuHwmonSensor(const std::string& hwmon_name, const std::string& label) {
|
||||
const std::string name = toLower(hwmon_name);
|
||||
const std::string lbl = toLower(label);
|
||||
|
||||
if (name.find("nvidia") != std::string::npos) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
if (name == "amdgpu") {
|
||||
score += 20;
|
||||
} else if (name == "i915" || name == "xe") {
|
||||
score += 20;
|
||||
} else if (name == "nouveau") {
|
||||
score += 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lbl.find("junction") != std::string::npos || lbl.find("edge") != std::string::npos) {
|
||||
score += 30;
|
||||
} else if (lbl.find("gpu") != std::string::npos || lbl.find("mem") != std::string::npos) {
|
||||
score += 25;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
bool isGpuHwmonAwake(const std::filesystem::path& hwmonPath) {
|
||||
namespace fs = std::filesystem;
|
||||
const auto deviceLink = hwmonPath / "device";
|
||||
if (!fs::exists(deviceLink)) {
|
||||
return true;
|
||||
}
|
||||
const auto status = readSmallTextFile(deviceLink / "power" / "runtime_status");
|
||||
if (!status.has_value()) {
|
||||
return true;
|
||||
}
|
||||
return *status == "active";
|
||||
}
|
||||
|
||||
constexpr Logger kLog("sysmon");
|
||||
|
||||
} // namespace
|
||||
@@ -143,6 +184,10 @@ void SystemMonitorService::retainCpuTemp() { m_cpuTempRefs.fetch_add(1, std::mem
|
||||
|
||||
void SystemMonitorService::releaseCpuTemp() { m_cpuTempRefs.fetch_sub(1, std::memory_order_relaxed); }
|
||||
|
||||
void SystemMonitorService::retainGpuTemp() { m_gpuTempRefs.fetch_add(1, std::memory_order_relaxed); }
|
||||
|
||||
void SystemMonitorService::releaseGpuTemp() { m_gpuTempRefs.fetch_sub(1, std::memory_order_relaxed); }
|
||||
|
||||
void SystemMonitorService::retainDiskPath(const std::string& path) {
|
||||
const float initialPercent = isRunning() ? readDiskUsagePercent(path) : 0.0f;
|
||||
std::lock_guard lock{m_statsMutex};
|
||||
@@ -212,6 +257,7 @@ void SystemMonitorService::samplingLoop() {
|
||||
next.sampledAt = std::chrono::steady_clock::now();
|
||||
std::vector<std::string> diskPaths;
|
||||
std::optional<double> previousCpuTemp;
|
||||
std::optional<double> previousGpuTemp;
|
||||
|
||||
const auto currentCpu = readCpuTotals();
|
||||
if (prevCpu.has_value() && currentCpu.has_value()) {
|
||||
@@ -266,6 +312,7 @@ void SystemMonitorService::samplingLoop() {
|
||||
{
|
||||
std::lock_guard lock{m_statsMutex};
|
||||
previousCpuTemp = m_latest.cpuTempC;
|
||||
previousGpuTemp = m_latest.gpuTempC;
|
||||
diskPaths.reserve(m_diskHistories.size());
|
||||
for (const auto& [path, disk] : m_diskHistories) {
|
||||
if (disk.refs > 0) {
|
||||
@@ -281,6 +328,13 @@ void SystemMonitorService::samplingLoop() {
|
||||
next.cpuTempC = previousCpuTemp.value_or(40.0);
|
||||
}
|
||||
|
||||
if (m_gpuTempRefs.load(std::memory_order_relaxed) > 0) {
|
||||
next.gpuTempC = readGpuTempCelsius();
|
||||
}
|
||||
if (!next.gpuTempC.has_value()) {
|
||||
next.gpuTempC = previousGpuTemp;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, float>> diskPercents;
|
||||
diskPercents.reserve(diskPaths.size());
|
||||
for (const auto& path : diskPaths) {
|
||||
@@ -473,6 +527,60 @@ std::optional<double> SystemMonitorService::readCpuTempCelsius() {
|
||||
return fallback_temp;
|
||||
}
|
||||
|
||||
std::optional<double> SystemMonitorService::readGpuTempCelsius() {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path hwmon_root{"/sys/class/hwmon"};
|
||||
if (!fs::exists(hwmon_root) || !fs::is_directory(hwmon_root)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int bestScore = -1;
|
||||
std::optional<double> best_temp;
|
||||
|
||||
for (const auto& hwmon_entry : fs::directory_iterator{hwmon_root}) {
|
||||
if (!hwmon_entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string hwmonName = readSmallTextFile(hwmon_entry.path() / "name").value_or("");
|
||||
const int nameScore = scoreGpuHwmonSensor(hwmonName, "");
|
||||
if (nameScore < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isGpuHwmonAwake(hwmon_entry.path())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& file_entry : fs::directory_iterator{hwmon_entry.path()}) {
|
||||
if (!file_entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string fileName = file_entry.path().filename().string();
|
||||
if (!fileName.starts_with("temp") || !fileName.ends_with("_input")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string base = fileName.substr(0, fileName.size() - 6);
|
||||
const std::string label = readSmallTextFile(hwmon_entry.path() / (base + "_label")).value_or("");
|
||||
const auto tempC = readTempInputCelsius(file_entry.path());
|
||||
if (!tempC.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int score = scoreGpuHwmonSensor(hwmonName, label);
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
best_temp = *tempC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_temp;
|
||||
}
|
||||
|
||||
float SystemMonitorService::readDiskUsagePercent(const std::string& path) {
|
||||
struct statvfs sv{};
|
||||
if (::statvfs(path.c_str(), &sv) == 0 && sv.f_blocks > 0) {
|
||||
|
||||
@@ -21,6 +21,7 @@ struct SystemStats {
|
||||
std::uint64_t swapUsedMb{0};
|
||||
std::uint64_t swapTotalMb{0};
|
||||
std::optional<double> cpuTempC;
|
||||
std::optional<double> gpuTempC;
|
||||
double netRxBytesPerSec{0.0};
|
||||
double netTxBytesPerSec{0.0};
|
||||
double loadAvg1{0.0};
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
|
||||
void retainCpuTemp();
|
||||
void releaseCpuTemp();
|
||||
void retainGpuTemp();
|
||||
void releaseGpuTemp();
|
||||
void retainDiskPath(const std::string& path);
|
||||
void releaseDiskPath(const std::string& path);
|
||||
[[nodiscard]] float diskUsagePercent(const std::string& path) const;
|
||||
@@ -75,6 +78,7 @@ private:
|
||||
};
|
||||
[[nodiscard]] static std::optional<MemData> readMemoryKb();
|
||||
[[nodiscard]] static std::optional<double> readCpuTempCelsius();
|
||||
[[nodiscard]] static std::optional<double> readGpuTempCelsius();
|
||||
[[nodiscard]] static float readDiskUsagePercent(const std::string& path);
|
||||
|
||||
struct NetIfaceBytes {
|
||||
@@ -86,6 +90,7 @@ private:
|
||||
|
||||
std::atomic<bool> m_running{false};
|
||||
std::atomic<int> m_cpuTempRefs{0};
|
||||
std::atomic<int> m_gpuTempRefs{0};
|
||||
std::thread m_thread;
|
||||
std::mutex m_wakeMutex;
|
||||
std::condition_variable m_wakeCv;
|
||||
|
||||
Reference in New Issue
Block a user