From 6fc5af3d21d929e527f771e2b2fddc024cc1584a Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Sat, 8 Nov 2025 06:04:52 +0800 Subject: [PATCH 1/4] DiskIOMeter: Move cache update code to a new function The new function is named DiskIOUpdateCache(). Allow code reuse. Signed-off-by: Kang-Che Sung --- DiskIOMeter.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 4df5c21b8..c96b93f50 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -33,9 +33,7 @@ static char cached_write_diff_str[6]; static double cached_utilisation_diff; static double cached_utilisation_norm; -static void DiskIOMeter_updateValues(Meter* this) { - const Machine* host = this->host; - +static void DiskIOUpdateCache(const Machine* host) { static uint64_t cached_last_update; uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; bool hasNewData = false; @@ -99,6 +97,10 @@ static void DiskIOMeter_updateValues(Meter* this) { cached_write_total = data.totalBytesWritten; cached_msTimeSpend_total = data.totalMsTimeSpend; } +} + +static void DiskIOMeter_updateValues(Meter* this) { + DiskIOUpdateCache(this->host); this->values[0] = cached_utilisation_norm; From 089ba326353ba4d92e79fc6ebe1f32d5bbe23254 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Sat, 8 Nov 2025 06:05:08 +0800 Subject: [PATCH 2/4] DiskIOMeter: Adjust code indent and formatting No changes in behavior. Signed-off-by: Kang-Che Sung --- DiskIOMeter.c | 127 ++++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index c96b93f50..89ce8cd52 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -35,68 +35,70 @@ static double cached_utilisation_norm; static void DiskIOUpdateCache(const Machine* host) { static uint64_t cached_last_update; + uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; - bool hasNewData = false; - DiskIOData data; /* update only every 500ms to have a sane span for rate calculation */ - if (passedTimeInMs > 500) { - hasNewData = Platform_getDiskIO(&data); - if (!hasNewData) { - status = RATESTATUS_NODATA; - } else if (cached_last_update == 0) { - status = RATESTATUS_INIT; - } else if (passedTimeInMs > 30000) { - status = RATESTATUS_STALE; - } else { - status = RATESTATUS_DATA; - } + if (passedTimeInMs <= 500) + return; - cached_last_update = host->realtimeMs; + DiskIOData data; + bool hasNewData = Platform_getDiskIO(&data); + if (!hasNewData) { + status = RATESTATUS_NODATA; + } else if (cached_last_update == 0) { + status = RATESTATUS_INIT; + } else if (passedTimeInMs > 30000) { + status = RATESTATUS_STALE; + } else { + status = RATESTATUS_DATA; } - if (hasNewData) { - static uint64_t cached_read_total; - static uint64_t cached_write_total; - static uint64_t cached_msTimeSpend_total; + cached_last_update = host->realtimeMs; - if (status != RATESTATUS_INIT) { - uint64_t diff; + if (!hasNewData) + return; - if (data.totalBytesRead > cached_read_total) { - diff = data.totalBytesRead - cached_read_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ - } else { - diff = 0; - } - Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str)); - - if (data.totalBytesWritten > cached_write_total) { - diff = data.totalBytesWritten - cached_write_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ - } else { - diff = 0; - } - Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str)); - - cached_utilisation_diff = 0.0; - cached_utilisation_norm = 0.0; - if (data.totalMsTimeSpend > cached_msTimeSpend_total) { - diff = data.totalMsTimeSpend - cached_msTimeSpend_total; - cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs; - if (data.numDisks > 0) { - cached_utilisation_norm = (double)diff / (passedTimeInMs * data.numDisks); - cached_utilisation_norm = MINIMUM(cached_utilisation_norm, 1.0); - } - } + static uint64_t cached_read_total; + static uint64_t cached_write_total; + static uint64_t cached_msTimeSpend_total; + + if (status != RATESTATUS_INIT) { + uint64_t diff; + + if (data.totalBytesRead > cached_read_total) { + diff = data.totalBytesRead - cached_read_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ + } else { + diff = 0; } + Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str)); - cached_read_total = data.totalBytesRead; - cached_write_total = data.totalBytesWritten; - cached_msTimeSpend_total = data.totalMsTimeSpend; + if (data.totalBytesWritten > cached_write_total) { + diff = data.totalBytesWritten - cached_write_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ + } else { + diff = 0; + } + Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str)); + + cached_utilisation_diff = 0.0; + cached_utilisation_norm = 0.0; + if (data.totalMsTimeSpend > cached_msTimeSpend_total) { + diff = data.totalMsTimeSpend - cached_msTimeSpend_total; + cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs; + if (data.numDisks > 0) { + cached_utilisation_norm = (double)diff / (passedTimeInMs * data.numDisks); + cached_utilisation_norm = MINIMUM(cached_utilisation_norm, 1.0); + } + } } + + cached_read_total = data.totalBytesRead; + cached_write_total = data.totalBytesWritten; + cached_msTimeSpend_total = data.totalMsTimeSpend; } static void DiskIOMeter_updateValues(Meter* this) { @@ -104,17 +106,18 @@ static void DiskIOMeter_updateValues(Meter* this) { this->values[0] = cached_utilisation_norm; - if (status == RATESTATUS_NODATA) { - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); - return; - } - if (status == RATESTATUS_INIT) { - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); - return; - } - if (status == RATESTATUS_STALE) { - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); - return; + switch (status) { + case RATESTATUS_NODATA: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); + return; + case RATESTATUS_INIT: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); + return; + case RATESTATUS_STALE: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); + return; + case RATESTATUS_DATA: + break; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", cached_read_diff_str, cached_write_diff_str, cached_utilisation_diff); From 4ff3cdb5f3b55d56349a46ac96a1e4f6ef9f9d7a Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 10 Nov 2025 10:59:51 +0800 Subject: [PATCH 3/4] Introduce DiskIORateMeter and DiskIOTimeMeter The two meters are split from DiskIOMeter and they allow separate display of disk read & write rates and the busy time percentage, including drawing the data as separate bars and graphs. The old DiskIOMeter is kept for backward compatibility. It will be reworked in the next commit. Note that DiskIORateMeter and DiskIOTimeMeter have different 'isPercentChart' values. Signed-off-by: Kang-Che Sung --- DiskIOMeter.c | 164 +++++++++++++++++++++++++++++++++++++++- DiskIOMeter.h | 4 + darwin/Platform.c | 2 + dragonflybsd/Platform.c | 2 + freebsd/Platform.c | 2 + linux/Platform.c | 2 + netbsd/Platform.c | 2 + pcp/Platform.c | 2 + 8 files changed, 176 insertions(+), 4 deletions(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 89ce8cd52..43bdfc9a4 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -21,6 +21,15 @@ in the source distribution for its full text. #include "XUtils.h" +static const int DiskIORateMeter_attributes[] = { + METER_VALUE_IOREAD, + METER_VALUE_IOWRITE, +}; + +static const int DiskIOTimeMeter_attributes[] = { + METER_VALUE_NOTICE, +}; + static const int DiskIOMeter_attributes[] = { METER_VALUE_NOTICE, METER_VALUE_IOREAD, @@ -28,8 +37,11 @@ static const int DiskIOMeter_attributes[] = { }; static MeterRateStatus status = RATESTATUS_INIT; +static double cached_read_diff; static char cached_read_diff_str[6]; +static double cached_write_diff; static char cached_write_diff_str[6]; +static uint64_t cached_num_disks; static double cached_utilisation_diff; static double cached_utilisation_norm; @@ -69,21 +81,22 @@ static void DiskIOUpdateCache(const Machine* host) { if (data.totalBytesRead > cached_read_total) { diff = data.totalBytesRead - cached_read_total; diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ } else { diff = 0; } - Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str)); + cached_read_diff = diff; + Meter_humanUnit(cached_read_diff_str, cached_read_diff / ONE_K, sizeof(cached_read_diff_str)); if (data.totalBytesWritten > cached_write_total) { diff = data.totalBytesWritten - cached_write_total; diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ } else { diff = 0; } - Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str)); + cached_write_diff = diff; + Meter_humanUnit(cached_write_diff_str, cached_write_diff / ONE_K, sizeof(cached_write_diff_str)); + cached_num_disks = data.numDisks; cached_utilisation_diff = 0.0; cached_utilisation_norm = 0.0; if (data.totalMsTimeSpend > cached_msTimeSpend_total) { @@ -101,6 +114,111 @@ static void DiskIOUpdateCache(const Machine* host) { cached_msTimeSpend_total = data.totalMsTimeSpend; } +static void DiskIORateMeter_updateValues(Meter* this) { + DiskIOUpdateCache(this->host); + + this->values[0] = cached_read_diff; + this->values[1] = cached_write_diff; + + switch (status) { + case RATESTATUS_NODATA: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); + return; + case RATESTATUS_INIT: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); + return; + case RATESTATUS_STALE: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); + return; + case RATESTATUS_DATA: + break; + } + + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s", cached_read_diff_str, cached_write_diff_str); +} + +static void DiskIORateMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + switch (status) { + case RATESTATUS_NODATA: + RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); + return; + case RATESTATUS_INIT: + RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing..."); + return; + case RATESTATUS_STALE: + RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data"); + return; + case RATESTATUS_DATA: + break; + } + + RichString_appendAscii(out, CRT_colors[METER_TEXT], "read: "); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); + + RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); +} + +static void DiskIOTimeMeter_updateValues(Meter* this) { + DiskIOUpdateCache(this->host); + + this->values[0] = cached_utilisation_norm; + + switch (status) { + case RATESTATUS_NODATA: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); + return; + case RATESTATUS_INIT: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); + return; + case RATESTATUS_STALE: + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); + return; + case RATESTATUS_DATA: + break; + } + + char numDisksStr[12]; + numDisksStr[0] = '\0'; + if (cached_num_disks > 1 && cached_num_disks < 1000) { + xSnprintf(numDisksStr, sizeof(numDisksStr), " (%udisks)", (unsigned int)cached_num_disks); + } + + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%%s", cached_utilisation_diff, numDisksStr); +} + +static void DiskIOTimeMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + switch (status) { + case RATESTATUS_NODATA: + RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); + return; + case RATESTATUS_INIT: + RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing..."); + return; + case RATESTATUS_STALE: + RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data"); + return; + case RATESTATUS_DATA: + break; + } + + char buffer[16]; + + int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; + int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); + RichString_appendnAscii(out, CRT_colors[color], buffer, len); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " busy"); + + if (cached_num_disks > 1 && cached_num_disks < 1000) { + RichString_appendAscii(out, CRT_colors[METER_TEXT], " ("); + len = xSnprintf(buffer, sizeof(buffer), "%u", (unsigned int)cached_num_disks); + RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, len); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " disks)"); + } +} + static void DiskIOMeter_updateValues(Meter* this) { DiskIOUpdateCache(this->host); @@ -153,6 +271,44 @@ static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); } +const MeterClass DiskIORateMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = DiskIORateMeter_display + }, + .updateValues = DiskIORateMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .supportedModes = METERMODE_DEFAULT_SUPPORTED, + .maxItems = 2, + .isPercentChart = false, + .total = 1.0, + .attributes = DiskIORateMeter_attributes, + .name = "DiskIORate", + .uiName = "Disk IO Rate", + .description = "Disk IO read & write bytes per second", + .caption = "Dsk: " +}; + +const MeterClass DiskIOTimeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = DiskIOTimeMeter_display + }, + .updateValues = DiskIOTimeMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .supportedModes = METERMODE_DEFAULT_SUPPORTED, + .maxItems = 1, + .isPercentChart = true, + .total = 1.0, + .attributes = DiskIOTimeMeter_attributes, + .name = "DiskIOTime", + .uiName = "Disk IO Time", + .description = "Disk percent time busy", + .caption = "Dsk: " +}; + const MeterClass DiskIOMeter_class = { .super = { .extends = Class(Meter), diff --git a/DiskIOMeter.h b/DiskIOMeter.h index b49adaf3a..2e1d29c1c 100644 --- a/DiskIOMeter.h +++ b/DiskIOMeter.h @@ -19,6 +19,10 @@ typedef struct DiskIOData_ { uint64_t numDisks; } DiskIOData; +extern const MeterClass DiskIORateMeter_class; + +extern const MeterClass DiskIOTimeMeter_class; + extern const MeterClass DiskIOMeter_class; #endif /* HEADER_DiskIOMeter */ diff --git a/darwin/Platform.c b/darwin/Platform.c index aa7400c87..e2ad672af 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -143,6 +143,8 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &FileDescriptorMeter_class, diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index ddd211771..46c367b10 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -120,6 +120,8 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &FileDescriptorMeter_class, diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 0eda02b7a..fce57f2a9 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -130,6 +130,8 @@ const MeterClass* const Platform_meterTypes[] = { &BlankMeter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &FileDescriptorMeter_class, &NetworkIOMeter_class, diff --git a/linux/Platform.c b/linux/Platform.c index 352c9c80b..a0bfbfa93 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -247,6 +247,8 @@ const MeterClass* const Platform_meterTypes[] = { &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &ZramMeter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &SELinuxMeter_class, diff --git a/netbsd/Platform.c b/netbsd/Platform.c index af2a8613d..12e427cb5 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -180,6 +180,8 @@ const MeterClass* const Platform_meterTypes[] = { &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &FileDescriptorMeter_class, diff --git a/pcp/Platform.c b/pcp/Platform.c index e779ea0bc..f02fbde74 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -114,6 +114,8 @@ const MeterClass* const Platform_meterTypes[] = { &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &ZramMeter_class, + &DiskIORateMeter_class, + &DiskIOTimeMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &SysArchMeter_class, From bb7317cd89d2acebf41e4bb13c574b0eb8011df9 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 10 Nov 2025 11:00:06 +0800 Subject: [PATCH 4/4] Rework DiskIOMeter into a combined display of 2 sub-meters The new meter is a combined display of DiskIORate and DiskIOTime. The combination works similarly to MemorySwapMeter. Signed-off-by: Kang-Che Sung --- DiskIOMeter.c | 121 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 43bdfc9a4..264074aa9 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -21,6 +21,11 @@ in the source distribution for its full text. #include "XUtils.h" +typedef struct DiskIOMeterData_ { + Meter* diskIORateMeter; + Meter* diskIOTimeMeter; +} DiskIOMeterData; + static const int DiskIORateMeter_attributes[] = { METER_VALUE_IOREAD, METER_VALUE_IOWRITE, @@ -30,12 +35,6 @@ static const int DiskIOTimeMeter_attributes[] = { METER_VALUE_NOTICE, }; -static const int DiskIOMeter_attributes[] = { - METER_VALUE_NOTICE, - METER_VALUE_IOREAD, - METER_VALUE_IOWRITE, -}; - static MeterRateStatus status = RATESTATUS_INIT; static double cached_read_diff; static char cached_read_diff_str[6]; @@ -219,56 +218,88 @@ static void DiskIOTimeMeter_display(ATTR_UNUSED const Object* cast, RichString* } } -static void DiskIOMeter_updateValues(Meter* this) { - DiskIOUpdateCache(this->host); - - this->values[0] = cached_utilisation_norm; +static void DiskIOMeter_display(const Object* cast, RichString* out) { + DiskIORateMeter_display(cast, out); switch (status) { case RATESTATUS_NODATA: - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); - return; case RATESTATUS_INIT: - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); - return; case RATESTATUS_STALE: - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); return; case RATESTATUS_DATA: break; } - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", cached_read_diff_str, cached_write_diff_str, cached_utilisation_diff); + RichString_appendAscii(out, CRT_colors[METER_TEXT], "; "); + DiskIOTimeMeter_display(cast, out); } -static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { - switch (status) { - case RATESTATUS_NODATA: - RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); - return; - case RATESTATUS_INIT: - RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing..."); - return; - case RATESTATUS_STALE: - RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data"); - return; - case RATESTATUS_DATA: - break; +static void DiskIOMeter_updateValues(Meter* this) { + DiskIOMeterData* data = this->meterData; + + Meter_updateValues(data->diskIORateMeter); + Meter_updateValues(data->diskIOTimeMeter); +} + +static void DiskIOMeter_draw(Meter* this, int x, int y, int w) { + DiskIOMeterData* data = this->meterData; + + assert(data->diskIORateMeter->draw); + assert(data->diskIOTimeMeter->draw); + + switch (this->mode) { + case TEXT_METERMODE: + case LED_METERMODE: + data->diskIORateMeter->draw(this, x, y, w); + return; } - char buffer[16]; + /* Use the same width for each sub meter to align with CPU meter */ + const int colwidth = w / 2; + const int diff = w % 2; - int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; - int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); - RichString_appendnAscii(out, CRT_colors[color], buffer, len); + data->diskIORateMeter->draw(data->diskIORateMeter, x, y, colwidth); + data->diskIOTimeMeter->draw(data->diskIOTimeMeter, x + colwidth + diff, y, colwidth); +} - RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); +static void DiskIOMeter_init(Meter* this) { + if (!this->meterData) { + this->meterData = xCalloc(1, sizeof(DiskIOMeterData)); + } - RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); + DiskIOMeterData* data = this->meterData; + + if (!data->diskIORateMeter) + data->diskIORateMeter = Meter_new(this->host, 0, (const MeterClass*) Class(DiskIORateMeter)); + if (!data->diskIOTimeMeter) + data->diskIOTimeMeter = Meter_new(this->host, 0, (const MeterClass*) Class(DiskIOTimeMeter)); + + if (Meter_initFn(data->diskIORateMeter)) { + Meter_init(data->diskIORateMeter); + } + if (Meter_initFn(data->diskIOTimeMeter)) { + Meter_init(data->diskIOTimeMeter); + } +} + +static void DiskIOMeter_updateMode(Meter* this, MeterModeId mode) { + DiskIOMeterData* data = this->meterData; + + this->mode = mode; + + Meter_setMode(data->diskIORateMeter, mode); + Meter_setMode(data->diskIOTimeMeter, mode); + + this->h = MAXIMUM(data->diskIORateMeter->h, data->diskIOTimeMeter->h); +} + +static void DiskIOMeter_done(Meter* this) { + DiskIOMeterData* data = this->meterData; + + Meter_delete((Object*)data->diskIORateMeter); + Meter_delete((Object*)data->diskIOTimeMeter); + + free(data); } const MeterClass DiskIORateMeter_class = { @@ -318,11 +349,13 @@ const MeterClass DiskIOMeter_class = { .updateValues = DiskIOMeter_updateValues, .defaultMode = TEXT_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, - .maxItems = 1, - .isPercentChart = true, - .total = 1.0, - .attributes = DiskIOMeter_attributes, + .isMultiColumn = true, .name = "DiskIO", .uiName = "Disk IO", - .caption = "Disk IO: " + .description = "Disk IO rate & time combined display", + .caption = "Dsk: ", + .draw = DiskIOMeter_draw, + .init = DiskIOMeter_init, + .updateMode = DiskIOMeter_updateMode, + .done = DiskIOMeter_done };