Skip to content

Two-channel graph (Graph2) meter style#1415

Open
Explorer09 wants to merge 13 commits into
htop-dev:mainfrom
Explorer09:graph-with-neg
Open

Two-channel graph (Graph2) meter style#1415
Explorer09 wants to merge 13 commits into
htop-dev:mainfrom
Explorer09:graph-with-neg

Conversation

@Explorer09

@Explorer09 Explorer09 commented Mar 17, 2024

Copy link
Copy Markdown
Contributor

This pull request implements the "two-channel" graph style as the new "Graph2" meter mode. This is based on #714 so that the graphs will be shown with colors.

This new mode is designed for a few meters only— only DiskIORateMeter (#1763, merged) and NetworkIOMeter can correctly utilise this mode by showing the input and output rates as separate channels.

The feature was previously discussed in #1390.

Below is a preview screenshot taken in macOS Sequoia.
I included both Unicode mode and --no-unicode mode.
htop-screenshot-graph2-meter-mode

@Explorer09 Explorer09 force-pushed the graph-with-neg branch 3 times, most recently from f47a975 to 745f394 Compare March 28, 2024 13:54
@BenBE BenBE added the feature request Completely new feature requested label Apr 13, 2024
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 3 times, most recently from 31cb513 to bedca8f Compare April 21, 2024 06:39
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 4 times, most recently from ade4922 to 54d3d1c Compare April 26, 2024 18:59
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 3 times, most recently from 728ef87 to 3033b1d Compare May 2, 2024 21:01
@BenBE BenBE self-assigned this May 2, 2024
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 2 times, most recently from aa96aa4 to 10bb617 Compare May 22, 2024 19:03
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 2 times, most recently from f57affb to 5e3eeb1 Compare August 25, 2024 23:10
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 2 times, most recently from b5560f5 to 8a8cfda Compare December 16, 2024 09:53
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 3 times, most recently from c7c11bc to b446269 Compare February 9, 2025 18:16
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 2 times, most recently from 95052ef to d370bf3 Compare December 2, 2025 17:13
@Explorer09 Explorer09 force-pushed the graph-with-neg branch 2 times, most recently from 5e7bb49 to 0d092e5 Compare January 9, 2026 08:12
@eworm-de

Copy link
Copy Markdown
Contributor

The traditional graphical meters have a gray baseline. Does it make sense to have that here as well?

@eworm-de

Copy link
Copy Markdown
Contributor

Oh, and another note... Personally I do not like just adding numbers to names, at least for new functionality. How about something a little bit more descriptive? Perhaps "NGraph" (for negative) or "TCGraph" (for two-channel)?

@Explorer09

Explorer09 commented Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

The traditional graphical meters have a gray baseline. Does it make sense to have that here as well?

I've tried it. It would visually distract too much, as the "baseline" on the middle would be two times as thick. And I didn't like it.

Oh, and another note... Personally I do not like just adding numbers to names, at least for new functionality. How about something a little bit more descriptive? Perhaps "NGraph" (for negative) or "TCGraph" (for two-channel)?

"Graph2C" ?
For technical reason I would like to avoid putting digits in the front of the name. Although I believe the "Graph2" name is fine.

@eworm-de

Copy link
Copy Markdown
Contributor

I've tried it. It would visually distract too much, as the "baseline" on the middle would be two times as thick. And I didn't like it.

Ah, right... But I have some systems that have just little to no disk io activity, especially for reading. Some random blue dots floating around is distracting, too. 😝 Not sure what's better.

”Graph2C” ? For technical reason I would like to avoid putting digits in the front of the name. Although I believe the "Graph2" name is fine.

Oh, I read the name "Graph2" as "Graph, increment by one". If you take it as an abbreviation for "Graph two-channel" it makes kind of sense. 😉

Anyway, I would prefer your suggestion "Graph2C" if it is possible and fine for you. Any other opinions?

@eworm-de

eworm-de commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

This screenshot is made in a machine started from live media.

Screenshot_2026-04-02_13-51-41

Nothing in the disk meter (as it is running from optical media nothing is ever written), and just very little activity in network meter. It's really hard to tell where the (virtual) base line is.

@eworm-de

eworm-de commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

... and created a mockup.

Screenshot_2026-04-02_13-51-41

That's what a base line could look like.

Still not sure which one I would prefer...

@Explorer09

Copy link
Copy Markdown
Contributor Author

... and created a mockup.

That's what a base line could look like.

Still not sure which one I would prefer...

The baselines could look bad if you have a terminal emulator that displays braille matrix with gaps between characters.

See this screenshot for what I mean:

htop-screenshot-network-io-meter

In the screenshot I also use an alternative to two baselines in the middle.

@eworm-de

eworm-de commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Actually it's not that bad. I think that's the version I would prefer, and with proper spacing (I use font Terminus here, and my screenshot were made with that) this would look even better.

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Graph2 meter mode implementation

Implements a new two-channel graph meter mode (GRAPH2_METERMODE) enabling DiskIORateMeter and NetworkIOMeter to visualize input/output channels as separate data streams in graph format. The implementation uses cell-aware rendering with dot/detail bitmasks and builds on prior colored graph work (#714).

Core implementation

Meter.c (+1164/-72): Replaces the previous graph rendering approach with a complete cell- and dot-aware pipeline. Introduces fixed-width/graph-specific helper types (cells, offsets, compute state, draw context) and implements internal graph helpers for buffer reallocation/compaction, coordinate mapping, scale computation, and per-cell detail bitmasks. GraphMeterMode_draw now expands/reallocates the graph data buffer, records new values, determines scale exponents for non-percent charts, and renders using per-cell lookup + detail printing rather than fixed character-set pixel-pair plotting. The mode registry includes GRAPH2_METERMODE, and meter teardown logic adjusts to free/reset this->drawData.buffer (void* instead of typed double*).

Meter.h (+2/-1): Changes GraphData_.values storage from double* to void* buffer. Adds Meter_maxItems(this_) accessor macro.

MeterMode.h (+1): Adds GRAPH2_METERMODE enum value marked as not default-supported.

Meter support

DiskIOMeter.c (+6/-2): DiskIORateMeter and DiskIOMeter now advertise GRAPH2_METERMODE support via bitmask. DiskIOMeter_updateMode remaps GRAPH2_METERMODE to GRAPH_METERMODE for the sub-meter (since DiskIOTimeMeter doesn't support Graph2).

NetworkIOMeter.c (+1/-1): Adds GRAPH2_METERMODE to supportedModes bitmask.

Platform compatibility

CPU meter display unified across platforms (linux/netbsd/openbsd/pcp/Platform.c, +43/-33 total): Simplified meter composition for non-detailed CPU time path by reducing curItems and consolidating IRQ/SOFTIRQ handling. Removes redundant CPU_METER_FREQUENCY assignments in detailed paths.

Action.c (+5/-2): Updated CPU usage bar help rendering to branch on color scheme and adjust padding strings.

CPUMeter.c (+0/-12): Unified CPU meter attribute configuration by removing separate CPUMeter_attributes_summary; meter now uses single full attribute set containing CPU_IOWAIT instead of switching at runtime.

Build and utilities

XUtils.h/XUtils.c (+52/+10): Added bit-manipulation helpers: powerOf2Floor() (computes greatest power-of-two ≤ x) with conditional compilation for __builtin_clz, stdc_bit_floor_ui, or fallback; popCount8() (8-bit population count) with ARM NEON, x86 POPCNT, or arithmetic fallback.

configure.ac (+55/-1): Extended compiler feature detection for <stdbit.h>, GCC/Clang builtins (__builtin_clz, __builtin_popcount), stdc_bit_floor, and ARM NEON support.

Commit quality

Single, well-scoped commit implementing the complete Graph2 feature with all interdependent changes grouped logically. Code organization is clean: Graph2-specific logic is clearly separated via conditional branches on this->mode == GRAPH2_METERMODE. Implementation includes proper assertions, bounds checking, and platform-specific adaptations. Build system properly extended with compiler feature probes.

Walkthrough

This PR adds GRAPH2_METERMODE: a cell- and dot-aware graph renderer with buffer-backed records, per-item allocation, detail masks, and per-cell rendering. It updates Meter APIs to use a generic buffer, registers GRAPH2 mode, and frees the buffer on mode change/delete. It adds powerOf2Floor/popCount8 helpers with configure probes and fallbacks. CPUMeter attributes are unified (IOWAIT added; runtime summary switching removed), platform code adjusts curItems and slot assignments across Linux, NetBSD, OpenBSD, and PCP, DiskIO/NetworkIO advertise Graph2 (DiskIO time remaps to Graph), and Action.c help text gains monochrome-specific labels.

Poem

New cells replace the pixel rows,
buffers hold the meter flows,
CPU lists trimmed to fewer lines,
scales fall to neat power-of-two signs,
rendering sharper, data wise.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3b7a5791-e5f0-4fbd-a282-ecfd4dfa3748

📥 Commits

Reviewing files that changed from the base of the PR and between b7f9df9 and 11a9036.

📒 Files selected for processing (14)
  • Action.c
  • CPUMeter.c
  • DiskIOMeter.c
  • Meter.c
  • Meter.h
  • MeterMode.h
  • NetworkIOMeter.c
  • XUtils.c
  • XUtils.h
  • configure.ac
  • linux/Platform.c
  • netbsd/Platform.c
  • openbsd/Platform.c
  • pcp/Platform.c
💤 Files with no reviewable changes (1)
  • CPUMeter.c

Comment thread linux/Platform.c
Comment thread netbsd/Platform.c
Comment thread openbsd/Platform.c
Comment thread pcp/Platform.c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 40c24eae-e11b-48b6-9b75-3394a558438b

📥 Commits

Reviewing files that changed from the base of the PR and between 11a9036 and 87ec5dc.

📒 Files selected for processing (14)
  • Action.c
  • CPUMeter.c
  • DiskIOMeter.c
  • Meter.c
  • Meter.h
  • MeterMode.h
  • NetworkIOMeter.c
  • XUtils.c
  • XUtils.h
  • configure.ac
  • linux/Platform.c
  • netbsd/Platform.c
  • openbsd/Platform.c
  • pcp/Platform.c
💤 Files with no reviewable changes (1)
  • CPUMeter.c

Comment thread Meter.c
Comment on lines +1145 to +1147
assert(itemIndex < maxItems);
assert(itemIndex < Meter_maxItems(this));
return Meter_attributes(this)[itemIndex];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor curAttributes when resolving graph colors.

Line 1147 now hard-codes Meter_attributes(), while BarMeterMode_draw() still prefers this->curAttributes. Any meter that overrides its colors at runtime will render the wrong colors in Graph and Graph2.

Proposed fix
-   return Meter_attributes(this)[itemIndex];
+   const int* attrs = this->curAttributes ? this->curAttributes : Meter_attributes(this);
+   return attrs[itemIndex];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert(itemIndex < maxItems);
assert(itemIndex < Meter_maxItems(this));
return Meter_attributes(this)[itemIndex];
assert(itemIndex < maxItems);
assert(itemIndex < Meter_maxItems(this));
const int* attrs = this->curAttributes ? this->curAttributes : Meter_attributes(this);
return attrs[itemIndex];

When both the options "Add guest time in CPU meter percentage" and
"Detailed CPU time" are turned off, the CPU meter used to show the
"virtual" CPU time in the bar display as a glitch. Now fix it.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
The time displayed is (steal+guest), not just guest CPU time.

Regression from 3d8fa0b

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Linux and PCP platform code used to write the 'virtualized'
(steal+guest) CPU time into the CPU_METER_IRQ item when the "detailed
CPU time" option is off, which would result in a wrong color painted for
virtual CPU time in bar mode. Write the 'virtualized' CPU time to
CPU_METER_STEAL instead, which is more appropriate for the job.

Also update the "virtualized" CPU meter item in the help screen: (1) The
color is now CPU_STEAL for consistency. (2) In monochrome mode, two
dummy items are displayed before the "virtualized" word so that the
bar meter symbol mapping is correct.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Round the graph meter's dynamic scale to a power of two and print the
graph scale. For a percent graph, a "%" character is printed in place
of the scale.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
This is a prerequisite for the feature "Graph meter coloring (with
GraphData structure rework)".

powerOf2Floor() will utilize __builtin_clz() or stdc_bit_floor_ui()
(__builtin_clz() is preferred) if either is supported.

popCount8() will utilize ARM NEON instructions and x86 POPCNT
instruction if the machine supports either of them.

I am not adopting the C23 standard interface stdc_count_ones_uc() yet,
as I am not sure C libraries would implement it as fast as our version.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Rewrite the entire graph meter drawing code to support color graph
drawing in addition to the dynamic scaling (both can work together
because of the new GraphData structure design).

The colors of a graph are based on the percentages of item values of
the meter. The rounding differences of each terminal character are
addressed through the different numbers of braille dots.

Due to low resolution of the character terminal, the rasterized colors
may not look nice, but better than nothing. :)

The new GraphData structure design has two technical limitations:
* The height of a graph meter now has a limit of 8191 (terminal rows).
* If dynamic scaling is used, the "total" value or scale of a graph now
  has to align to a power of 2.

The code is designed with the anticipation that the graph height may
change at runtime. No UI or option has been implemented for that yet.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Specifically 'PIXPERROW_*' and 'GraphMeterMode_dots*' constants.
They were commented out rather than removed in the previous commit (for
ease of code reviewing). Now this commit removes the constant defines
for good.
Introduce a new meter mode (meter style) called "Graph2". The "Graph2"
name stands for "two-channel graph" or "two-quadrant graph".

This meter mode is not in the list of default supported modes. This
"Graph2" mode is intended for meters with two item values. It plots
the first item above the x-axis and the second item below the x-axis.
Items other than the first two are always hidden.

This code is written with the intention to share as much code with the
Graph meter mode as possible.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Specifically it's the DiskIORateMeter part that supports "Graph2" mode.
For the combined DiskIOMeter, the "IO rate" part can show as two-channel
graph while the "IO time" part remains a one-channel graph.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
This special case of "details" value would print as ':' in the ASCII
display mode. It is used in "Graph2" meter drawing only.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a087998d-12a4-417c-bbe2-608d1288050f

📥 Commits

Reviewing files that changed from the base of the PR and between 87ec5dc and 5e03c67.

📒 Files selected for processing (14)
  • Action.c
  • CPUMeter.c
  • DiskIOMeter.c
  • Meter.c
  • Meter.h
  • MeterMode.h
  • NetworkIOMeter.c
  • XUtils.c
  • XUtils.h
  • configure.ac
  • linux/Platform.c
  • netbsd/Platform.c
  • openbsd/Platform.c
  • pcp/Platform.c
💤 Files with no reviewable changes (1)
  • CPUMeter.c

Comment thread linux/Platform.c
Comment on lines +375 to +379
v[CPU_METER_IRQ] = 0.0; // Accounted in 'kernel'
v[CPU_METER_SOFTIRQ] = 0.0; // Accounted in 'kernel'
v[CPU_METER_STEAL] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
if (settings->accountGuestInCPUMeter) {
this->curItems = 6;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared CPU slot contract mismatch in non-detailed mode. Both platform implementations moved virtualization data into CPU_METER_STEAL while zeroing CPU_METER_IRQ, but the non-detailed CPU text path still reads CPU_METER_IRQ for vir:.

  • linux/Platform.c#L375-L379: keep non-detailed virtualization in the slot the display consumes, or update the display path to consume CPU_METER_STEAL.
  • pcp/Platform.c#L579-L585: apply the same producer/consumer slot alignment as Linux to avoid incorrect vir: output.
📍 Affects 2 files
  • linux/Platform.c#L375-L379 (this comment)
  • pcp/Platform.c#L579-L585

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Completely new feature requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Graph meter style with negative values

3 participants