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 <james.warner@comcast.net>
This commit is contained in:
Jim Warner 2022-04-30 00:00:00 -05:00 committed by Craig Small
parent 27f5904edd
commit fb32021eeb
3 changed files with 153 additions and 16 deletions

156
top/top.c
View File

@ -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

View File

@ -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);

View File

@ -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