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"
@@ -68,19 +69,43 @@ static const char *setup_header_option_descriptions[setup_header_options_count]
6869
6970enum setup_chart_options {
7071 setup_chart_reverse ,
71- setup_chart_all_gpu ,
72- setup_chart_start_gpu_list ,
73- setup_chart_options_count
72+ setup_chart_color_start , // 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)
7475};
7576
76- static const char * setup_chart_options_descriptions [setup_chart_options_count ] = {
77- "Reverse plot direction" , "Displayed all GPUs" , "Displayed GPU" };
77+ static const char * setup_chart_reverse_description = "Reverse plot direction" ;
78+ static const char * setup_chart_all_gpu_description = "Displayed all GPUs" ;
79+ static const char * setup_chart_gpu_description = "Displayed GPU" ;
7880
7981static const char * setup_chart_gpu_value_descriptions [plot_information_count ] = {
8082 "GPU utilization rate" , "GPU memory utilization rate" , "GPU encoder rate" , "GPU decoder rate" ,
8183 "GPU temperature" , "Power draw rate (current/max)" , "Fan speed" , "GPU clock rate" ,
8284 "GPU memory clock rate" , "Effective load rate" };
8385
86+ static const char * chart_color_names [] = {"Red" , "Cyan" , "Green" , "Yellow" , "Blue" , "Magenta" , "White" };
87+ static const unsigned chart_color_names_count = ARRAY_SIZE (chart_color_names );
88+
89+ // Build labels for each active plot slot for a given GPU's to_draw mask.
90+ // Uses the same iteration order as populate_plot_data_from_ring_buffer.
91+ static unsigned get_plot_slot_labels (plot_info_to_draw to_draw , unsigned dev_id ,
92+ const char * labels [MAX_LINES_PER_PLOT ]) {
93+ unsigned slot = 0 ;
94+ for (enum plot_information info = plot_gpu_rate ; info < plot_information_count && slot < MAX_LINES_PER_PLOT ; ++ info ) {
95+ if (plot_isset_draw_info (info , to_draw )) {
96+ char buf [64 ];
97+ snprintf (buf , sizeof (buf ), "GPU%u %s" , dev_id , setup_chart_gpu_value_descriptions [info ]);
98+ // store in a static table since we need stable pointers for the caller
99+ static char label_storage [MAX_LINES_PER_PLOT ][64 ];
100+ snprintf (label_storage [slot ], sizeof (label_storage [slot ]), "GPU%u %s" , dev_id ,
101+ setup_chart_gpu_value_descriptions [info ]);
102+ labels [slot ] = label_storage [slot ];
103+ slot ++ ;
104+ }
105+ }
106+ return slot ;
107+ }
108+
84109// Process List Options
85110
86111enum setup_proc_list_options {
@@ -297,22 +322,38 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
297322 struct nvtop_interface * interface ) {
298323 WINDOW * option_list_win ;
299324
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 {
325+ // Compute how many active plot slots exist for GPU 0 (or union across all)
326+ // so we know how many color rows to show. We use the monitored count.
327+ plot_info_to_draw ref_draw = 0 ;
328+ for (unsigned j = 0 ; j < interface -> monitored_dev_count ; ++ j )
329+ ref_draw |= interface -> options .gpu_specific_opts [j ].to_draw ;
330+ unsigned slot_count = plot_count_draw_info (ref_draw );
331+ if (slot_count == 0 ) slot_count = 0 ; // no color rows if nothing plotted
332+
333+ // Compute dynamic row indices
334+ unsigned chart_all_gpu = setup_chart_color_start + slot_count ;
335+ unsigned chart_start_gpu_list = chart_all_gpu + 1 ;
336+
337+ // Clamp selected row
338+ if (interface -> setup_win .options_selected [0 ] > chart_start_gpu_list + devices_count - 1 )
339+ interface -> setup_win .options_selected [0 ] = chart_start_gpu_list + devices_count - 1 ;
340+
341+ if (interface -> setup_win .options_selected [0 ] < chart_all_gpu ) {
308342 if (interface -> setup_win .indentation_level > 1 )
309343 interface -> setup_win .indentation_level = 1 ;
310344 option_list_win = interface -> setup_win .single ;
345+ } else {
346+ if (interface -> setup_win .options_selected [1 ] >= plot_information_count )
347+ interface -> setup_win .options_selected [1 ] = plot_information_count - 1 ;
348+ option_list_win = interface -> setup_win .split [0 ];
311349 }
350+
312351 werase (interface -> setup_win .single );
313352 wnoutrefresh (interface -> setup_win .single );
314- touchwin (interface -> setup_win .split [0 ]);
315- touchwin (interface -> setup_win .split [1 ]);
353+ werase (interface -> setup_win .split [0 ]);
354+ wnoutrefresh (interface -> setup_win .split [0 ]);
355+ werase (interface -> setup_win .split [1 ]);
356+ wnoutrefresh (interface -> setup_win .split [1 ]);
316357
317358 wattr_set (option_list_win , A_STANDOUT , green_color , NULL );
318359 mvwprintw (option_list_win , 0 , 0 , "Chart Options" );
@@ -329,49 +370,73 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
329370 // Reverse plot
330371 option_state = interface -> options .plot_left_to_right ;
331372 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 ) {
373+ setup_chart_reverse_description );
374+ if (interface -> setup_win .indentation_level == 1 &&
375+ interface -> setup_win .options_selected [0 ] == setup_chart_reverse ) {
334376 mvwchgat (option_list_win , setup_chart_reverse + 1 , 0 , 3 , A_STANDOUT , cyan_color , NULL );
335377 }
336378
379+ // Dynamic color rows — one per active plot slot
380+ // Build slot labels from GPU 0's active metrics (representative)
381+ const char * slot_labels [MAX_LINES_PER_PLOT ];
382+ plot_info_to_draw gpu0_draw = interface -> monitored_dev_count > 0
383+ ? interface -> options .gpu_specific_opts [0 ].to_draw
384+ : ref_draw ;
385+ get_plot_slot_labels (gpu0_draw , 0 , slot_labels );
386+
387+ for (unsigned s = 0 ; s < slot_count && s < MAX_LINES_PER_PLOT ; ++ s ) {
388+ unsigned row = setup_chart_color_start + s ;
389+ char color_row_buf [256 ];
390+ snprintf (color_row_buf , sizeof (color_row_buf ), "[%s] %s" ,
391+ chart_color_names [interface -> options .gpu_plot_color_idx [s ]],
392+ slot_labels [s ]);
393+ mvwprintw (option_list_win , row + 1 , 0 , "%.*s" , maxcols , color_row_buf );
394+
395+ if (interface -> setup_win .indentation_level == 1 &&
396+ interface -> setup_win .options_selected [0 ] == row ) {
397+ mvwchgat (option_list_win , row + 1 , 0 ,
398+ (int )strlen (chart_color_names [interface -> options .gpu_plot_color_idx [s ]]) + 2 ,
399+ A_STANDOUT , cyan_color , NULL );
400+ }
401+ }
402+
337403 // Set for all GPUs at once
338- if (interface -> setup_win .options_selected [0 ] == setup_chart_all_gpu ) {
404+ if (interface -> setup_win .options_selected [0 ] == chart_all_gpu ) {
339405 if (interface -> setup_win .indentation_level == 1 )
340406 wattr_set (option_list_win , A_STANDOUT , cyan_color , NULL );
341407 if (interface -> setup_win .indentation_level == 2 )
342408 wattr_set (option_list_win , A_BOLD , cyan_color , NULL );
343409 }
344- mvwaddch (option_list_win , setup_chart_all_gpu + 1 , 1 , ACS_HLINE );
410+ mvwaddch (option_list_win , chart_all_gpu + 1 , 1 , ACS_HLINE );
345411 waddch (option_list_win , '>' );
346412 wstandend (option_list_win );
347- wprintw (option_list_win , " %s" , setup_chart_options_descriptions [ setup_chart_all_gpu ] );
413+ wprintw (option_list_win , " %s" , setup_chart_all_gpu_description );
348414
349415 // GPUs as a list
350416 for (unsigned i = 0 ; i < devices_count ; ++ i ) {
351- if (interface -> setup_win .options_selected [0 ] == setup_chart_start_gpu_list + i ) {
417+ if (interface -> setup_win .options_selected [0 ] == chart_start_gpu_list + i ) {
352418 if (interface -> setup_win .indentation_level == 1 )
353419 wattr_set (option_list_win , A_STANDOUT , cyan_color , NULL );
354420 if (interface -> setup_win .indentation_level == 2 )
355421 wattr_set (option_list_win , A_BOLD , cyan_color , NULL );
356422 }
357- mvwaddch (option_list_win , setup_chart_start_gpu_list + 1 + i , 1 , ACS_HLINE );
423+ mvwaddch (option_list_win , chart_start_gpu_list + 1 + i , 1 , ACS_HLINE );
358424 waddch (option_list_win , '>' );
359425 wstandend (option_list_win );
360- wprintw (option_list_win , " %s %u" , setup_chart_options_descriptions [ setup_chart_start_gpu_list ] , i );
426+ wprintw (option_list_win , " %s %u" , setup_chart_gpu_description , i );
361427 }
362428 wnoutrefresh (option_list_win );
363429
364430 // Window of list of metric to display in chart (4 maximum)
365- if (interface -> setup_win .options_selected [0 ] >= setup_chart_all_gpu ) {
431+ if (interface -> setup_win .options_selected [0 ] >= chart_all_gpu ) {
366432 WINDOW * value_list_win = interface -> setup_win .split [1 ];
367433 wattr_set (value_list_win , A_STANDOUT , green_color , NULL );
368434 mvwprintw (value_list_win , 0 , 0 , "Metric Displayed in Graph" );
369435 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 ) {
436+ unsigned selected_gpu = interface -> setup_win .options_selected [0 ] - chart_start_gpu_list ;
437+ if (interface -> setup_win .options_selected [0 ] == chart_all_gpu ) {
372438 wprintw (value_list_win , " (All GPUs)" );
373439 } else {
374- // Get the selected device
375440 struct gpu_info * device ;
376441 unsigned index = 0 ;
377442 list_for_each_entry (device , devices , list ) {
@@ -393,7 +458,7 @@ static void draw_setup_window_chart(unsigned devices_count, struct list_head *de
393458 wstandend (value_list_win );
394459
395460 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 ) {
461+ if (interface -> setup_win .options_selected [0 ] == chart_all_gpu ) {
397462 plot_info_to_draw draw_union = 0 , draw_intersection = 0xffff ;
398463 for (unsigned j = 0 ; j < devices_count ; ++ j ) {
399464 draw_union |= interface -> options .gpu_specific_opts [j ].to_draw ;
@@ -757,15 +822,31 @@ void handle_setup_win_keypress(int keyId, struct nvtop_interface *interface) {
757822 }
758823 // Chart Options
759824 if (interface -> setup_win .selected_section == setup_chart_selected ) {
825+ // Recompute dynamic indices (same logic as draw function)
826+ plot_info_to_draw ref_draw_kp = 0 ;
827+ for (unsigned j = 0 ; j < interface -> monitored_dev_count ; ++ j )
828+ ref_draw_kp |= interface -> options .gpu_specific_opts [j ].to_draw ;
829+ unsigned slot_count_kp = plot_count_draw_info (ref_draw_kp );
830+ unsigned chart_all_gpu_kp = setup_chart_color_start + slot_count_kp ;
831+ unsigned chart_gpu_list_kp = chart_all_gpu_kp + 1 ;
832+
760833 if (interface -> setup_win .indentation_level == 1 ) {
761834 if (interface -> setup_win .options_selected [0 ] == setup_chart_reverse ) {
762835 interface -> options .plot_left_to_right = !interface -> options .plot_left_to_right ;
763836 }
764- if (interface -> setup_win .options_selected [0 ] >= setup_chart_all_gpu ) {
837+ // Color rows
838+ unsigned sel = interface -> setup_win .options_selected [0 ];
839+ if (sel >= setup_chart_color_start && sel < chart_all_gpu_kp ) {
840+ unsigned slot = sel - setup_chart_color_start ;
841+ interface -> options .gpu_plot_color_idx [slot ] =
842+ (interface -> options .gpu_plot_color_idx [slot ] + 1 ) % chart_color_names_count ;
843+ apply_plot_colors (interface -> options .gpu_plot_color_idx );
844+ }
845+ if (interface -> setup_win .options_selected [0 ] >= chart_all_gpu_kp ) {
765846 handle_setup_win_keypress (KEY_RIGHT , interface );
766847 }
767848 } else if (interface -> setup_win .indentation_level == 2 ) {
768- if (interface -> setup_win .options_selected [0 ] == setup_chart_all_gpu ) {
849+ if (interface -> setup_win .options_selected [0 ] == chart_all_gpu_kp ) {
769850 plot_info_to_draw draw_intersection = 0xffff ;
770851 for (unsigned j = 0 ; j < interface -> monitored_dev_count ; ++ j ) {
771852 draw_intersection = draw_intersection & interface -> options .gpu_specific_opts [j ].to_draw ;
@@ -784,8 +865,8 @@ void handle_setup_win_keypress(int keyId, struct nvtop_interface *interface) {
784865 }
785866 }
786867 }
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 ;
868+ if (interface -> setup_win .options_selected [0 ] > chart_all_gpu_kp ) {
869+ unsigned selected_gpu = interface -> setup_win .options_selected [0 ] - chart_gpu_list_kp ;
789870 if (plot_isset_draw_info (interface -> setup_win .options_selected [1 ],
790871 interface -> options .gpu_specific_opts [selected_gpu ].to_draw ))
791872 interface -> options .gpu_specific_opts [selected_gpu ].to_draw = plot_remove_draw_info (
0 commit comments