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
6970enum 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
7982static 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
86115enum 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