Skip to content

Implement different display types for bar meters#1794

Open
rustedusted wants to merge 5 commits into
htop-dev:mainfrom
rustedusted:feature-647
Open

Implement different display types for bar meters#1794
rustedusted wants to merge 5 commits into
htop-dev:mainfrom
rustedusted:feature-647

Conversation

@rustedusted

Copy link
Copy Markdown

Allow bar meters to enable "sub-pixel" rendering.

Bar meter styles can be changed in setup (F2) under Display Options.

Co-Authored-By: Benny Baumann BenBE@geshi.org

@rustedusted

Copy link
Copy Markdown
Author

referencing with issue #647
the previous merge request was a mess

@rustedusted

Copy link
Copy Markdown
Author

One more thing
the bar type option is visible in F2 even when we do htop -U should i disable it when opened with -U
or keep it there??

the function didn't have any if statement in it so felt awkward to do that
it's just a line of difference to add

@rustedusted

Copy link
Copy Markdown
Author

[x] Resolve the conflict marker in the latest commit(not using the previous commit)
[x] Handle storing the setting to disk
[ ] Proper guarding against invalid barType settings in Meter.c(i do not understand this, i just put asserts in the Meter.c line 168)(please let me know if there's anything else)
[x] Handle when htop is configured without Unicode support(done using header guards #ifdef HAVE_LIBNCURSESW)
[x] Update Commit message

@BenBE BenBE linked an issue Oct 25, 2025 that may be closed by this pull request
@BenBE BenBE added the enhancement Extension or improvement to existing feature label Oct 25, 2025
Comment thread Settings.c
Comment thread Settings.c Outdated
this->hideFunctionBar = atoi(option[1]);
#ifdef HAVE_LIBNCURSESW
} else if (String_eq(option[0], "bar_type")) {
this->barType = atoi(option[1]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The value read here isn't protected against an invalid configuration file. Please ensure, that the value in this->barType is sanitized to be a valid bar type index.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be a good idea to set this->barType to 0 whenever we read an invalid value from the config. This makes the field forward-compatible to any future extensions of the list. Also, we should use strtol for parsing this field to get a defined behavior on integer overflow.

Unfortunately we don't have a macro to easily produce code like this one:

{
   long value = strtol(option[1], NULL, 10);
   if (value < 0 || value > BAR_METER_NUM_STYLES)
      value = 0;
   this->barType = (unsigned int)value;
}

Comment thread Meter.c Outdated
Comment thread Meter.c Outdated
Comment thread Meter.c
Comment on lines 181 to +186
blockSizes[i] = ceil((value / this->total) * w);
blockSizes[i] = MINIMUM(blockSizes[i], w - offset);

#ifdef HAVE_LIBNCURSESW
extraWidth = (int)ceil((value / this->total) * w * barLen) % barLen;
#endif

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this needs some minor update. The extraWidth calculation probably should re-use the value calculated in the blockSizes[i] assignments above.

Comment thread Meter.c Outdated
Comment on lines +93 to +99
L"||||||||",
L"########",
L"⣿⡀⡄⡆⡇⣇⣧⣷",
L"█░░▒▒▓▓█",
L"█▏▎▍▌▋▊▉",
L"█▁▂▃▄▅▆▇",
L"█▌▌▌▌███",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Having the full block final seems more natural. Any reason to put it first and skip the per-profile blank instead?

Comment thread DisplayOptionsPanel.c Outdated
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));

#ifdef HAVE_LIBNCURSESW
Panel_add(super, (Object*) NumberItem_newByRef("Bar Type (0-6)", &(settings->barType), 0, 0, 6));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe 0 = default to avoid having this needing to updated whenever there's a new character theme.

Comment thread Settings.h Outdated
#endif
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
#ifdef HAVE_LIBNCURSESW
int barType;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any reason not to use unsigned int for this field?

Comment thread Meter.c
assert(settings->barType < ( sizeof(bars) / sizeof(wchar_t*) ));
assert(settings->barType >= 0);
const wchar_t* currBar = bars[settings->barType];
int barLen = (int)wcslen(currBar);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I consider this function call redundant when you have declared the list of bars to have equal length.

Not sure if this is what you intended, but I think the bar meter characters can be defined with variable widths if you wish.

Like this:

static const wchar_t* BarMeterMode_drawStyles[] = {
   L"|",
   L"#",
   L"⡀⡄⡆⡇⣇⣧⣷⣿",
   L"░▒▓█",
   L"▏▎▍▌▋▊▉█",
   L"▁▂▃▄▅▆▇█",
   L"▌█",
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The bars currently have all the same length, but the intention is to actually allow for bars to contain different number of symbols.

Comment thread Meter.c Outdated
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove this include line. wchar.h is better included indirectly via ProvideCurses.h.

Comment thread Meter.c Outdated
#include <float.h>
#include <limits.h> // IWYU pragma: keep
#include <math.h>
#include <stdio.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I didn't see any stdio.h function being used at all in this PR.

@BenBE

BenBE commented Oct 25, 2025

Copy link
Copy Markdown
Member

Please squash/fixup changes into the original commit by rebasing. Details are in the style guide.

@rustedusted rustedusted force-pushed the feature-647 branch 2 times, most recently from 39d372a to 5f1a7a9 Compare October 26, 2025 07:39
Allow bar meters to enable "sub-pixel" rendering.

Bar meter styles can be changed in setup (F2) under Display Options.

Co-Authored-By: Benny Baumann <BenBE@geshi.org>
@rustedusted

Copy link
Copy Markdown
Author

here's the latest commit that contains all the suggested things

Comment thread DisplayOptionsPanel.c Outdated
Comment thread Meter.c
assert(settings->barType < ( sizeof(bars) / sizeof(wchar_t*) ));
assert(settings->barType >= 0);
const wchar_t* currBar = bars[settings->barType];
int barLen = (int)wcslen(currBar);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The bars currently have all the same length, but the intention is to actually allow for bars to contain different number of symbols.

Comment thread Meter.c Outdated
Comment thread Meter.c Outdated
Comment thread Meter.c Outdated
Comment thread MeterMode.h
(1 << LED_METERMODE) | \
0) // Avoids edits when updating

#define BAR_METER_NUM_STYLES 7

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
#define BAR_METER_NUM_STYLES 7
extern const size_t bar_meter_num_styles;

And below the definition of bars' for the meter styles in Meter.c`:

const size_t bar_meter_num_styles = sizeof(bars) / sizeof(*bars);

This avoids an additional point of manual updating.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@BenBE This has the problem that when compiling Setting.c, compiler cannot inline the constant but has to refer to the external variable for the number. In other words, the compiled code would be sub-optimal.

I think the better approach is to move the definitions of the bar styles into the header. Look at the unitPrefixes definition in XUtils.h for what I mean.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm aware of this downside, but AFAICT there are no real places where looking up this constant happens inside a tight loop …

Comment thread Meter.c Outdated
} else {
}
#ifdef HAVE_LIBNCURSESW
else if(CRT_utf8 && settings->barType) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The whole block of else if (CRT_utf8 && settings->barType) { ... } can merge with the else { ... } block below so we can reduce duplicate code.

Comment thread Settings.h
#endif
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
#ifdef HAVE_LIBNCURSESW
unsigned int barType;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since the proposed bar drawing styles has | (vertical bar, the default), and # (number sign), both can be drawn in ASCII-only mode, I think we can remove the #ifdef HAVE_LIBNCURSESW conditional and allow the setting to be available in both ASCII-only build and Unicode build of htop.

Note: only the first two drawing styles can be selected in ASCII. The rest can be guarded in the #ifdef HAVE_LIBNCURSESW so they require Unicode to enable:

#ifdef HAVE_LIBNCURSESW
static const wchar_t* barMeterDrawStyles[] = {
   L"|",
   L"#",
   L"⡀⡄⡆⡇⣇⣧⣷⣿",
   L"░▒▓█",
   L"▏▎▍▌▋▊▉█",
   L"▁▂▃▄▅▆▇█",
   L"▌█",
};
#else
static const char* barMeterDrawStyles[] = {
   "|",
   "#"
};
#endif
static const size_t numBarMeterStyles = ARRAYSIZE(barMeterDrawStyles);

Comment thread Meter.c
for (int j = offset; j < nextOffset; j++)
for (int j = offset; j < nextOffset; j++) {
if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is one thing I wish to add when the feature of changing bar drawing character is added. It's this:

Suggested change
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME && Meter_maxItems(this) != 1) {

Make the bar character changeable for meters with only one item.

--- a/Meter.h
+++ b/Meter.h
@@ -100,6 +100,7 @@ typedef struct MeterClass_ {
 #define Meter_attributes(this_)        As_Meter(this_)->attributes
 #define Meter_name(this_)              As_Meter(this_)->name
 #define Meter_uiName(this_)            As_Meter(this_)->uiName
+#define Meter_maxItems(this_)          As_Meter(this_)->maxItems
 #define Meter_isMultiColumn(this_)     As_Meter(this_)->isMultiColumn
 #define Meter_isPercentChart(this_)    As_Meter(this_)->isPercentChart
 

rustedusted and others added 4 commits October 26, 2025 21:44
Co-authored-by: BenBE <BenBE@geshi.org>
Co-authored-by: BenBE <BenBE@geshi.org>
Co-authored-by: BenBE <BenBE@geshi.org>
Co-authored-by: BenBE <BenBE@geshi.org>
Comment thread Meter.c
Comment on lines +93 to +97
L"⣿⡀⡄⡆⡇⣇⣧⣷",
L"█░░▒▒▓▓█",
L"█▏▎▍▌▋▊▉",
L"█▁▂▃▄▅▆▇",
L"█▌▌▌▌███"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Does this mean that there can be fractional bar blocks, i.e. the bar doesn't fill an entire character but only a part of it, increasing horizontal resolution? I know these bars primarily from the CPU meter and they have sections of different colours. While that could work for the vertically sliced blocks (foreground is the block in the left colour, background is the colour of the following section), it probably can't work with the dotted style. And still, if a bar section is shorter than a full character, such a character would need more than 2 colours and foreground/background isn't enough to properly render it.

Personally, I'd already be happy if I could replace that thin vertical line with something more filling that would stretch from the left to the right edge of the character. Doesn't need to be the whole height like █ (U+2588), could also be ■ (U+25A0) or even ▬ (U+25AC).

@Explorer09 Explorer09 May 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The technical limitations have removed the possibility of showing two colors in a single block. (In ANSI color terminal, there's no "bright" colors for background, only the foreground can be set to a "bright" color).

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

Labels

enhancement Extension or improvement to existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom character for meter (instead of "|")

4 participants