From c4b55c7dfd0776d02ccc4d7967c362ea3de2711f Mon Sep 17 00:00:00 2001 From: aristocratos Date: Sat, 12 Jun 2021 18:49:27 +0200 Subject: [PATCH] Added config file loader --- btop.cpp | 35 +++++++++++++++++--- src/btop_config.h | 69 ++++++++++++++++++++++++++++++++++---- src/btop_draw.h | 12 ++++--- src/btop_input.h | 7 ++-- src/btop_linux.h | 32 ++++++++++-------- src/btop_tools.h | 84 ++++++++++++++++++++++++++++++----------------- 6 files changed, 177 insertions(+), 62 deletions(-) diff --git a/btop.cpp b/btop.cpp index 8384276..fdeadf5 100644 --- a/btop.cpp +++ b/btop.cpp @@ -256,8 +256,19 @@ int main(int argc, char **argv){ } } - Global::debug = true; - if (Global::debug) { Logger::loglevel = 4; Logger::debug("Starting in debug mode");} + //? Read config file if present + { vector load_errors; + Config::load(Config::conf_file, load_errors); + + if (Global::debug) Logger::loglevel = 4; + else Logger::loglevel = v_index(Logger::log_levels, Config::getS("log_level")); + + if (Logger::loglevel == 4) Logger::debug("Starting with logger set to debug."); + + if (!load_errors.empty()) { + for (auto& err_str : load_errors) Logger::error(err_str); + } + } if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) { string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.\n" @@ -279,8 +290,7 @@ int main(int argc, char **argv){ Proc::init(); #endif - //? Read config file if present - Config::load(); + // Config::set("truecolor", false); auto thts = time_ms(); @@ -433,6 +443,23 @@ int main(int argc, char **argv){ } + if (false) { + cout << Config::getS("log_level") << endl; + + vector vv = {"hej", "vad", "du"}; + vector vy; + + cout << v_contains(vv, "vad"s) << endl; + cout << v_index(vv, "hej"s) << endl; + cout << v_index(vv, "du"s) << endl; + cout << v_index(vv, "kodkod"s) << endl; + cout << v_index(vy, 4) << endl; + + + exit(0); + } + + // if (thread_test){ // unordered_flat_map> runners; diff --git a/src/btop_config.h b/src/btop_config.h index 60bb750..afd8159 100644 --- a/src/btop_config.h +++ b/src/btop_config.h @@ -41,7 +41,7 @@ namespace Config { atomic locked (false); atomic writelock (false); - bool changed; + bool write_new; vector> descriptions = { {"color_theme", "#* Color theme, looks for a .theme file in \"/usr/[local/]share/bpytop/themes\" and \"~/.config/bpytop/themes\", \"Default\" for builtin default theme.\n" @@ -160,7 +160,7 @@ namespace Config { bool _locked(){ atomic_wait(writelock); - if (!changed) changed = true; + if (!write_new) write_new = true; return locked.load(); } } @@ -238,14 +238,71 @@ namespace Config { } //* Load the config file from disk - void load(){ - if (conf_file.empty()) return; - else if (!fs::exists(conf_file)) { changed = true; return; } + void load(fs::path conf_file, vector& load_errors){ + if (conf_file.empty()) + return; + else if (!fs::exists(conf_file)) { + write_new = true; + return; + } + std::ifstream cread(conf_file); + if (cread.good()) { + unordered_flat_map valid_names; + for (auto &n : descriptions) + valid_names[n[0]] = 0; + string v_string; + getline(cread, v_string, '\n'); + if (!v_string.ends_with(Global::Version)) + write_new = true; + while (!cread.eof()) { + cread >> std::ws; + if (cread.peek() == '#') { + cread.ignore(SSmax, '\n'); + continue; + } + string name, value; + getline(cread, name, '='); + if (!valid_names.contains(name)) { + cread.ignore(SSmax, '\n'); + continue; + } + + if (bools.contains(name)) { + cread >> value; + if (!isbool(value)) + load_errors.push_back("Got an invalid bool value for config name: " + name); + else + bools.at(name) = stobool(value); + } + else if (ints.contains(name)) { + cread >> value; + if (!isint(value)) + load_errors.push_back("Got an invalid integer value for config name: " + name); + else + ints.at(name) = stoi(value); + } + else if (strings.contains(name)) { + cread >> std::ws; + if (cread.peek() == '"') { + cread.ignore(1); + getline(cread, value, '"'); + } + else cread >> value; + + if (name == "log_level" && !v_contains(Logger::log_levels, value)) load_errors.push_back("Invalid log_level: " + value); + else strings.at(name) = value; + } + + cread.ignore(SSmax, '\n'); + } + cread.close(); + if (!load_errors.empty()) write_new = true; + } } //* Write the config file to disk void write(){ - if (conf_file.empty() || !changed) return; + if (conf_file.empty() || !write_new) return; Logger::debug("Writing new config file"); std::ofstream cwrite(conf_file, std::ios::trunc); if (cwrite.good()) { diff --git a/src/btop_draw.h b/src/btop_draw.h index 862e8b2..8e0d38e 100644 --- a/src/btop_draw.h +++ b/src/btop_draw.h @@ -193,14 +193,13 @@ namespace Draw { int ai = 0; for (auto value : {last, data_value}) { if (value >= cur_high) - result[ai] = 4; + result[ai++] = 4; else if (value <= cur_low) - result[ai] = 0; + result[ai++] = 0; else { - result[ai] = round((float)(value - cur_low) * 4 / (cur_high - cur_low) + mod); + result[ai++] = round((float)(value - cur_low) * 4 / (cur_high - cur_low) + mod); if (no_zero && horizon == height - 1 && i != -1 && result[ai] == 0) result[ai] = 1; } - ai++; } //? Generate braille symbol from 5x5 2D vector graphs[current][horizon] += (height == 1 && result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])]; @@ -228,8 +227,10 @@ namespace Draw { graphs[true].clear(); graphs[false].clear(); this->width = width; this->height = height; this->invert = invert; this->offset = offset; - this->no_zero = no_zero; this->max_value = max_value; + this->no_zero = no_zero; this->color_gradient = color_gradient; + if (max_value == 0 && offset > 0) max_value = 100; + this->max_value = max_value; int value_width = ceil((float)data.size() / 2); int data_offset = 0; if (value_width > width) data_offset = data.size() - width * 2; @@ -268,6 +269,7 @@ namespace Box { + } namespace Proc { diff --git a/src/btop_input.h b/src/btop_input.h index 79e2fa9..7ba087b 100644 --- a/src/btop_input.h +++ b/src/btop_input.h @@ -90,10 +90,10 @@ namespace Input { } //* Get a key or mouse action from input - string get(){ + string get(bool clear = false){ string key; while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get(); - if (!key.empty()){ + if (!clear && !key.empty()){ if (key.substr(0,2) == Fx::e) key.erase(0, 1); if (Key_escapes.contains(key)) key = Key_escapes.at(key); else if (ulen(key) > 1) key = ""; @@ -105,9 +105,10 @@ namespace Input { //* Wait until input is available void wait(bool clear=false){ while (cin.rdbuf()->in_avail() < 1) sleep_ms(10); - if (clear) get(); + if (clear) get(clear); } + //* Clears last entered key void clear(){ last.clear(); } diff --git a/src/btop_linux.h b/src/btop_linux.h index 235d54d..6e68b30 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -44,7 +44,6 @@ using std::string, std::vector, std::array, std::ifstream, std::atomic, std::num using std::cout, std::flush, std::endl; namespace fs = std::filesystem; using namespace Tools; -const auto SSmax = std::numeric_limits::max(); //? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- @@ -63,6 +62,9 @@ namespace Proc { struct p_cache { string name, cmd, user; uint64_t cpu_t = 0, cpu_s = 0; + string prefix = ""; + size_t depth = 0; + bool collapsed = false; }; unordered_flat_map cache; unordered_flat_map uid_user; @@ -92,7 +94,7 @@ namespace Proc { }; unordered_flat_map sort_map; - //* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c, state, cpu_n, p_nice, ppid + //* Container for process information struct proc_info { uint pid; string name = "", cmd = ""; @@ -102,21 +104,25 @@ namespace Proc { double cpu_p = 0.0, cpu_c = 0.0; char state = '0'; int cpu_n = 0, p_nice = 0; - uint ppid = 0; + int ppid = -1; + string prefix = ""; }; vector current_procs; - //* Collects process information from /proc, saves to and returns reference to Proc::current_procs; - auto& collect(string sorting="pid", bool reverse=false, string filter="", bool per_core=true){ + //* Collects and sorts process information from /proc, saves to and returns reference to Proc::current_procs; + auto& collect(string sorting="pid", bool reverse=false, string filter="", bool per_core=true, bool tree=false){ atomic_wait_set(collecting); ifstream pread; auto uptime = system_uptime(); vector procs; + vector pid_list; procs.reserve((numpids + 10)); + pid_list.reserve(numpids + 10); int npids = 0; int cmult = (per_core) ? Global::coreCount : 1; + (void)tree; //* Update uid_user map if /etc/passwd changed since last run if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) { @@ -160,6 +166,7 @@ namespace Proc { if (d.is_directory() && isdigit(pid_str[0])) { npids++; proc_info new_proc (stoul(pid_str)); + pid_list.push_back(new_proc.pid); //* Cache program name, command and username if (!cache.contains(new_proc.pid)) { @@ -238,7 +245,7 @@ namespace Proc { break; } case 1: { //? Process parent pid - new_proc.ppid = stoul(instr.substr(s_pos, c_pos - s_pos)); + new_proc.ppid = stoi(instr.substr(s_pos, c_pos - s_pos)); break; } case 11: { //? Process utime @@ -297,7 +304,7 @@ namespace Proc { } - //* Sort processes vector + //* Sort processes std::ranges::sort(procs, [sortint = sort_map.at(sorting), &reverse](proc_info& a, proc_info& b) { switch (sortint) { case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid; @@ -326,14 +333,13 @@ namespace Proc { //* Clear dead processes from cache at a regular interval if (++counter >= 10000 || ((int)cache.size() > npids + 100)) { - unordered_flat_map r_cache; - r_cache.reserve(procs.size()); counter = 0; - if (filter.empty()) { - for (auto& p : procs) r_cache[p.pid] = cache[p.pid]; - cache.swap(r_cache); + unordered_flat_map r_cache; + r_cache.reserve(pid_list.size()); + for (auto& p : pid_list) { + if (cache.contains(p)) r_cache[p] = cache.at(p); } - else cache.clear(); + cache.swap(r_cache); } old_cputimes = cputimes; atomic_wait(drawing); diff --git a/src/btop_tools.h b/src/btop_tools.h index 9d61790..2c4f178 100644 --- a/src/btop_tools.h +++ b/src/btop_tools.h @@ -38,7 +38,7 @@ tab-size = 4 #include #include -using std::string, std::vector, std::array, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map; +using std::string, std::string_view, std::vector, std::array, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map; namespace fs = std::filesystem; //? ------------------------------------------------- NAMESPACES ------------------------------------------------------ @@ -225,8 +225,10 @@ namespace Term { namespace Tools { + const auto SSmax = std::numeric_limits::max(); + //* Return number of UTF8 characters in a string with option to disregard escape sequences - size_t ulen(string str, bool escape=false){ + size_t ulen(string str, const bool escape=false){ if (escape) str = std::regex_replace(str, Fx::escape_regex, ""); return std::count_if(str.begin(), str.end(), [](char c) { return (static_cast(c) & 0xC0) != 0x80; } ); @@ -247,6 +249,18 @@ namespace Tools { return str; } + //* Check if vector contains value + template + bool v_contains(vector& vec, T find_val) { + return std::ranges::find(vec, find_val) != vec.end(); + } + + //* Return index of from vector , returns size of if is not present + template + size_t v_index(vector& vec, T find_val) { + return std::ranges::distance(vec.begin(), std::ranges::find(vec, find_val)); + } + //* Return current time since epoch in seconds uint64_t time_s(){ return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -267,49 +281,57 @@ namespace Tools { return (str == "true") || (str == "false") || (str == "True") || (str == "False"); } - //* Check if a string is a valid positive integer value - bool isuint(string& str){ - return all_of(str.begin(), str.end(), ::isdigit); + //* Convert string to bool, returning any value not equal to "true" or "True" as false + bool stobool(string& str){ + return (str == "true" || str == "True") ? true : false; } - //* Left-trim from and return string - string ltrim(string str, string t_str = " "){ - while (str.starts_with(t_str)) str.erase(0, t_str.size()); - return str; + //* Check if a string is a valid integer value + bool isint(string& str){ + if (str.empty()) return false; + size_t offset = (str[0] == '-' ? 1 : 0); + return all_of(str.begin() + offset, str.end(), ::isdigit); } - //* Right-trim from and return string - string rtrim(string str, string t_str = " "){ - while (str.ends_with(t_str)) str.resize(str.size() - t_str.size()); - return str; + //* Left-trim from and return new string + string ltrim(const string& str, const string t_str = " "){ + string_view str_v = str; + while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size()); + return (string)str_v; } - //* Left-right-trim from and return string - string trim(string str, string t_str = " "){ + //* Right-trim from and return new string + string rtrim(const string& str, const string t_str = " "){ + string_view str_v = str; + while (str_v.ends_with(t_str)) str_v.remove_suffix(t_str.size()); + return (string)str_v; + } + + //* Left-right-trim from and return new string + string trim(const string& str, const string t_str = " "){ return ltrim(rtrim(str, t_str), t_str); } //* Split at