diff --git a/top/top.c b/top/top.c index ebe7bb5b..978e58b6 100644 --- a/top/top.c +++ b/top/top.c @@ -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, " "); diff --git a/top/top.h b/top/top.h index 0f2b59ca..3ae65d58 100644 --- a/top/top.h +++ b/top/top.h @@ -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 */ diff --git a/top/top_nls.c b/top/top_nls.c index c4761508..828ca788 100644 --- a/top/top_nls.c +++ b/top/top_nls.c @@ -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" diff --git a/top/top_nls.h b/top/top_nls.h index 1a3c9add..8dece855 100644 --- a/top/top_nls.h +++ b/top/top_nls.h @@ -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,