top: allow collapsible forest view children, pgm logic

The issue cited below really dealt with preserving the
'Other filter' criteria in the rcfile. But as an aside
the htop 'F6' feature (collapsed children) was raised.

I took that as an implied challenge and decided to try
implementing a similar feature in top. So, this commit
will now provide a brand new forest view toggle ('^V')
which will be used to collapse/expand forked children.

[ this patch will also lead to additional patches in ]
[ support of more rational vertical scrolling, since ]
[ many more tasks might now be hidden in some window ]

Reference(s):
. where this secondary issue was raised
https://gitlab.com/procps-ng/procps/issues/99

Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
Jim Warner 2018-06-19 00:00:00 -05:00 committed by Craig Small
parent 57f0ff367c
commit 14eb7275d9
4 changed files with 88 additions and 11 deletions

View File

@ -4682,13 +4682,20 @@ static void wins_stage_2 (void) {
/*###### Forest View support ###########################################*/
/*
* We try to keep most existing code unaware of our activities
* ( plus, maintain alphabetical order with carefully chosen )
* ( function names: forest_a, forest_b, forest_c & forest_d )
* ( each with exactly one letter more than its predecessor! ) */
* We try keeping most existing code unaware of these activities
* ( plus, maintain alphabetical order within carefully chosen )
* ( function names of: forest_a, forest_b, forest_c, forest_d )
* ( with each name exactly 1 letter more than its predecessor ) */
static proc_t **Seed_ppt; // temporary win ppt pointer
static proc_t **Tree_ppt; // forest_create will resize
static int Tree_idx; // frame_make resets to zero
/* those next two support collapse/expand children. the Hide_pid
array holds parent pids whose children have been manipulated.
positive pid values represent parents with collapsed children
while a negative pid value means children have been expanded.
( both of these are managed under the 'keys_task()' routine ) */
static int *Hide_pid; // collapsible process array
static int Hide_tot; // total used in above array
/*
* This little recursive guy is the real forest view workhorse.
@ -4730,25 +4737,49 @@ static int forest_based (const proc_t **x, const proc_t **y) {
/*
* This routine is responsible for preparing the proc_t's for
* a forest display in the designated window. Upon completion,
* he'll replace the original window ppt with our specially
* ordered forest version. */
* a forest display in a designated window. After completion,
* he will replace the original window ppt with our specially
* ordered forest version. He also marks any hidden children! */
static void forest_create (WIN_t *q) {
static int hwmsav;
int i;
int i, j;
Seed_ppt = q->ppt; // avoid passing WIN_t ptrs
if (!Tree_idx) { // do just once per frame
if (hwmsav < Frame_maxtask) { // grow, but never shrink
hwmsav = Frame_maxtask;
Tree_ppt = alloc_r(Tree_ppt, sizeof(proc_t*) * hwmsav);
Hide_pid = alloc_r(Hide_pid, sizeof(int) * hwmsav);
}
#ifndef TREE_SCANALL
qsort(Seed_ppt, Frame_maxtask, sizeof(proc_t*), (QFP_t)forest_based);
#endif
for (i = 0; i < Frame_maxtask; i++) // avoid any hidepid distortions
if (!Seed_ppt[i]->pad_3) // identify real or pretend trees
forest_adds(i, 0); // add as parent plus its children
/* we're borrowing some pad bytes in the proc_t,
pad_2: 'x' means a collapsed thread, 'z' means an unseen child
pad_3: where level number is stored (0 - 100) */
for (i = 0; i < Hide_tot; i++) {
if (Hide_pid[i] > 0) {
for (j = 0; j < Frame_maxtask; j++) {
if (Tree_ppt[j]->tid == Hide_pid[i]) {
int idx = j;
char lvl = Tree_ppt[j]->pad_3;
Tree_ppt[j]->pad_2 = 'x';
while (j+1 < Frame_maxtask && Tree_ppt[j+1]->pad_3 > lvl) {
Tree_ppt[j+1]->pad_2 = 'z';
idx = 0;
++j;
}
// no children found, so unmark this puppy
if (idx) Tree_ppt[idx]->pad_2 = '\0';
}
}
}
}
}
memcpy(Seed_ppt, Tree_ppt, sizeof(proc_t*) * Frame_maxtask);
} // end: forest_create
@ -4766,6 +4797,13 @@ static inline const char *forest_display (const WIN_t *q, const proc_t *p) {
const char *which = (CHKw(q, Show_CMDLIN)) ? *p->cmdline : p->cmd;
if (!CHKw(q, Show_FOREST) || !p->pad_3) return which;
#ifndef TREE_VWINALL
if (q == Curwin)
#endif
if (p->pad_2 == 'x') {
snprintf(buf, sizeof(buf), "+%*s%s", ((4 * p->pad_3) - 1), "`- ", which);
return buf;
}
if (p->pad_3 > 100) snprintf(buf, sizeof(buf), "%400s%s", " + ", which);
else snprintf(buf, sizeof(buf), "%*s%s", (4 * p->pad_3), " `- ", which);
return buf;
@ -5336,6 +5374,29 @@ static void keys_task (int ch) {
capsmk(w);
}
break;
case kbd_CtrlV:
if (VIZCHKw(w)) {
if (CHKw(w, Show_FOREST)) {
int i, pid = w->ppt[w->begtask]->tid;
#ifdef TREE_VPROMPT
int got = get_int(fmtmk(N_txt(XTRA_vforest_fmt), pid));
if (got < GET_NUM_NOT) break;
if (got > GET_NUM_NOT) pid = got;
#endif
for (i = 0; i < Hide_tot; i++) {
if (Hide_pid[i] == pid || Hide_pid[i] == -pid) {
Hide_pid[i] = -Hide_pid[i];
break;
}
}
if (i == Hide_tot) Hide_pid[Hide_tot++] = pid;
// plenty of room, but if everything's expaned let's reset ...
for (i = 0; i < Hide_tot; i++)
if (Hide_pid[i] > 0) break;
if (i == Hide_tot) Hide_tot = 0;
}
}
break;
default: // keep gcc happy
break;
}
@ -5348,12 +5409,14 @@ static void keys_window (int ch) {
switch (ch) {
case '+':
if (ALTCHKw) wins_reflag(Flags_OFF, EQUWINS_xxx);
Hide_tot = 0;
break;
case '-':
if (ALTCHKw) TOGw(w, Show_TASKON);
break;
case '=':
win_reset(w);
Hide_tot = 0;
break;
case '_':
if (ALTCHKw) wins_reflag(Flags_TOG, Show_TASKON);
@ -5543,7 +5606,7 @@ static void do_key (int ch) {
{ keys_task,
{ '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o'
, 'R', 'S', 'U', 'u', 'V', 'x', 'y', 'z'
, kbd_CtrlO, '\0' } },
, kbd_CtrlO, kbd_CtrlV, '\0' } },
{ keys_window,
{ '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w'
, kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN
@ -5878,6 +5941,15 @@ static const char *task_show (const WIN_t *q, const proc_t *p) {
char *rp;
int x;
/* we're borrowing some pad bytes in the proc_t,
pad_2: 'x' means a collapsed thread, 'z' means an unseen child
pad_3: where level number is stored (0 - 100) */
#ifndef TREE_VWINALL
if (q == Curwin)
#endif
if (CHKw(q, Show_FOREST) && p->pad_2 == 'z')
return "";
// we must begin a row with a possible window number in mind...
*(rp = rbuf) = '\0';
if (Rc.mode_altscr) rp = scat(rp, " ");

View File

@ -56,6 +56,8 @@
//#define TERMIOS_ONLY /* just limp along with native input only */
//#define TREE_NORESET /* sort keys do NOT force forest view OFF */
//#define TREE_SCANALL /* rescan array w/ forest view, avoid sort */
//#define TREE_VPROMPT /* pid collapse/expand prompt, vs. top row */
//#define TREE_VWINALL /* pid collapse/expand impacts all windows */
//#define USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */
//#define VALIDATE_NLS /* validate the integrity of all nls tbls */
//#define VER_J_RCFILE /* increase # of fields, rcfile ver to 'j' */
@ -172,6 +174,7 @@ char *strcasestr(const char *haystack, const char *needle);
#define kbd_INS 138
#define kbd_DEL 139
#define kbd_CtrlO '\017'
#define kbd_CtrlV '\026'
/* Special value in Pseudo_row to force an additional procs refresh
-- used at startup and for task/thread mode transitions */

View File

@ -498,6 +498,7 @@ static void build_norm_nlstab (void) {
Norm_nlstab[WORD_abv_mem_txt] = _("Mem ");
Norm_nlstab[WORD_abv_swp_txt] = _("Swap");
Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%c'");
Norm_nlstab[XTRA_vforest_fmt] = _("PID to collapse/expand [default pid = %d]");
}
@ -558,7 +559,7 @@ static void build_uniq_nlstab (void) {
" z~5,~1b~5 . Toggle: '~1z~2' color/mono; '~1b~2' bold/reverse (only if 'x' or 'y')\n"
" u,U,o,O . Filter by: '~1u~2'/'~1U~2' effective/any user; '~1o~2'/'~1O~2' other criteria\n"
" n,#,^O . Set: '~1n~2'/'~1#~2' max tasks displayed; Show: ~1Ctrl~2+'~1O~2' other filter(s)\n"
" C,... . Toggle scroll coordinates msg for: ~1up~2,~1down~2,~1left~2,~1right~2,~1home~2,~1end~2\n"
" C,^V . Toggle: '~1C~2' coordinates; ~1Ctrl~2+'~1V~2' hide/show forest view children\n"
"\n"
"%s"
" W,Y Write configuration file '~1W~2'; Inspect other output '~1Y~2'\n"

View File

@ -82,7 +82,8 @@ enum norm_nls {
WORD_abv_mem_txt, WORD_abv_swp_txt, WORD_allcpus_txt, WORD_another_txt,
WORD_eachcpu_fmt, WORD_exclude_txt, WORD_include_txt, WORD_noneone_txt,
WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt,
XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_warncfg_txt, XTRA_winsize_txt,
XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_vforest_fmt, XTRA_warncfg_txt,
XTRA_winsize_txt,
#ifndef INSP_OFFDEMO
YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt,
YINSP_deqtyp_txt, YINSP_dstory_txt,