diff --git a/src/btop.cpp b/src/btop.cpp index db44d50..213d24e 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -99,8 +99,8 @@ namespace Global { //* A simple argument parser void argumentParser(const int& argc, char **argv) { for(int i = 1; i < argc; i++) { - string argument = argv[i]; - if (argument == "-h" or argument == "--help") { + const string argument = argv[i]; + if (is_in(argument, "-h", "--help")) { cout << "usage: btop [-h] [-v] [-/+t] [--debug]\n\n" << "optional arguments:\n" << " -h, --help show this help message and exit\n" @@ -113,18 +113,18 @@ void argumentParser(const int& argc, char **argv) { << endl; exit(0); } - if (argument == "-v" or argument == "--version") { + if (is_in(argument, "-v", "--version")) { cout << "btop version: " << Global::Version << endl; exit(0); } - else if (argument == "-lc" or argument == "--low-color") { + else if (is_in(argument, "-lc", "--low-color")) { Global::arg_low_color = true; } - else if (argument == "-t" or argument == "--tty_on") { + else if (is_in(argument, "-t", "--tty_on")) { Config::set("tty_mode", true); Global::arg_tty = true; } - else if (argument == "+t" or argument == "--tty_off") { + else if (is_in(argument, "+t", "--tty_off")) { Config::set("tty_mode", false); Global::arg_tty = true; } @@ -296,11 +296,11 @@ namespace Runner { const uint_fast8_t cpu_done = 0b1100'0000; struct runner_conf { - vector boxes; - bool no_update = false; - bool force_redraw = false; - string overlay = ""; - string clock = ""; + bitset<8> box_mask; + bool no_update; + bool force_redraw; + string overlay; + string clock; }; struct runner_conf current_conf; @@ -334,12 +334,6 @@ namespace Runner { output.clear(); - //? Setup bitmask for selected boxes instead of parsing strings in the loop - bitset<8> box_mask; - for (const auto& box : conf->boxes) { - box_mask |= box_bits.at(box); - } - future cpu; future mem; future net; @@ -348,33 +342,32 @@ namespace Runner { //* Start collection functions for all boxes in async threads and draw in this thread when finished //? Starting order below based on mean time to finish try { - while (box_mask.count() > 0) { + while (conf->box_mask.count() > 0) { if (stopping) break; //? PROC - if (box_mask.test(proc_present)) { - if (not box_mask.test(proc_running)) { + if (conf->box_mask.test(proc_present)) { + if (not conf->box_mask.test(proc_running)) { proc = async(Proc::collect, conf->no_update); - box_mask.set(proc_running); + conf->box_mask.set(proc_running); } else if (not proc.valid()) throw std::runtime_error("Proc::collect() future not valid."); - else if (proc.wait_for(ZeroSec) == future_status::ready) { + else if (proc.wait_for(std::chrono::microseconds(100)) == future_status::ready) { try { output += Proc::draw(proc.get(), conf->force_redraw, conf->no_update); } catch (const std::exception& e) { throw std::runtime_error("Proc:: -> " + (string)e.what()); } - box_mask ^= proc_done; - continue; + conf->box_mask ^= proc_done; } } //? MEM - if (box_mask.test(mem_present)) { - if (not box_mask.test(mem_running)) { + if (conf->box_mask.test(mem_present)) { + if (not conf->box_mask.test(mem_running)) { mem = async(Mem::collect, conf->no_update); - box_mask.set(mem_running); + conf->box_mask.set(mem_running); } else if (not mem.valid()) throw std::runtime_error("Mem::collect() future not valid."); @@ -386,15 +379,14 @@ namespace Runner { catch (const std::exception& e) { throw std::runtime_error("Mem:: -> " + (string)e.what()); } - box_mask ^= mem_done; - continue; + conf->box_mask ^= mem_done; } } //? NET - if (box_mask.test(net_present)) { - if (not box_mask.test(net_running)) { + if (conf->box_mask.test(net_present)) { + if (not conf->box_mask.test(net_running)) { net = async(Net::collect, conf->no_update); - box_mask.set(net_running); + conf->box_mask.set(net_running); } else if (not net.valid()) throw std::runtime_error("Net::collect() future not valid."); @@ -406,15 +398,14 @@ namespace Runner { catch (const std::exception& e) { throw std::runtime_error("Net:: -> " + (string)e.what()); } - box_mask ^= net_done; - continue; + conf->box_mask ^= net_done; } } //? CPU - if (box_mask.test(cpu_present)) { - if (not box_mask.test(cpu_running)) { + if (conf->box_mask.test(cpu_present)) { + if (not conf->box_mask.test(cpu_running)) { cpu = async(Cpu::collect, conf->no_update); - box_mask.set(cpu_running); + conf->box_mask.set(cpu_running); } else if (not cpu.valid()) throw std::runtime_error("Cpu::collect() future not valid."); @@ -426,11 +417,9 @@ namespace Runner { catch (const std::exception& e) { throw std::runtime_error("Cpu:: -> " + (string)e.what()); } - box_mask ^= cpu_done; - continue; + conf->box_mask ^= cpu_done; } } - sleep_micros(100); } } catch (const std::exception& e) { @@ -457,7 +446,7 @@ namespace Runner { } //? ------------------------------------------ Secondary thread end ----------------------------------------------- - //* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values, box="all": all boxes + //* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values void run(const string& box, const bool no_update, const bool force_redraw) { atomic_lock lck(waiting); atomic_wait(active); @@ -477,7 +466,13 @@ namespace Runner { Config::unlock(); Config::lock(); - current_conf = { (box == "all" ? Config::current_boxes : vector{box}), no_update, force_redraw, Global::overlay, Global::clock}; + //? Setup bitmask for selected boxes instead of parsing strings + bitset<8> box_mask; + for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) { + box_mask |= box_bits.at(box); + } + + current_conf = {box_mask, no_update, force_redraw, Global::overlay, Global::clock}; pthread_t runner_id; if (pthread_create(&runner_id, NULL, &_runner, (void *) ¤t_conf) != 0) @@ -609,7 +604,7 @@ int main(int argc, char **argv) { Logger::warning("No UTF-8 locale detected! Forcing start with --utf-force argument."); else if (not found) { Global::exit_error_msg = "No UTF-8 locale detected! Use --utf-force argument to start anyway."; - clean_quit(1); + exit(1); } else Logger::debug("Setting LC_ALL=" + (string)std::setlocale(LC_ALL, NULL)); @@ -618,7 +613,7 @@ int main(int argc, char **argv) { //? Initialize terminal and set options if (not Term::init()) { Global::exit_error_msg = "No tty detected!\nbtop++ needs an interactive shell to run."; - clean_quit(1); + exit(1); } Logger::info("Running on " + Term::current_tty); @@ -637,7 +632,7 @@ int main(int argc, char **argv) { } catch (const std::exception& e) { Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what(); - clean_quit(1); + exit(1); } //? Update list of available themes and generate the selected theme @@ -709,7 +704,7 @@ int main(int argc, char **argv) { } catch (std::exception& e) { Global::exit_error_msg = "Exception in main loop -> " + (string)e.what(); - clean_quit(1); + exit(1); } } diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 77850df..0853627 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -199,7 +199,7 @@ namespace Config { {"proc_tree", false}, {"proc_colors", true}, {"proc_gradient", true}, - {"proc_per_core", false}, + {"proc_per_core", true}, {"proc_mem_bytes", true}, {"proc_info_smaps", false}, {"proc_left", false}, @@ -299,7 +299,7 @@ namespace Config { } catch (const std::exception& e) { Global::exit_error_msg = "Exception during Config::unlock() : " + (string)e.what(); - clean_quit(1); + exit(1); } locked = false; diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index 0700842..be7a190 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -453,11 +453,23 @@ namespace Cpu { } try { - //? Cpu graphs, cpu clock and cpu meter + //? Cpu graphs out += Fx::ub + Mv::to(y + 1, x + 1) + graph_upper(cpu.cpu_percent.at(graph_up_field), (data_same or redraw)); if (not single_graph) out += Mv::to( y + graph_up_height + 1 + (mid_line ? 1 : 0), x + 1) + graph_lower(cpu.cpu_percent.at(graph_lo_field), (data_same or redraw)); + //? Uptime + if (Config::getB("show_uptime")) { + string upstr = sec_to_dhms(system_uptime()); + if (upstr.size() > 8) { + upstr.resize(upstr.size() - 3); + upstr = trans(upstr); + } + out += Mv::to(y + (single_graph or not Config::getB("cpu_invert_lower") ? 1 : height - 2), x + 2) + + Theme::c("graph_text") + "up" + Mv::r(1) + upstr; + } + + //? Cpu clock and cpu meter if (Config::getB("show_cpu_freq") and not cpuHz.empty()) out += Mv::to(b_y, b_x + b_width - 10) + Fx::ub + Theme::c("div_line") + Symbols::h_line * (7 - cpuHz.size()) + Symbols::title_left + Fx::b + Theme::c("title") + cpuHz + Fx::ub + Theme::c("div_line") + Symbols::title_right; @@ -519,16 +531,7 @@ namespace Cpu { out += Mv::to(b_y + b_height - 2, b_x + cx + 1) + Theme::c("main_fg") + lavg_pre + lavg; } - //? Uptime - if (Config::getB("show_uptime")) { - string upstr = sec_to_dhms(system_uptime()); - if (upstr.size() > 8) { - upstr.resize(upstr.size() - 3); - upstr = trans(upstr); - } - out += Mv::to(y + (single_graph or not Config::getB("cpu_invert_lower") ? 1 : height - 2), x + 2) - + Theme::c("graph_text") + "up" + Mv::r(1) + upstr; - } + redraw = false; return out + Fx::reset; diff --git a/src/btop_input.cpp b/src/btop_input.cpp index d7616a6..7b7d2e4 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -178,7 +178,7 @@ namespace Input { if (key.empty()) return; try { auto& filtering = Config::getB("proc_filtering"); - if (not filtering and key == "q") clean_quit(0); + if (not filtering and key == "q") exit(0); //? Global input actions if (not filtering) { diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp index 6805e1f..9333a8b 100644 --- a/src/btop_linux.cpp +++ b/src/btop_linux.cpp @@ -814,29 +814,6 @@ namespace Proc { int collapse = -1, expand = -1; uint64_t old_cputimes = 0; atomic numpids = 0; - vector sort_vector = { - "pid", - "name", - "command", - "threads", - "user", - "memory", - "cpu direct", - "cpu lazy", - }; - unordered_flat_map proc_states = { - {'R', "Running"}, - {'S', "Sleeping"}, - {'D', "Waiting"}, - {'Z', "Zombie"}, - {'T', "Stopped"}, - {'t', "Tracing"}, - {'X', "Dead"}, - {'x', "Dead"}, - {'K', "Wakekill"}, - {'W', "Unknown"}, - {'P', "Parked"} - }; detail_container detailed; @@ -859,11 +836,13 @@ namespace Proc { } } + //? Add process to vector if not filtered out or currently in a collapsed sub-tree if (not collapsed and not filtering) { out_procs.push_back(cur_proc); - if (std::string_view cmd_view = cur_proc.cmd; not cmd_view.empty()) { - cmd_view = cmd_view.substr(0, std::min(cmd_view.find(' '), cmd_view.size())); - cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size())); + //? Try to find name of the binary file and append to program name if not the same (replacing cmd string) + if (std::string_view cmd_view = cur_proc.cmd; not cmd_view.empty() and not cmd_view.starts_with('(')) { + cmd_view = cmd_view.substr(0, min(cmd_view.find(' '), cmd_view.size())); + cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size())); if (cmd_view == cur_proc.name) out_procs.back().cmd.clear(); else @@ -871,6 +850,7 @@ namespace Proc { } } + //? Recursive iteration over all children int children = 0; for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) { if (collapsed and not filtering) { @@ -883,9 +863,11 @@ namespace Proc { } if (collapsed or filtering) return; + //? Add tree terminator symbol if it's the last child in a sub-tree if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("]─")) out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ "); + //? Add collapse/expand symbols if process have any children out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+]─" : "[-]─") : " ├─ "); } @@ -1035,9 +1017,9 @@ namespace Proc { if (pread.good()) { pread.ignore(SSmax, ' '); for (uint64_t times; pread >> times; cputimes += times); - pread.close(); } else throw std::runtime_error("Failure to read /proc/stat"); + pread.close(); //? Iterate over all pids in /proc for (const auto& d: fs::directory_iterator(Shared::procPath)) { @@ -1095,16 +1077,16 @@ namespace Proc { if (not pread.good()) continue; //? Check cached value for whitespace characters in name and set offset to get correct fields from stat file - size_t& offset = cache.at(new_proc.pid).name_offset; + const auto& offset = cache.at(new_proc.pid).name_offset; short_str.clear(); size_t x = 0, next_x = 3; uint64_t cpu_t = 0; try { for (;;) { - while (pread.good() and ++x - offset < next_x) { + while (pread.good() and ++x < next_x + offset) { pread.ignore(SSmax, ' '); } - if (pread.bad()) goto stat_loop_done; + if (pread.bad()) break; getline(pread, short_str, ' '); diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index d31e399..f8e4c7c 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -28,7 +28,6 @@ tab-size = 4 using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array; -void clean_quit(int sig=-1); void term_resize(bool force=false); void banner_gen(); @@ -169,10 +168,31 @@ namespace Proc { extern int selected_pid, start, selected, collapse, expand; //? Contains the valid sorting options for processes - extern vector sort_vector; + const vector sort_vector = { + "pid", + "name", + "command", + "threads", + "user", + "memory", + "cpu direct", + "cpu lazy", + }; //? Translation from process state char to explanative string - extern unordered_flat_map proc_states; + const unordered_flat_map proc_states = { + {'R', "Running"}, + {'S', "Sleeping"}, + {'D', "Waiting"}, + {'Z', "Zombie"}, + {'T', "Stopped"}, + {'t', "Tracing"}, + {'X', "Dead"}, + {'x', "Dead"}, + {'K', "Wakekill"}, + {'W', "Unknown"}, + {'P', "Parked"} + }; //* Container for process information struct proc_info {