diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 159257d..8b9e24f 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -340,6 +340,7 @@ namespace Config { {"proc_follow_detailed", true}, {"follow_process", false}, {"update_following", false}, + {"should_selection_return_to_followed", false}, #ifdef GPU_SUPPORT {"nvml_measure_pcie_speeds", true}, {"rsmi_measure_pcie_speeds", true}, @@ -356,6 +357,7 @@ namespace Config { {"net_download", 100}, {"net_upload", 100}, {"detailed_pid", 0}, + {"restore_detailed_pid", 0}, {"selected_pid", 0}, {"followed_pid", 0}, {"selected_depth", 0}, diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index cadaefa..b85eea1 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -1561,6 +1561,7 @@ namespace Proc { Draw::Graph detailed_mem_graph; int user_size, thread_size, prog_size, cmd_size, tree_size; int dgraph_x, dgraph_width, d_width, d_x, d_y; + bool previous_proc_banner_state = false; string box; @@ -1568,11 +1569,19 @@ namespace Proc { auto start = Config::getI("proc_start"); auto selected = Config::getI("proc_selected"); auto last_selected = Config::getI("proc_last_selected"); + bool changed = false; int select_max = (Config::getB("show_detailed") ? (Config::getB("proc_banner_shown") ? Proc::select_max - 9 : Proc::select_max - 8) : (Config::getB("proc_banner_shown") ? Proc::select_max - 1 : Proc::select_max)); + // Return the selection from the detailed view to the followed process before moving the selection + // Disengage following mode when moving the selection unless paused if (Config::getB("follow_process")) { - if (selected == 0) selected = Config::getI("proc_followed");; + if (Config::getB("show_detailed") and selected == 0 and Config::getB("should_selection_return_to_followed") + and Config::getI("detailed_pid") == Config::getI("followed_pid")) { + selected = Config::getI("proc_followed"); + Config::set("should_selection_return_to_followed", false); + changed = true; + } if (not Config::getB("pause_proc_list")) { Config::flip("follow_process"); Config::set("followed_pid", 0); @@ -1625,7 +1634,6 @@ namespace Proc { start = clamp((int)round((double)mouse_y * (numpids - select_max - 2) / (select_max - 2)), 0, max(0, numpids - select_max)); } - bool changed = false; if (start != Config::getI("proc_start")) { Config::set("proc_start", start); changed = true; @@ -1653,13 +1661,14 @@ namespace Proc { auto follow_process = Config::getB("follow_process"); int followed_pid = Config::getI("followed_pid"); int followed = Config::getI("proc_followed"); + bool should_selection_return_to_followed = Config::getB("should_selection_return_to_followed"); auto proc_banner_shown = pause_proc_list or follow_process; Config::set("proc_banner_shown", proc_banner_shown); start = Config::getI("proc_start"); selected = Config::getI("proc_selected"); const int y = show_detailed ? Proc::y + 8 : Proc::y; const int height = show_detailed ? Proc::height - 8 : Proc::height; - const int select_max = show_detailed ? (proc_banner_shown ? Proc::select_max - 9 : Proc::select_max - 8) : + int select_max = show_detailed ? (proc_banner_shown ? Proc::select_max - 9 : Proc::select_max - 8) : (proc_banner_shown ? Proc::select_max - 1 : Proc::select_max); auto totalMem = Mem::get_totalMem(); int numpids = Proc::numpids; @@ -1668,13 +1677,15 @@ namespace Proc { out.reserve(width * height); //? Move current selection/view to the selected process when a process should be followed - if (follow_process and (not pause_proc_list or Config::getB("update_following"))) { + //? Restore view and selection to the detailed view process when detailed view is closed + const int restore_detailed_pid = Config::getI("restore_detailed_pid"); + if ((follow_process and (not pause_proc_list or Config::getB("update_following"))) or restore_detailed_pid > 0) { Config::set("update_following", false); int loc = 1; bool can_follow = false; for (auto& p : plist) { if (p.filtered or (proc_tree and p.tree_index == plist.size())) continue; - if (p.pid == (size_t)followed_pid) { + if (p.pid == (size_t)(restore_detailed_pid > 0 ? restore_detailed_pid : followed_pid)) { can_follow = true; break; } @@ -1682,19 +1693,35 @@ namespace Proc { } if (can_follow) { - start = max(0, loc - (select_max / 2)); - followed = loc < (select_max / 2) ? loc : start > numpids - select_max ? select_max - numpids + loc : select_max / 2; - Config::set("proc_followed", followed); - selected = followed_pid != Config::getI("detailed_pid") ? followed : 0; + const int list_middle = select_max % 2 == 0 ? select_max / 2 : select_max / 2 + 1; + start = max(0, loc - list_middle); + followed = loc < list_middle ? loc : start > numpids - select_max ? select_max - numpids + loc : list_middle; + if (restore_detailed_pid == 0) { + Config::set("proc_followed", followed); + Config::set("should_selection_return_to_followed", should_selection_return_to_followed = true); + } + selected = (followed_pid != Config::getI("detailed_pid") or restore_detailed_pid > 0) ? followed : 0; } - else { + else if (restore_detailed_pid == 0) { Config::set("followed_pid", followed_pid = 0); Config::set("follow_process", follow_process = false); Config::set("proc_banner_shown", proc_banner_shown = pause_proc_list); Config::set("proc_followed", 0); + if (not proc_banner_shown) select_max++; } + if (restore_detailed_pid > 0) Config::set("restore_detailed_pid", 0); } + //? Handle selection edge cases when list view is showing bottom of list + //? for Pause and Following modes + const bool proc_banner_changed = proc_banner_shown != previous_proc_banner_state; + previous_proc_banner_state = proc_banner_shown; + if (proc_banner_changed and not proc_banner_shown + and start + select_max - 1 == numpids) + selected++; + else if (pause_proc_list and selected > select_max) + start++; + //? redraw if selection reaches or leaves the end of the list if (selected != Config::getI("proc_last_selected")) { if (selected >= select_max and start >= numpids - select_max) { @@ -1860,11 +1887,13 @@ namespace Proc { //? select, info, signal, and follow buttons const string down_button = (is_last_process_in_list ? Theme::c("inactive_fg") : Theme::c("hi_fg")) + Symbols::down; + const bool is_up_button_highlighted = selected != 0 or (follow_process and followed_pid == Config::getI("detailed_pid") and should_selection_return_to_followed); + const string up_button = (is_up_button_highlighted ? Theme::c("hi_fg") : Theme::c("inactive_fg")) + Symbols::up; const string t_color = (selected == 0 ? Theme::c("inactive_fg") : Theme::c("title")); const string hi_color = (selected == 0 ? Theme::c("inactive_fg") : Theme::c("hi_fg")); int mouse_x = x + 14; - out += Mv::to(y + height - 1, x + 1) + title_left_down + Fx::b + hi_color + Symbols::up + Theme::c("title") + " select " + down_button + Fx::ub + title_right_down - + title_left_down + Fx::b + t_color + "info " + hi_color + Symbols::enter + Fx::ub + title_right_down; + out += Mv::to(y + height - 1, x + 1) + title_left_down + Fx::b + hi_color + up_button + Theme::c("title") + " select " + down_button + Fx::ub + title_right_down + + title_left_down + Fx::b + t_color + "info " + hi_color + Symbols::enter + Fx::ub + title_right_down; if (selected > 0) Input::mouse_mappings["info_enter"] = {y + height - 1, mouse_x, 1, 6}; mouse_x += 8; if (width > 60) { diff --git a/src/btop_input.cpp b/src/btop_input.cpp index f7aa15c..ce0bfc2 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -355,6 +355,10 @@ namespace Input { } else if (Config::getB("follow_process")) { Config::flip("follow_process"); + if (Config::getB("should_selection_return_to_followed")) + Config::set("proc_selected", Config::getI("proc_followed")); + else if (Config::getB("show_detailed") and Config::getI("followed_pid") == Config::getI("detailed_pid")) + Config::set("restore_detailed_pid", Config::getI("detailed_pid")); Config::set("followed_pid", 0); Config::set("proc_followed", 0); } @@ -395,6 +399,8 @@ namespace Input { process("enter"); return; } + else if (Config::getB("proc_banner_shown") and line == y + height - 2) + return; else if (current_selection == 0 or line - y - 1 == 0) redraw = true; @@ -449,31 +455,35 @@ namespace Input { if (Config::getB("proc_follow_detailed")) { Config::set("follow_process", true); Config::set("followed_pid", Config::getI("selected_pid")); - Config::set("update_following", true); } Config::set("show_detailed", true); } else if (Config::getB("show_detailed")) { - const int proc_start_offset = Config::getB("proc_follow_detailed") ? Config::getI("proc_followed") - Config::getI("proc_last_selected") : 0; - if (Config::getI("proc_last_selected") > 0) Config::set("proc_selected", Config::getI("proc_last_selected")); - Config::set("proc_start", std::max(0, Config::getI("proc_start") + proc_start_offset)); + if (Config::getB("proc_follow_detailed")) { + Config::set("restore_detailed_pid", Config::getI("detailed_pid")); + if (Config::getB("follow_process") and Config::getI("followed_pid") == Config::getI("detailed_pid")) { + Config::flip("follow_process"); + Config::set("followed_pid", 0); + Config::set("proc_followed", 0); + } + } + else if (Config::getI("proc_last_selected") > 0) Config::set("proc_selected", Config::getI("proc_last_selected")); Config::set("proc_last_selected", 0); Config::set("detailed_pid", 0); Config::set("show_detailed", false); - if (Config::getB("follow_process") and Config::getB("proc_follow_detailed")) { - Config::flip("follow_process"); - Config::set("followed_pid", 0); - Config::set("proc_followed", 0); - } } + Config::set("update_following", true); } - else if (is_in(key, "+", "-", "space", "C") and Config::getB("proc_tree") and Config::getI("proc_selected") > 0) { - atomic_wait(Runner::active); - auto& pid = Config::getI("selected_pid"); - if (key == "+" or key == "space") Proc::expand = pid; - if (key == "-" or key == "space") Proc::collapse = pid; - if (key == "C") Proc::toggle_children = pid; - no_update = false; + else if (is_in(key, "+", "-", "space", "C") and Config::getB("proc_tree")) { + const bool is_following_detailed = Config::getB("follow_process") and Config::getI("followed_pid") == Config::getI("detailed_pid"); + if (Config::getI("proc_selected") > 0 or is_following_detailed) { + atomic_wait(Runner::active); + auto& pid = is_following_detailed and Config::getI("proc_selected") == 0 ? Config::getI("followed_pid") : Config::getI("selected_pid"); + if (key == "+" or key == "space") Proc::expand = pid; + if (key == "-" or key == "space") Proc::collapse = pid; + if (key == "C") Proc::toggle_children = pid; + no_update = false; + } } else if (is_in(key, "t", kill_key) and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) { atomic_wait(Runner::active); diff --git a/src/freebsd/btop_collect.cpp b/src/freebsd/btop_collect.cpp index 1a8bdf7..17f916f 100644 --- a/src/freebsd/btop_collect.cpp +++ b/src/freebsd/btop_collect.cpp @@ -1210,6 +1210,7 @@ namespace Proc { } //? Set correct state of dead processes if paused else { + const bool keep_dead_proc_usage = Config::getB("keep_dead_proc_usage"); for (auto& r : current_procs) { if (rng::find(found, r.pid) == found.end()) { if (r.state != 'X') { @@ -1220,7 +1221,7 @@ namespace Proc { r.state = 'X'; dead_procs.emplace(r.pid); //? Reset cpu usage for dead processes if paused and option is set - if (!Config::getB("keep_dead_proc_usage")) { + if (!keep_dead_proc_usage) { r.cpu_p = 0.0; r.mem = 0; } diff --git a/src/linux/btop_collect.cpp b/src/linux/btop_collect.cpp index 456abfa..ae0b035 100644 --- a/src/linux/btop_collect.cpp +++ b/src/linux/btop_collect.cpp @@ -3169,13 +3169,14 @@ namespace Proc { } //? Set correct state of dead processes if paused else { + const bool keep_dead_proc_usage = Config::getB("keep_dead_proc_usage"); for (auto& r : current_procs) { if (rng::find(found, r.pid) == found.end()) { if (r.state != 'X') r.death_time = round(uptime) - (r.cpu_s / Shared::clkTck); r.state = 'X'; dead_procs.emplace(r.pid); //? Reset cpu usage for dead processes if paused and option is set - if (!Config::getB("keep_dead_proc_usage")) { + if (!keep_dead_proc_usage) { r.cpu_p = 0.0; r.mem = 0; } diff --git a/src/netbsd/btop_collect.cpp b/src/netbsd/btop_collect.cpp index 050dba8..9f56899 100755 --- a/src/netbsd/btop_collect.cpp +++ b/src/netbsd/btop_collect.cpp @@ -1283,6 +1283,7 @@ namespace Proc { } //? Set correct state of dead processes if paused else { + const bool keep_dead_proc_usage = Config::getB("keep_dead_proc_usage"); for (auto& r : current_procs) { if (rng::find(found, r.pid) == found.end()) { if (r.state != 'X') { @@ -1293,7 +1294,7 @@ namespace Proc { r.state = 'X'; dead_procs.emplace(r.pid); //? Reset cpu usage for dead processes if paused and option is set - if (!Config::getB("keep_dead_proc_usage")) { + if (!keep_dead_proc_usage) { r.cpu_p = 0.0; r.mem = 0; } diff --git a/src/openbsd/btop_collect.cpp b/src/openbsd/btop_collect.cpp index 7e52700..faf3b2f 100644 --- a/src/openbsd/btop_collect.cpp +++ b/src/openbsd/btop_collect.cpp @@ -1148,6 +1148,7 @@ namespace Proc { } //? Set correct state of dead processes if paused else { + const bool keep_dead_proc_usage = Config::getB("keep_dead_proc_usage"); for (auto& r : current_procs) { if (rng::find(found, r.pid) == found.end()) { if (r.state != 'X') { @@ -1158,7 +1159,7 @@ namespace Proc { r.state = 'X'; dead_procs.emplace(r.pid); //? Reset cpu usage for dead processes if paused and option is set - if (!Config::getB("keep_dead_proc_usage")) { + if (!keep_dead_proc_usage) { r.cpu_p = 0.0; r.mem = 0; } diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp index 068451e..197cec5 100644 --- a/src/osx/btop_collect.cpp +++ b/src/osx/btop_collect.cpp @@ -1290,6 +1290,7 @@ namespace Proc { } //? Set correct state of dead processes if paused else { + const bool keep_dead_proc_usage = Config::getB("keep_dead_proc_usage"); for (auto& r : current_procs) { if (rng::find(found, r.pid) == found.end()) { if (r.state != 'X') { @@ -1300,7 +1301,7 @@ namespace Proc { r.state = 'X'; dead_procs.emplace(r.pid); //? Reset cpu usage for dead processes if paused and option is set - if (!Config::getB("keep_dead_proc_usage")) { + if (!keep_dead_proc_usage) { r.cpu_p = 0.0; r.mem = 0; }