From fb32021eeb6f78f01bf455231423a162c9766408 Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Sat, 30 Apr 2022 00:00:00 -0500 Subject: [PATCH] top: add variable width data display without scrolling There are times when one might want to see some task's particular variable width data. However, prior to this commit, the only way was to first turn on a field then scroll through it via repeated right arrow keystrokes. [ this also required that field to be displayed last ] Needless to say, given the potential length of some of that variable data this could be extremely cumbersome. Now with this patch, a Ctrl keystroke combination will create a separate window at the bottom of the terminal screen where such variable width data is seen in full. [ the targeted task is the 1st task displayed, which ] [ is a convention employed in some existing commands ] [ the targeted data was determined by these Ctrl key ] [ combinations: CtrlG = ctrl group; CtrlK = cmdline; ] [ CtrlU = supplementary groups; plus CtrlV = environ ] Signed-off-by: Jim Warner --- top/top.c | 156 +++++++++++++++++++++++++++++++++++++++++++++----- top/top.h | 7 +++ top/top_nls.c | 6 +- 3 files changed, 153 insertions(+), 16 deletions(-) diff --git a/top/top.c b/top/top.c index ca33aaf2..b8833f16 100644 --- a/top/top.c +++ b/top/top.c @@ -100,7 +100,19 @@ static int Monpidsidx = 0; basis (see the WIN_t). Max_lines is the total number of screen rows after deducting summary information overhead. */ /* Current terminal screen size. */ -static int Screen_cols, Screen_rows, Max_lines; +static int Screen_cols, Screen_rows, Max_lines; + + // these are used to potentially set aside a bottom 'window' +#define SCREEN_ROWS ( Screen_rows - Tagged_rsvd ) + // 1 for horizontal separator +#define TAGGED_RSVD ( 1 ) +#define TAGGED_UNDO do { Tagged_task = Tagged_rsvd = Tagged_enum = 0; \ + Fieldstab[eu_GENERIC].item = PIDS_extra; } while (0) +static int Tagged_task, + Tagged_rsvd, + Tagged_enum; +static char *Tagged_name; +static void(*Tagged_func)(void); /* This is really the number of lines needed to display the summary information (0 - nn), but is used as the relative row where we @@ -1791,7 +1803,7 @@ static struct { int width; // field width, if applicable int scale; // scaled target, if applicable const int align; // the default column alignment flag - const enum pids_item item; // the new libproc item enum identifier + enum pids_item item; // the new libproc item enum identifier } Fieldstab[] = { // these identifiers reflect the default column alignment but they really // contain the WIN_t flag used to check/change justification at run-time! @@ -1888,12 +1900,14 @@ static struct { #define eu_TREE_HID eu_LAST +4 #define eu_TREE_LVL eu_LAST +5 #define eu_TREE_ADD eu_LAST +6 +#define eu_GENERIC eu_LAST +7 , { -1, -1, -1, PIDS_CMDLINE } // str ( if Show_CMDLIN, eu_CMDLINE ) , { -1, -1, -1, PIDS_TICS_ALL_C } // ull_int ( if Show_CTIMES, eu_TICS_ALL_C ) , { -1, -1, -1, PIDS_ID_FUID } // u_int ( if a usrseltyp, eu_ID_FUID ) , { -1, -1, -1, PIDS_extra } // s_ch ( if Show_FOREST, eu_TREE_HID ) , { -1, -1, -1, PIDS_extra } // s_int ( if Show_FOREST, eu_TREE_LVL ) , { -1, -1, -1, PIDS_extra } // s_int ( if Show_FOREST, eu_TREE_ADD ) + , { -1, -1, -1, PIDS_extra } // str { special 'tag', eu_GENERIC } #undef A_left #undef A_right }; @@ -2078,6 +2092,9 @@ static void build_headers (void) { f = w->rc.sortindx; if (EU_CMD == f) ckCMDS(w); else ckITEM(f); + + // lastly, accommodate any special non-display 'tagged' needs... + if (Tagged_enum) ckITEM(Tagged_enum); } // end: VIZISw(w) if (Rc.mode_altscr) w = w->next; @@ -4331,13 +4348,14 @@ static void win_reset (WIN_t *q) { #else q->rc.maxtasks = q->usrseltyp = q->begpflg = q->begtask = q->begnext = q->focus_pid = 0; #endif - // these next two are global, not really windows based - Monpidsidx = 0; - Rc.tics_scaled = 0; - osel_clear(q); q->findstr[0] = '\0'; q->rc.combine_cpus = 0; + + // these next guys are global, not really windows based + Monpidsidx = 0; + Rc.tics_scaled = 0; + TAGGED_UNDO; } // end: win_reset @@ -4609,6 +4627,64 @@ static void wins_stage_2 (void) { } // end: wins_stage_2 + /* + * This guy manages the bottom margin window | + * & the tagged process command line display | */ +static void wins_tag_cmdline (void) { + char buf[SMLBUFSIZ]; + const char *p; + int i; + + for (i = 0; i < PIDSmaxt; i++) { + if (Tagged_task == PID_VAL(EU_PID, s_int, Curwin->ppt[i])) + break; + } + if (i < PIDSmaxt) { + snprintf(buf, sizeof(buf), "command line for pid %d:", Tagged_task); +#ifndef TAG_CMD_MUST + p = PID_VAL(eu_CMDLINE, str, Curwin->ppt[i]); + if (!p || !*p) p = "n/a"; +#else + p = CHKw(Curwin, Show_CMDLIN) ? PID_VAL(eu_CMDLINE, str, Curwin->ppt[i]) : "n/a"; +#endif + Tagged_rsvd = 1 + TAGGED_RSVD + (strlen(p) / Screen_cols); + putp(fmtmk("%s%s%-*s", tg2(0, SCREEN_ROWS), Curwin->capclr_hdr, Screen_cols, buf)); + putp(fmtmk("%s%s", tg2(0, SCREEN_ROWS + 1), Cap_clr_eos)); + putp(fmtmk("%s%s", tg2(0, SCREEN_ROWS + 1), Cap_norm)); + fputs(p, stdout); + } else { + TAGGED_UNDO; + } +} // end: wins_tag_cmdline + + + /* + * This guy manages the bottom margin window | + * showing miscellaneous variable width data | */ +static void wins_tag_generic (void) { + char buf[SMLBUFSIZ]; + const char *p; + int i; + + for (i = 0; i < PIDSmaxt; i++) { + if (Tagged_task == PID_VAL(EU_PID, s_int, Curwin->ppt[i])) + break; + } + if (i < PIDSmaxt) { + snprintf(buf, sizeof(buf), "%s for pid %d:", Tagged_name, Tagged_task); + p = PID_VAL(eu_GENERIC, str, Curwin->ppt[i]); + if (!p || !*p || !strcmp(p, "-")) p = "n/a"; + Tagged_rsvd = 1 + TAGGED_RSVD + (strlen(p) / Screen_cols); + putp(fmtmk("%s%s%-*s", tg2(0, SCREEN_ROWS), Curwin->capclr_hdr, Screen_cols, buf)); + putp(fmtmk("%s%s", tg2(0, SCREEN_ROWS + 1), Cap_clr_eos)); + putp(fmtmk("%s%s", tg2(0, SCREEN_ROWS + 1), Cap_norm)); + fputs(p, stdout); + } else { + TAGGED_UNDO; + } +} // end: wins_tag_generic + + /* * Determine if this task matches the 'u/U' selection * criteria for a given window */ @@ -4799,7 +4875,7 @@ static void forest_config (WIN_t *q) { // if some task 'above' us ended, try to maintain focus // ( but allow scrolling when there are many children ) if (q->begtask > q->focus_beg - && (Screen_rows > (q->focus_end - q->focus_beg))) { + && (SCREEN_ROWS > (q->focus_end - q->focus_beg))) { q->begtask = q->focus_beg; q->begnext = 0; // as 'mkVIZoff' but in any window } @@ -5222,6 +5298,33 @@ static void keys_global (int ch) { Rc.tics_scaled = 0; #endif break; + case kbd_CtrlG: + def = PID_VAL(EU_PID, s_int, w->ppt[w->begtask]); + // if already targeted, assume user wants to turn it off ... + if (Tagged_task && Fieldstab[eu_GENERIC].item == PIDS_CGROUP) { + TAGGED_UNDO; + } else { + Tagged_task = def; + Tagged_enum = eu_GENERIC; + Tagged_name = "control groups"; + Tagged_func = wins_tag_generic; + Fieldstab[eu_GENERIC].item = PIDS_CGROUP; + } + break; + case kbd_CtrlK: + def = PID_VAL(EU_PID, s_int, w->ppt[w->begtask]); + // if already targeted, assume user wants to turn it off ... + if (Tagged_task && Tagged_func == wins_tag_cmdline) { + TAGGED_UNDO; + } else { + Tagged_task = def; +#ifndef TAG_CMD_MUST + Tagged_enum = eu_CMDLINE; +#endif + Tagged_func = wins_tag_cmdline; + Fieldstab[eu_GENERIC].item = PIDS_extra; + } + break; case kbd_CtrlR: if (Secure_mode) show_msg(N_txt(NOT_onsecure_txt)); @@ -5248,6 +5351,32 @@ static void keys_global (int ch) { } } break; + case kbd_CtrlU: + def = PID_VAL(EU_PID, s_int, w->ppt[w->begtask]); + // if already targeted, assume user wants to turn it off ... + if (Tagged_task && Fieldstab[eu_GENERIC].item == PIDS_SUPGROUPS) { + TAGGED_UNDO; + } else { + Tagged_task = def; + Tagged_enum = eu_GENERIC; + Tagged_name = "supplementary groups"; + Tagged_func = wins_tag_generic; + Fieldstab[eu_GENERIC].item = PIDS_SUPGROUPS; + } + break; + case kbd_CtrlV: + def = PID_VAL(EU_PID, s_int, w->ppt[w->begtask]); + // if already targeted, assume user wants to turn it off ... + if (Tagged_task && Fieldstab[eu_GENERIC].item == PIDS_ENVIRON) { + TAGGED_UNDO; + } else { + Tagged_task = def; + Tagged_enum = eu_GENERIC; + Tagged_name = "environment"; + Tagged_func = wins_tag_generic; + Fieldstab[eu_GENERIC].item = PIDS_ENVIRON; + } + break; case kbd_ENTER: // these two have the effect of waking us case kbd_SPACE: // from 'pselect', refreshing the display break; // and updating any hot-plugged resources @@ -5874,7 +6003,7 @@ static int sum_unify (struct stat_stack *this, int nobuf) { * A helper function that displays cpu and/or numa node stuff | * ( so as to keep the 'summary_show' guy a reasonable size ) | */ static void do_cpus (void) { - #define noMAS (Msg_row + 1 >= Screen_rows - 1) + #define noMAS (Msg_row + 1 >= SCREEN_ROWS - 1) char tmp[MEDBUFSIZ]; int i; @@ -6088,7 +6217,8 @@ static void do_key (int ch) { { keys_global, { '?', 'B', 'd', 'E', 'e', 'f', 'g', 'H', 'h' , 'I', 'k', 'r', 's', 'X', 'Y', 'Z', '0' - , kbd_CtrlE, kbd_CtrlR, kbd_ENTER, kbd_SPACE, '\0' } }, + , kbd_CtrlE, kbd_CtrlG, kbd_CtrlK, kbd_CtrlR, kbd_CtrlU, kbd_CtrlV + , kbd_ENTER, kbd_SPACE, '\0' } }, { keys_summary, { '!', '1', '2', '3', '4', 'C', 'l', 'm', 't', '\0' } }, { keys_task, @@ -6157,7 +6287,7 @@ all_done: * 2) Display task/cpu states (maybe) * 3) Display memory & swap usage (maybe) */ static void summary_show (void) { - #define isROOM(f,n) (CHKw(Curwin, f) && Msg_row + (n) < Screen_rows - 1) + #define isROOM(f,n) (CHKw(Curwin, f) && Msg_row + (n) < SCREEN_ROWS - 1) if (Restrict_some) { #ifdef THREADED_TSK @@ -6712,7 +6842,7 @@ static void frame_make (void) { Tree_idx = Pseudo_row = Msg_row = scrlins = 0; summary_show(); - Max_lines = (Screen_rows - Msg_row) - 1; + Max_lines = (SCREEN_ROWS - Msg_row) - 1; // we're now on Msg_row so clear out any residual messages ... putp(Cap_clr_eol); @@ -6739,8 +6869,8 @@ static void frame_make (void) { PSU_CLREOS(Pseudo_row); } - if (CHKw(w, View_SCROLL) && VIZISw(Curwin)) - show_scroll(); + if (CHKw(w, View_SCROLL) && VIZISw(Curwin)) show_scroll(); + if (Tagged_task) Tagged_func(); fflush(stdout); /* we'll deem any terminal not supporting tgoto as dumb and disable diff --git a/top/top.h b/top/top.h index 06271700..b7ab4afd 100644 --- a/top/top.h +++ b/top/top.h @@ -54,6 +54,7 @@ //#define SCROLLVAR_NO /* disable intra-column horizontal scrolls */ //#define SCROLLV_BY_1 /* when scrolling left/right do not move 8 */ //#define STRINGCASENO /* case insenstive compare/locate versions */ +//#define TAG_CMD_MUST /* CtrlK (cmdline) needs proper 'c' toggle */ //#define TERMIOS_ONLY /* use native input only (just limp along) */ //#define THREADED_CPU /* separate background thread for cpu updt */ //#define THREADED_MEM /* separate background thread for mem updt */ @@ -171,8 +172,12 @@ char *strcasestr(const char *haystack, const char *needle); #define kbd_INS 138 #define kbd_DEL 139 #define kbd_CtrlE '\005' +#define kbd_CtrlG '\007' +#define kbd_CtrlK '\013' #define kbd_CtrlO '\017' #define kbd_CtrlR '\022' +#define kbd_CtrlU '\025' +#define kbd_CtrlV '\026' /* Special value in Pseudo_row to force an additional procs refresh -- used at startup and for task/thread mode transitions */ @@ -721,6 +726,8 @@ typedef struct WIN_t { //atic void wins_reflag (int what, int flg); //atic void wins_stage_1 (void); //atic void wins_stage_2 (void); +//atic void wins_tag_cmdline (void); +//atic void wins_tag_generic (void); //atic inline int wins_usrselect (const WIN_t *q, int idx); /*------ Forest View support -------------------------------------------*/ //atic void forest_adds (const int self, int level); diff --git a/top/top_nls.c b/top/top_nls.c index 23a83851..03ddedc5 100644 --- a/top/top_nls.c +++ b/top/top_nls.c @@ -651,7 +651,8 @@ static void build_uniq_nlstab (void) { " V,v,F . Toggle: '~1V~2' forest view; '~1v~2' hide/show children; '~1F~2' keep focused\n" "\n" "%s" - " W,Y,!,^E Write cfg '~1W~2'; Inspect '~1Y~2'; Combine Cpus '~1!~2'; Scale time ~1Ctrl~2+'~1E~2'\n" + " ^G,K,U,V View: ctl groups ~1^g~2; cmdline ~1^k~2; supp groups ~1^u~2; environment ~1^v~2\n" + " W,Y,!,^E Write cfg '~1W~2'; Inspect '~1Y~2'; Combine Cpus '~1!~2'; Scale time ~1^e~2'\n" " q Quit\n" " ( commands shown with '.' require a ~1visible~2 task display ~1window~2 ) \n" "Press '~1h~2' or '~1?~2' for help with ~1Windows~2,\n" @@ -722,8 +723,7 @@ static void build_uniq_nlstab (void) { . also imbedded in the translatable text (along with escape seqs) . should never themselves be translated. */ Uniq_nlstab[KEYS_helpext_fmt] = _("" - " k,r,^R, Tasks: '~1k~2' kill; '~1r~2' renice; ~1Ctrl~2+'~1R~2' renice autogroup\n" - " d or s Set update interval\n"); + " d,k,r,^R, '~1d~2' set delay; '~1k~2' kill; '~1r~2' renice; ~1Ctrl~2+'~1R~2' renice autogroup\n"); /* Translation Hint: . This Fields Management header should be 3 lines long so as