Skip to content

Commit ad6e2f8

Browse files
grayskygraysky
authored andcommitted
add user-configurable plot line colors via setup menu
Add support for customizing the color of each active plot line. Pressing Enter cycles through the available colors: Red, Cyan, Green, Yellow, Blue, Magenta, White. Colors are applied immediately at runtime without restart.
1 parent 7689023 commit ad6e2f8

6 files changed

Lines changed: 166 additions & 41 deletions

File tree

include/nvtop/interface_internal_common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ enum interface_color {
4747
green_color,
4848
red_color,
4949
blue_color,
50+
gpu_util_plot_color = 7,
51+
gpu_mem_plot_color = 8,
52+
gpu_plot_color_3 = 9,
53+
gpu_plot_color_4 = 10,
5054
};
5155

5256
struct device_window {

include/nvtop/interface_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ typedef struct nvtop_interface_option_struct {
5252
bool has_monitored_set_changed; // True if the set of monitored gpu was modified through the interface
5353
bool has_gpu_info_bar; // Show info bar with additional GPU parameters
5454
bool hide_processes_list; // Hide processes list
55+
unsigned char gpu_plot_color_idx[MAX_LINES_PER_PLOT]; // index into plot_color_names[] per plot slot
5556
} nvtop_interface_option;
5657

5758
inline bool plot_isset_draw_info(enum plot_information check_info, plot_info_to_draw to_draw) {

src/interface.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,11 @@ static void delete_all_windows(struct nvtop_interface *dwin) {
401401
free(dwin->plots);
402402
}
403403

404-
static void initialize_colors(void) {
404+
static const short plot_terminal_colors[] = {
405+
COLOR_RED, COLOR_CYAN, COLOR_GREEN, COLOR_YELLOW,
406+
COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE};
407+
408+
static void initialize_colors(const unsigned char plot_color_idx[MAX_LINES_PER_PLOT]) {
405409
start_color();
406410
short background_color;
407411
#ifdef NCURSES_VERSION
@@ -418,6 +422,10 @@ static void initialize_colors(void) {
418422
init_pair(yellow_color, COLOR_YELLOW, background_color);
419423
init_pair(blue_color, COLOR_BLUE, background_color);
420424
init_pair(magenta_color, COLOR_MAGENTA, background_color);
425+
static const short gpu_plot_pairs[MAX_LINES_PER_PLOT] = {
426+
gpu_util_plot_color, gpu_mem_plot_color, gpu_plot_color_3, gpu_plot_color_4};
427+
for (unsigned s = 0; s < MAX_LINES_PER_PLOT; ++s)
428+
init_pair(gpu_plot_pairs[s], plot_terminal_colors[plot_color_idx[s]], background_color);
421429
}
422430

423431
struct nvtop_interface *initialize_curses(unsigned total_devices, unsigned devices_count, unsigned largest_device_name,
@@ -431,7 +439,7 @@ struct nvtop_interface *initialize_curses(unsigned total_devices, unsigned devic
431439
initscr();
432440
refresh();
433441
if (interface->options.use_color && has_colors() == TRUE) {
434-
initialize_colors();
442+
initialize_colors(options.gpu_plot_color_idx);
435443
}
436444
cbreak();
437445
noecho();
@@ -457,6 +465,10 @@ struct nvtop_interface *initialize_curses(unsigned total_devices, unsigned devic
457465
return interface;
458466
}
459467

468+
void apply_plot_colors(const unsigned char plot_color_idx[MAX_LINES_PER_PLOT]) {
469+
initialize_colors(plot_color_idx);
470+
}
471+
460472
void clean_ncurses(struct nvtop_interface *interface) {
461473
endwin();
462474
delete_all_windows(interface);
@@ -2037,7 +2049,8 @@ bool show_information_messages(unsigned num_messages, const char **messages) {
20372049
initscr();
20382050
clear();
20392051
refresh();
2040-
initialize_colors();
2052+
static const unsigned char default_plot_colors[MAX_LINES_PER_PLOT] = {1, 3, 2, 4};
2053+
initialize_colors(default_plot_colors);
20412054
cbreak();
20422055
noecho();
20432056
keypad(stdscr, TRUE);

src/interface_options.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ void alloc_interface_options_internals(char *config_location, unsigned num_devic
129129
options->show_startup_messages = true;
130130
options->filter_nvtop_pid = true;
131131
options->has_gpu_info_bar = false;
132+
options->gpu_plot_color_idx[0] = 1; // Cyan
133+
options->gpu_plot_color_idx[1] = 3; // Yellow
134+
options->gpu_plot_color_idx[2] = 2; // Green
135+
options->gpu_plot_color_idx[3] = 4; // Blue
132136
if (config_location) {
133137
options->config_file_location = malloc(strlen(config_location) + 1);
134138
if (!options->config_file_location) {
@@ -174,6 +178,12 @@ static const char header_value_gpu_info_bar[] = "GPUInfoBar";
174178

175179
static const char chart_section[] = "ChartOption";
176180
static const char chart_value_reverse[] = "ReverseChart";
181+
static const char *chart_value_gpu_plot_color[MAX_LINES_PER_PLOT] = {
182+
"GpuPlotColor0", "GpuPlotColor1", "GpuPlotColor2", "GpuPlotColor3"};
183+
184+
static const char *plot_color_names[] = {
185+
"Red", "Cyan", "Green", "Yellow", "Blue", "Magenta", "White"};
186+
static const unsigned plot_color_names_count = 7;
177187

178188
static const char process_list_section[] = "ProcessListOption";
179189
static const char process_hide_nvtop_process_list[] = "HideNvtopProcessList";
@@ -254,6 +264,14 @@ static int nvtop_option_ini_handler(void *user, const char *section, const char
254264
ini_data->options->plot_left_to_right = false;
255265
}
256266
}
267+
for (unsigned s = 0; s < MAX_LINES_PER_PLOT; ++s) {
268+
if (strcmp(name, chart_value_gpu_plot_color[s]) == 0) {
269+
for (unsigned i = 0; i < plot_color_names_count; ++i) {
270+
if (strcmp(value, plot_color_names[i]) == 0)
271+
ini_data->options->gpu_plot_color_idx[s] = i;
272+
}
273+
}
274+
}
257275
}
258276
// Process List Options
259277
if (strcmp(section, process_list_section) == 0) {
@@ -404,6 +422,9 @@ bool save_interface_options_to_config_file(unsigned total_dev_count, const nvtop
404422
// Chart Options
405423
fprintf(config_file, "\n[%s]\n", chart_section);
406424
fprintf(config_file, "%s = %s\n", chart_value_reverse, boolean_string(options->plot_left_to_right));
425+
for (unsigned s = 0; s < MAX_LINES_PER_PLOT; ++s)
426+
fprintf(config_file, "%s = %s\n", chart_value_gpu_plot_color[s],
427+
plot_color_names[options->gpu_plot_color_idx[s]]);
407428

408429
// Process Options
409430
fprintf(config_file, "\n[%s]\n", process_list_section);

src/interface_setup_win.c

Lines changed: 117 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "nvtop/interface_setup_win.h"
2323
#include "nvtop/interface.h"
24+
#include <string.h>
2425
#include "nvtop/interface_internal_common.h"
2526
#include "nvtop/interface_options.h"
2627
#include "nvtop/interface_ring_buffer.h"
@@ -67,20 +68,48 @@ static const char *setup_header_option_descriptions[setup_header_options_count]
6768
// Chart Options
6869

6970
enum setup_chart_options {
70-
setup_chart_reverse,
71-
setup_chart_all_gpu,
72-
setup_chart_start_gpu_list,
71+
setup_chart_reverse = 0,
72+
setup_chart_color_start = 1, // dynamic color rows: slots 0..slot_count-1
73+
// setup_chart_all_gpu = setup_chart_color_start + slot_count (computed)
74+
// setup_chart_start_gpu_list = setup_chart_color_start + slot_count+1 (computed)
7375
setup_chart_options_count
7476
};
7577

76-
static const char *setup_chart_options_descriptions[setup_chart_options_count] = {
77-
"Reverse plot direction", "Displayed all GPUs", "Displayed GPU"};
78+
static const char *setup_chart_reverse_description = "Reverse plot direction";
79+
static const char *setup_chart_all_gpu_description = "Displayed all GPUs";
80+
static const char *setup_chart_gpu_description = "Displayed GPU";
7881

7982
static const char *setup_chart_gpu_value_descriptions[plot_information_count] = {
8083
"GPU utilization rate", "GPU memory utilization rate", "GPU encoder rate", "GPU decoder rate",
8184
"GPU temperature", "Power draw rate (current/max)", "Fan speed", "GPU clock rate",
8285
"GPU memory clock rate", "Effective load rate"};
8386

87+
static const char *chart_color_names[] = {
88+
"Red", "Cyan", "Green", "Yellow", "Blue", "Magenta", "White"};
89+
static const unsigned chart_color_names_count = 7;
90+
91+
void apply_plot_colors(const unsigned char plot_color_idx[MAX_LINES_PER_PLOT]);
92+
93+
// Build labels for each active plot slot for a given GPU's to_draw mask.
94+
// Uses the same iteration order as populate_plot_data_from_ring_buffer.
95+
static unsigned get_plot_slot_labels(plot_info_to_draw to_draw, unsigned dev_id,
96+
const char *labels[MAX_LINES_PER_PLOT]) {
97+
unsigned slot = 0;
98+
for (enum plot_information info = plot_gpu_rate; info < plot_information_count && slot < MAX_LINES_PER_PLOT; ++info) {
99+
if (plot_isset_draw_info(info, to_draw)) {
100+
char buf[64];
101+
snprintf(buf, sizeof(buf), "GPU%u %s", dev_id, setup_chart_gpu_value_descriptions[info]);
102+
// store in a static table since we need stable pointers for the caller
103+
static char label_storage[MAX_LINES_PER_PLOT][64];
104+
snprintf(label_storage[slot], sizeof(label_storage[slot]), "GPU%u %s", dev_id,
105+
setup_chart_gpu_value_descriptions[info]);
106+
labels[slot] = label_storage[slot];
107+
slot++;
108+
}
109+
}
110+
return slot;
111+
}
112+
84113
// Process List Options
85114

86115
enum setup_proc_list_options {
@@ -297,22 +326,38 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
297326
struct nvtop_interface *interface) {
298327
WINDOW *option_list_win;
299328

300-
// Fix indices for this window
301-
if (interface->setup_win.options_selected[0] > devices_count + 1)
302-
interface->setup_win.options_selected[0] = devices_count + 1;
303-
if (interface->setup_win.options_selected[0] > 0) {
304-
if (interface->setup_win.options_selected[1] >= plot_information_count)
305-
interface->setup_win.options_selected[1] = plot_information_count - 1;
306-
option_list_win = interface->setup_win.split[0];
307-
} else {
329+
// Compute how many active plot slots exist for GPU 0 (or union across all)
330+
// so we know how many color rows to show. We use the monitored count.
331+
plot_info_to_draw ref_draw = 0;
332+
for (unsigned j = 0; j < interface->monitored_dev_count; ++j)
333+
ref_draw |= interface->options.gpu_specific_opts[j].to_draw;
334+
unsigned slot_count = plot_count_draw_info(ref_draw);
335+
if (slot_count == 0) slot_count = 0; // no color rows if nothing plotted
336+
337+
// Compute dynamic row indices
338+
unsigned chart_all_gpu = setup_chart_color_start + slot_count;
339+
unsigned chart_start_gpu_list = chart_all_gpu + 1;
340+
341+
// Clamp selected row
342+
if (interface->setup_win.options_selected[0] > chart_start_gpu_list + devices_count - 1)
343+
interface->setup_win.options_selected[0] = chart_start_gpu_list + devices_count - 1;
344+
345+
if (interface->setup_win.options_selected[0] < chart_all_gpu) {
308346
if (interface->setup_win.indentation_level > 1)
309347
interface->setup_win.indentation_level = 1;
310348
option_list_win = interface->setup_win.single;
349+
} else {
350+
if (interface->setup_win.options_selected[1] >= plot_information_count)
351+
interface->setup_win.options_selected[1] = plot_information_count - 1;
352+
option_list_win = interface->setup_win.split[0];
311353
}
354+
312355
werase(interface->setup_win.single);
313356
wnoutrefresh(interface->setup_win.single);
314-
touchwin(interface->setup_win.split[0]);
315-
touchwin(interface->setup_win.split[1]);
357+
werase(interface->setup_win.split[0]);
358+
wnoutrefresh(interface->setup_win.split[0]);
359+
werase(interface->setup_win.split[1]);
360+
wnoutrefresh(interface->setup_win.split[1]);
316361

317362
wattr_set(option_list_win, A_STANDOUT, green_color, NULL);
318363
mvwprintw(option_list_win, 0, 0, "Chart Options");
@@ -329,49 +374,73 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
329374
// Reverse plot
330375
option_state = interface->options.plot_left_to_right;
331376
mvwprintw(option_list_win, setup_chart_reverse + 1, 0, "[%c] %s", option_state_char(option_state),
332-
setup_chart_options_descriptions[setup_chart_reverse]);
333-
if (interface->setup_win.indentation_level == 1 && interface->setup_win.options_selected[0] == setup_chart_reverse) {
377+
setup_chart_reverse_description);
378+
if (interface->setup_win.indentation_level == 1 &&
379+
interface->setup_win.options_selected[0] == setup_chart_reverse) {
334380
mvwchgat(option_list_win, setup_chart_reverse + 1, 0, 3, A_STANDOUT, cyan_color, NULL);
335381
}
336382

383+
// Dynamic color rows — one per active plot slot
384+
// Build slot labels from GPU 0's active metrics (representative)
385+
const char *slot_labels[MAX_LINES_PER_PLOT];
386+
plot_info_to_draw gpu0_draw = interface->monitored_dev_count > 0
387+
? interface->options.gpu_specific_opts[0].to_draw
388+
: ref_draw;
389+
get_plot_slot_labels(gpu0_draw, 0, slot_labels);
390+
391+
for (unsigned s = 0; s < slot_count && s < MAX_LINES_PER_PLOT; ++s) {
392+
unsigned row = setup_chart_color_start + s;
393+
char color_row_buf[256];
394+
snprintf(color_row_buf, sizeof(color_row_buf), "[%s] %s",
395+
chart_color_names[interface->options.gpu_plot_color_idx[s]],
396+
slot_labels[s]);
397+
mvwprintw(option_list_win, row + 1, 0, "%.*s", maxcols, color_row_buf);
398+
399+
if (interface->setup_win.indentation_level == 1 &&
400+
interface->setup_win.options_selected[0] == row) {
401+
mvwchgat(option_list_win, row + 1, 0,
402+
(int)strlen(chart_color_names[interface->options.gpu_plot_color_idx[s]]) + 2,
403+
A_STANDOUT, cyan_color, NULL);
404+
}
405+
}
406+
337407
// Set for all GPUs at once
338-
if (interface->setup_win.options_selected[0] == setup_chart_all_gpu) {
408+
if (interface->setup_win.options_selected[0] == chart_all_gpu) {
339409
if (interface->setup_win.indentation_level == 1)
340410
wattr_set(option_list_win, A_STANDOUT, cyan_color, NULL);
341411
if (interface->setup_win.indentation_level == 2)
342412
wattr_set(option_list_win, A_BOLD, cyan_color, NULL);
343413
}
344-
mvwaddch(option_list_win, setup_chart_all_gpu + 1, 1, ACS_HLINE);
414+
mvwaddch(option_list_win, chart_all_gpu + 1, 1, ACS_HLINE);
345415
waddch(option_list_win, '>');
346416
wstandend(option_list_win);
347-
wprintw(option_list_win, " %s", setup_chart_options_descriptions[setup_chart_all_gpu]);
417+
wprintw(option_list_win, " %s", setup_chart_all_gpu_description);
348418

349419
// GPUs as a list
350420
for (unsigned i = 0; i < devices_count; ++i) {
351-
if (interface->setup_win.options_selected[0] == setup_chart_start_gpu_list + i) {
421+
if (interface->setup_win.options_selected[0] == chart_start_gpu_list + i) {
352422
if (interface->setup_win.indentation_level == 1)
353423
wattr_set(option_list_win, A_STANDOUT, cyan_color, NULL);
354424
if (interface->setup_win.indentation_level == 2)
355425
wattr_set(option_list_win, A_BOLD, cyan_color, NULL);
356426
}
357-
mvwaddch(option_list_win, setup_chart_start_gpu_list + 1 + i, 1, ACS_HLINE);
427+
mvwaddch(option_list_win, chart_start_gpu_list + 1 + i, 1, ACS_HLINE);
358428
waddch(option_list_win, '>');
359429
wstandend(option_list_win);
360-
wprintw(option_list_win, " %s %u", setup_chart_options_descriptions[setup_chart_start_gpu_list], i);
430+
wprintw(option_list_win, " %s %u", setup_chart_gpu_description, i);
361431
}
362432
wnoutrefresh(option_list_win);
363433

364434
// Window of list of metric to display in chart (4 maximum)
365-
if (interface->setup_win.options_selected[0] >= setup_chart_all_gpu) {
435+
if (interface->setup_win.options_selected[0] >= chart_all_gpu) {
366436
WINDOW *value_list_win = interface->setup_win.split[1];
367437
wattr_set(value_list_win, A_STANDOUT, green_color, NULL);
368438
mvwprintw(value_list_win, 0, 0, "Metric Displayed in Graph");
369439
getmaxyx(value_list_win, tmp, maxcols);
370-
unsigned selected_gpu = interface->setup_win.options_selected[0] - setup_chart_start_gpu_list;
371-
if (interface->setup_win.options_selected[0] == setup_chart_all_gpu) {
440+
unsigned selected_gpu = interface->setup_win.options_selected[0] - chart_start_gpu_list;
441+
if (interface->setup_win.options_selected[0] == chart_all_gpu) {
372442
wprintw(value_list_win, " (All GPUs)");
373443
} else {
374-
// Get the selected device
375444
struct gpu_info *device;
376445
unsigned index = 0;
377446
list_for_each_entry(device, devices, list) {
@@ -393,7 +462,7 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
393462
wstandend(value_list_win);
394463

395464
for (enum plot_information i = plot_gpu_rate; i < plot_information_count; ++i) {
396-
if (interface->setup_win.options_selected[0] == setup_chart_all_gpu) {
465+
if (interface->setup_win.options_selected[0] == chart_all_gpu) {
397466
plot_info_to_draw draw_union = 0, draw_intersection = 0xffff;
398467
for (unsigned j = 0; j < devices_count; ++j) {
399468
draw_union |= interface->options.gpu_specific_opts[j].to_draw;
@@ -757,15 +826,31 @@ void handle_setup_win_keypress(int keyId, struct nvtop_interface *interface) {
757826
}
758827
// Chart Options
759828
if (interface->setup_win.selected_section == setup_chart_selected) {
829+
// Recompute dynamic indices (same logic as draw function)
830+
plot_info_to_draw ref_draw_kp = 0;
831+
for (unsigned j = 0; j < interface->monitored_dev_count; ++j)
832+
ref_draw_kp |= interface->options.gpu_specific_opts[j].to_draw;
833+
unsigned slot_count_kp = plot_count_draw_info(ref_draw_kp);
834+
unsigned chart_all_gpu_kp = setup_chart_color_start + slot_count_kp;
835+
unsigned chart_gpu_list_kp = chart_all_gpu_kp + 1;
836+
760837
if (interface->setup_win.indentation_level == 1) {
761838
if (interface->setup_win.options_selected[0] == setup_chart_reverse) {
762839
interface->options.plot_left_to_right = !interface->options.plot_left_to_right;
763840
}
764-
if (interface->setup_win.options_selected[0] >= setup_chart_all_gpu) {
841+
// Color rows
842+
unsigned sel = interface->setup_win.options_selected[0];
843+
if (sel >= setup_chart_color_start && sel < chart_all_gpu_kp) {
844+
unsigned slot = sel - setup_chart_color_start;
845+
interface->options.gpu_plot_color_idx[slot] =
846+
(interface->options.gpu_plot_color_idx[slot] + 1) % chart_color_names_count;
847+
apply_plot_colors(interface->options.gpu_plot_color_idx);
848+
}
849+
if (interface->setup_win.options_selected[0] >= chart_all_gpu_kp) {
765850
handle_setup_win_keypress(KEY_RIGHT, interface);
766851
}
767852
} else if (interface->setup_win.indentation_level == 2) {
768-
if (interface->setup_win.options_selected[0] == setup_chart_all_gpu) {
853+
if (interface->setup_win.options_selected[0] == chart_all_gpu_kp) {
769854
plot_info_to_draw draw_intersection = 0xffff;
770855
for (unsigned j = 0; j < interface->monitored_dev_count; ++j) {
771856
draw_intersection = draw_intersection & interface->options.gpu_specific_opts[j].to_draw;
@@ -784,8 +869,8 @@ void handle_setup_win_keypress(int keyId, struct nvtop_interface *interface) {
784869
}
785870
}
786871
}
787-
if (interface->setup_win.options_selected[0] > setup_chart_all_gpu) {
788-
unsigned selected_gpu = interface->setup_win.options_selected[0] - setup_chart_start_gpu_list;
872+
if (interface->setup_win.options_selected[0] > chart_all_gpu_kp) {
873+
unsigned selected_gpu = interface->setup_win.options_selected[0] - chart_gpu_list_kp;
789874
if (plot_isset_draw_info(interface->setup_win.options_selected[1],
790875
interface->options.gpu_specific_opts[selected_gpu].to_draw))
791876
interface->options.gpu_specific_opts[selected_gpu].to_draw = plot_remove_draw_info(

0 commit comments

Comments
 (0)