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 ###########################################*/ /*###### Forest View support ###########################################*/
/* /*
* We try to keep most existing code unaware of our activities * We try keeping most existing code unaware of these activities
* ( plus, maintain alphabetical order with carefully chosen ) * ( plus, maintain alphabetical order within carefully chosen )
* ( function names: forest_a, forest_b, forest_c & forest_d ) * ( function names of: forest_a, forest_b, forest_c, forest_d )
* ( each with exactly one letter more than its predecessor! ) */ * ( with each name exactly 1 letter more than its predecessor ) */
static proc_t **Seed_ppt; // temporary win ppt pointer static proc_t **Seed_ppt; // temporary win ppt pointer
static proc_t **Tree_ppt; // forest_create will resize static proc_t **Tree_ppt; // forest_create will resize
static int Tree_idx; // frame_make resets to zero 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. * 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 * This routine is responsible for preparing the proc_t's for
* a forest display in the designated window. Upon completion, * a forest display in a designated window. After completion,
* he'll replace the original window ppt with our specially * he will replace the original window ppt with our specially
* ordered forest version. */ * ordered forest version. He also marks any hidden children! */
static void forest_create (WIN_t *q) { static void forest_create (WIN_t *q) {
static int hwmsav; static int hwmsav;
int i; int i, j;
Seed_ppt = q->ppt; // avoid passing WIN_t ptrs Seed_ppt = q->ppt; // avoid passing WIN_t ptrs
if (!Tree_idx) { // do just once per frame if (!Tree_idx) { // do just once per frame
if (hwmsav < Frame_maxtask) { // grow, but never shrink if (hwmsav < Frame_maxtask) { // grow, but never shrink
hwmsav = Frame_maxtask; hwmsav = Frame_maxtask;
Tree_ppt = alloc_r(Tree_ppt, sizeof(proc_t*) * hwmsav); Tree_ppt = alloc_r(Tree_ppt, sizeof(proc_t*) * hwmsav);
Hide_pid = alloc_r(Hide_pid, sizeof(int) * hwmsav);
} }
#ifndef TREE_SCANALL #ifndef TREE_SCANALL
qsort(Seed_ppt, Frame_maxtask, sizeof(proc_t*), (QFP_t)forest_based); qsort(Seed_ppt, Frame_maxtask, sizeof(proc_t*), (QFP_t)forest_based);
#endif #endif
for (i = 0; i < Frame_maxtask; i++) // avoid any hidepid distortions for (i = 0; i < Frame_maxtask; i++) // avoid any hidepid distortions
if (!Seed_ppt[i]->pad_3) // identify real or pretend trees if (!Seed_ppt[i]->pad_3) // identify real or pretend trees
forest_adds(i, 0); // add as parent plus its children 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); memcpy(Seed_ppt, Tree_ppt, sizeof(proc_t*) * Frame_maxtask);
} // end: forest_create } // 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; const char *which = (CHKw(q, Show_CMDLIN)) ? *p->cmdline : p->cmd;
if (!CHKw(q, Show_FOREST) || !p->pad_3) return which; 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); if (p->pad_3 > 100) snprintf(buf, sizeof(buf), "%400s%s", " + ", which);
else snprintf(buf, sizeof(buf), "%*s%s", (4 * p->pad_3), " `- ", which); else snprintf(buf, sizeof(buf), "%*s%s", (4 * p->pad_3), " `- ", which);
return buf; return buf;
@ -5336,6 +5374,29 @@ static void keys_task (int ch) {
capsmk(w); capsmk(w);
} }
break; 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 default: // keep gcc happy
break; break;
} }
@ -5348,12 +5409,14 @@ static void keys_window (int ch) {
switch (ch) { switch (ch) {
case '+': case '+':
if (ALTCHKw) wins_reflag(Flags_OFF, EQUWINS_xxx); if (ALTCHKw) wins_reflag(Flags_OFF, EQUWINS_xxx);
Hide_tot = 0;
break; break;
case '-': case '-':
if (ALTCHKw) TOGw(w, Show_TASKON); if (ALTCHKw) TOGw(w, Show_TASKON);
break; break;
case '=': case '=':
win_reset(w); win_reset(w);
Hide_tot = 0;
break; break;
case '_': case '_':
if (ALTCHKw) wins_reflag(Flags_TOG, Show_TASKON); if (ALTCHKw) wins_reflag(Flags_TOG, Show_TASKON);
@ -5543,7 +5606,7 @@ static void do_key (int ch) {
{ keys_task, { keys_task,
{ '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o' { '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o'
, 'R', 'S', 'U', 'u', 'V', 'x', 'y', 'z' , 'R', 'S', 'U', 'u', 'V', 'x', 'y', 'z'
, kbd_CtrlO, '\0' } }, , kbd_CtrlO, kbd_CtrlV, '\0' } },
{ keys_window, { keys_window,
{ '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w' { '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w'
, kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN , 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; char *rp;
int x; 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... // we must begin a row with a possible window number in mind...
*(rp = rbuf) = '\0'; *(rp = rbuf) = '\0';
if (Rc.mode_altscr) rp = scat(rp, " "); if (Rc.mode_altscr) rp = scat(rp, " ");

View File

@ -56,6 +56,8 @@
//#define TERMIOS_ONLY /* just limp along with native input only */ //#define TERMIOS_ONLY /* just limp along with native input only */
//#define TREE_NORESET /* sort keys do NOT force forest view OFF */ //#define TREE_NORESET /* sort keys do NOT force forest view OFF */
//#define TREE_SCANALL /* rescan array w/ forest view, avoid sort */ //#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 USE_X_COLHDR /* emphasize header vs. whole col, for 'x' */
//#define VALIDATE_NLS /* validate the integrity of all nls tbls */ //#define VALIDATE_NLS /* validate the integrity of all nls tbls */
//#define VER_J_RCFILE /* increase # of fields, rcfile ver to 'j' */ //#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_INS 138
#define kbd_DEL 139 #define kbd_DEL 139
#define kbd_CtrlO '\017' #define kbd_CtrlO '\017'
#define kbd_CtrlV '\026'
/* Special value in Pseudo_row to force an additional procs refresh /* Special value in Pseudo_row to force an additional procs refresh
-- used at startup and for task/thread mode transitions */ -- 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_mem_txt] = _("Mem ");
Norm_nlstab[WORD_abv_swp_txt] = _("Swap"); Norm_nlstab[WORD_abv_swp_txt] = _("Swap");
Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%c'"); 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" " 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" " 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" " 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" "\n"
"%s" "%s"
" W,Y Write configuration file '~1W~2'; Inspect other output '~1Y~2'\n" " 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_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_eachcpu_fmt, WORD_exclude_txt, WORD_include_txt, WORD_noneone_txt,
WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt, 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 #ifndef INSP_OFFDEMO
YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt, YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt,
YINSP_deqtyp_txt, YINSP_dstory_txt, YINSP_deqtyp_txt, YINSP_dstory_txt,