diff --git a/BatteryMeter.c b/BatteryMeter.c index 001249d4c..7ab1bac97 100644 --- a/BatteryMeter.c +++ b/BatteryMeter.c @@ -64,6 +64,7 @@ const MeterClass BatteryMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 1, + .isPercentChart = true, .total = 100.0, .attributes = BatteryMeter_attributes, .name = "Battery", diff --git a/CPUMeter.c b/CPUMeter.c index 69da88db0..ea03bb227 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -352,6 +352,7 @@ const MeterClass CPUMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = CPU_METER_ITEMCOUNT, + .isPercentChart = true, .total = 100.0, .attributes = CPUMeter_attributes, .name = "CPU", diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 4bb689fac..4df5c21b8 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -158,6 +158,7 @@ const MeterClass DiskIOMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 1, + .isPercentChart = true, .total = 1.0, .attributes = DiskIOMeter_attributes, .name = "DiskIO", diff --git a/FileDescriptorMeter.c b/FileDescriptorMeter.c index cd3baf58c..bd7585cfd 100644 --- a/FileDescriptorMeter.c +++ b/FileDescriptorMeter.c @@ -110,6 +110,7 @@ const MeterClass FileDescriptorMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 2, + .isPercentChart = false, .total = 65536.0, .attributes = FileDescriptorMeter_attributes, .name = "FileDescriptors", diff --git a/GPUMeter.c b/GPUMeter.c index 9e1685b21..0a536960e 100644 --- a/GPUMeter.c +++ b/GPUMeter.c @@ -156,6 +156,7 @@ const MeterClass GPUMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = ARRAYSIZE(GPUMeter_attributes), + .isPercentChart = true, .total = 100.0, .attributes = GPUMeter_attributes, .name = "GPU", diff --git a/LoadAverageMeter.c b/LoadAverageMeter.c index 6bf13a03e..a0b05f2be 100644 --- a/LoadAverageMeter.c +++ b/LoadAverageMeter.c @@ -46,15 +46,15 @@ static void LoadAverageMeter_updateValues(Meter* this) { this->curItems = 1; // change bar color and total based on value + if (this->total < this->host->activeCPUs) { + this->total = this->host->activeCPUs; + } if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; - this->total = 1.0; } else if (this->values[0] < this->host->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->host->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->host->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); @@ -78,15 +78,15 @@ static void LoadMeter_updateValues(Meter* this) { Platform_getLoadAverage(&this->values[0], &five, &fifteen); // change bar color and total based on value + if (this->total < this->host->activeCPUs) { + this->total = this->host->activeCPUs; + } if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; - this->total = 1.0; } else if (this->values[0] < this->host->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->host->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->host->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]); @@ -111,7 +111,8 @@ const MeterClass LoadAverageMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, - .total = 100.0, + .isPercentChart = false, + .total = 1.0, .attributes = LoadAverageMeter_attributes, .name = "LoadAverage", .uiName = "Load average", @@ -129,7 +130,8 @@ const MeterClass LoadMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 1, - .total = 100.0, + .isPercentChart = false, + .total = 1.0, .attributes = LoadMeter_attributes, .name = "Load", .uiName = "Load", diff --git a/MemoryMeter.c b/MemoryMeter.c index 6769e3bec..b308b7a9b 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -117,6 +117,7 @@ const MeterClass MemoryMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = MEMORY_METER_ITEMCOUNT, + .isPercentChart = true, .total = 100.0, .attributes = MemoryMeter_attributes, .name = "Memory", diff --git a/Meter.c b/Meter.c index 7a9e5ff46..4f8f76673 100644 --- a/Meter.c +++ b/Meter.c @@ -10,6 +10,7 @@ in the source distribution for its full text. #include "Meter.h" #include +#include #include // IWYU pragma: keep #include #include @@ -47,6 +48,14 @@ static inline void Meter_displayBuffer(const Meter* this, RichString* out) { } } +static double Meter_computeSum(const Meter* this) { + assert(this->curItems > 0); + assert(this->values); + double sum = sumPositiveValues(this->values, this->curItems); + // Prevent rounding to infinity in IEEE 754 + return MINIMUM(DBL_MAX, sum); +} + /* ---------- TextMeterMode ---------- */ static void TextMeterMode_draw(Meter* this, int x, int y, int w) { @@ -100,6 +109,12 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { w--; } + // Update the "total" if necessary + if (!Meter_isPercentChart(this) && this->curItems > 0) { + double sum = Meter_computeSum(this); + this->total = MAXIMUM(sum, this->total); + } + if (w < 1) { goto end; } @@ -215,9 +230,12 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { } w -= captionLen; + // Prepare parameters for drawing assert(this->h >= 1); int h = this->h; + bool isPercentChart = Meter_isPercentChart(this); + GraphData* data = &this->drawData; // Expand the graph data buffer if necessary @@ -246,8 +264,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { data->values[nValues - 1] = 0.0; if (this->curItems > 0) { - assert(this->values); - data->values[nValues - 1] = sumPositiveValues(this->values, this->curItems); + data->values[nValues - 1] = Meter_computeSum(this); + if (isPercentChart && this->total > 0.0) { + data->values[nValues - 1] /= this->total; + } } } @@ -277,10 +297,19 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { } size_t i = nValues - (size_t)w * 2; + // Determine the graph scale + double total = 1.0; + if (!isPercentChart) { + for (size_t j = i; j < nValues; j++) { + total = MAXIMUM(data->values[j], total); + } + assert(total <= DBL_MAX); + } + assert(total >= 1.0); + // Draw the actual graph for (int col = 0; i < nValues - 1; i += 2, col++) { int pix = GraphMeterMode_pixPerRow * h; - double total = MAXIMUM(this->total, 1); int v1 = (int) lround(CLAMP(data->values[i] / total * pix, 1.0, pix)); int v2 = (int) lround(CLAMP(data->values[i + 1] / total * pix, 1.0, pix)); diff --git a/Meter.h b/Meter.h index b53a82324..d81e8d8bc 100644 --- a/Meter.h +++ b/Meter.h @@ -75,6 +75,12 @@ typedef struct MeterClass_ { const char* const description; /* optional meter description in header setup menu */ const uint8_t maxItems; const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */ + + /* Specifies how the meter is rendered in bar or graph mode: + true: a percent bar or graph with 'total' representing 100% or maximum. + false: the meter has no definite maximum; 'total' repesents initial + maximum value while actual maximum is updated automatically. */ + const bool isPercentChart; } MeterClass; #define As_Meter(this_) ((const MeterClass*)((this_)->super.klass)) @@ -95,6 +101,7 @@ typedef struct MeterClass_ { #define Meter_name(this_) As_Meter(this_)->name #define Meter_uiName(this_) As_Meter(this_)->uiName #define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn +#define Meter_isPercentChart(this_) As_Meter(this_)->isPercentChart typedef struct GraphData_ { struct timeval time; diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index 784c90ac3..66045ffe2 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -111,9 +111,6 @@ static void NetworkIOMeter_updateValues(Meter* this) { this->values[0] = cached_rxb_diff; this->values[1] = cached_txb_diff; - if (cached_rxb_diff + cached_txb_diff > this->total) { - this->total = cached_rxb_diff + cached_txb_diff; - } if (status == RATESTATUS_NODATA) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); @@ -171,7 +168,8 @@ const MeterClass NetworkIOMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 2, - .total = 100.0, + .isPercentChart = false, + .total = 1.0, .attributes = NetworkIOMeter_attributes, .name = "NetworkIO", .uiName = "Network IO", diff --git a/SwapMeter.c b/SwapMeter.c index 29c295d32..faa194556 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -75,6 +75,7 @@ const MeterClass SwapMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = SWAP_METER_ITEMCOUNT, + .isPercentChart = true, .total = 100.0, .attributes = SwapMeter_attributes, .name = "Swap", diff --git a/TasksMeter.c b/TasksMeter.c index fc1e4b0ed..f522b753b 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -34,7 +34,6 @@ static void TasksMeter_updateValues(Meter* this) { this->values[1] = pt->userlandThreads; this->values[2] = pt->totalTasks - pt->kernelThreads - pt->userlandThreads; this->values[3] = MINIMUM(pt->runningTasks, host->activeCPUs); - this->total = pt->totalTasks; xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%u/%u", MINIMUM(pt->runningTasks, host->activeCPUs), pt->totalTasks); } @@ -74,7 +73,8 @@ const MeterClass TasksMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 4, - .total = 100.0, + .isPercentChart = false, + .total = 1.0, .attributes = TasksMeter_attributes, .name = "Tasks", .uiName = "Task counter", diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c index bb975f942..f9a2241a3 100644 --- a/linux/HugePageMeter.c +++ b/linux/HugePageMeter.c @@ -101,6 +101,7 @@ const MeterClass HugePageMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = ARRAYSIZE(HugePageMeter_active_labels), + .isPercentChart = true, .total = 100.0, .attributes = HugePageMeter_attributes, .name = "HugePages", diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c index 5010c11d2..942213ea5 100644 --- a/linux/PressureStallMeter.c +++ b/linux/PressureStallMeter.c @@ -77,6 +77,7 @@ const MeterClass PressureStallCPUSomeMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallCPUSome", @@ -95,6 +96,7 @@ const MeterClass PressureStallIOSomeMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallIOSome", @@ -113,6 +115,7 @@ const MeterClass PressureStallIOFullMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallIOFull", @@ -131,6 +134,7 @@ const MeterClass PressureStallIRQFullMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallIRQFull", @@ -149,6 +153,7 @@ const MeterClass PressureStallMemorySomeMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallMemorySome", @@ -167,6 +172,7 @@ const MeterClass PressureStallMemoryFullMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 3, + .isPercentChart = true, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallMemoryFull", diff --git a/linux/ZramMeter.c b/linux/ZramMeter.c index 2a1c7715c..fe10c3baa 100644 --- a/linux/ZramMeter.c +++ b/linux/ZramMeter.c @@ -77,6 +77,7 @@ const MeterClass ZramMeter_class = { .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = ZRAM_METER_ITEMCOUNT, + .isPercentChart = true, .total = 100.0, .attributes = ZramMeter_attributes, .name = "Zram", diff --git a/zfs/ZfsArcMeter.c b/zfs/ZfsArcMeter.c index 87b7e19ce..8177ff2a5 100644 --- a/zfs/ZfsArcMeter.c +++ b/zfs/ZfsArcMeter.c @@ -93,6 +93,7 @@ const MeterClass ZfsArcMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 6, + .isPercentChart = true, .total = 100.0, .attributes = ZfsArcMeter_attributes, .name = "ZFSARC", diff --git a/zfs/ZfsCompressedArcMeter.c b/zfs/ZfsCompressedArcMeter.c index 35ab8b379..cd3bf43f6 100644 --- a/zfs/ZfsCompressedArcMeter.c +++ b/zfs/ZfsCompressedArcMeter.c @@ -81,6 +81,7 @@ const MeterClass ZfsCompressedArcMeter_class = { .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, .maxItems = 1, + .isPercentChart = true, .total = 100.0, .attributes = ZfsCompressedArcMeter_attributes, .name = "ZFSCARC",