diff options
author | aristocratos <gnmjpl@gmail.com> | 2021-06-19 14:57:27 +0200 |
---|---|---|
committer | aristocratos <gnmjpl@gmail.com> | 2021-06-19 14:57:27 +0200 |
commit | d459d088a0dfb216fc6827a940d8da3f61066ecd (patch) | |
tree | d56fd230fa7445b543f388166d3042c8fef28871 /src | |
parent | ba481d042cf340542bf642283a25a1faf5ec421b (diff) |
File reorganization and more efficient build
Diffstat (limited to 'src')
-rw-r--r-- | src/btop.cpp | 651 | ||||
-rw-r--r-- | src/btop_config.cpp | 337 | ||||
-rw-r--r-- | src/btop_config.h | 285 | ||||
-rw-r--r-- | src/btop_draw.cpp | 281 | ||||
-rw-r--r-- | src/btop_draw.h | 222 | ||||
-rw-r--r-- | src/btop_input.h | 9 | ||||
-rw-r--r-- | src/btop_linux.cpp | 411 | ||||
-rw-r--r-- | src/btop_linux.h | 392 | ||||
-rw-r--r-- | src/btop_menu.h | 7 | ||||
-rw-r--r-- | src/btop_theme.cpp | 288 | ||||
-rw-r--r-- | src/btop_theme.h | 262 | ||||
-rw-r--r-- | src/btop_tools.cpp | 432 | ||||
-rw-r--r-- | src/btop_tools.h | 457 |
13 files changed, 2561 insertions, 1473 deletions
diff --git a/src/btop.cpp b/src/btop.cpp new file mode 100644 index 0000000..8ed0a60 --- /dev/null +++ b/src/btop.cpp @@ -0,0 +1,651 @@ +/* Copyright 2021 Aristocratos (jakob@qvantnet.com) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +indent = tab +tab-size = 4 +*/ + + +#include <string> +#include <array> +#include <list> +#include <vector> +#include <csignal> +#include <thread> +#include <future> +#include <atomic> +#include <numeric> +#include <ranges> +#include <filesystem> +#include <unistd.h> +#include <robin_hood.h> +#include <cmath> + +#include <btop_tools.h> +#include <btop_config.h> +#include <btop_input.h> +#include <btop_theme.h> +#include <btop_draw.h> +#include <btop_menu.h> + +#if defined(__linux__) + #define LINUX + #include <btop_linux.h> +#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__) + #include <sys/param.h> + #if defined(BSD) + // #include <btop_bsd.h> + #error BSD support not yet implemented! + #endif +#elif defined(__APPLE__) && defined(__MACH__) + #include <TargetConditionals.h> + #if TARGET_OS_MAC == 1 + #define OSX + // #include <btop_osx.h> + #error OSX support not yet implemented! + #endif +#else + #error Platform not supported! +#endif + +namespace Global { + const std::vector<std::array<std::string, 2>> Banner_src = { + {"#E62525", "██████╗ ████████╗ ██████╗ ██████╗"}, + {"#CD2121", "██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗ ██╗ ██╗"}, + {"#B31D1D", "██████╔╝ ██║ ██║ ██║██████╔╝ ██████╗██████╗"}, + {"#9A1919", "██╔══██╗ ██║ ██║ ██║██╔═══╝ ╚═██╔═╝╚═██╔═╝"}, + {"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"}, + {"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"}, + }; + std::string Version = "0.0.21"; + int coreCount; +} + +using std::string, std::vector, std::array, robin_hood::unordered_flat_map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate; +using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status, std::to_string, std::round; +namespace fs = std::filesystem; +namespace rng = std::ranges; +using namespace Tools; + + +namespace Global { + string banner; + size_t banner_width = 0; + + fs::path self_path; + + bool debuginit = false; + bool debug = false; + + uint64_t start_time; + + bool quitting = false; + + bool arg_tty = false; +} + + +//* A simple argument parser +void argumentParser(int argc, char **argv){ + string argument; + for(int i = 1; i < argc; i++) { + argument = argv[i]; + if (argument == "-v" || argument == "--version") { + cout << "btop version: " << Global::Version << endl; + exit(0); + } + else if (argument == "-h" || argument == "--help") { + cout << "usage: btop [-h] [-v] [-/+t] [--debug]\n\n" + << "optional arguments:\n" + << " -h, --help show this help message and exit\n" + << " -v, --version show version info and exit\n" + << " -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n" + << " +t, --tty_off force (OFF) tty mode\n" + << " --debug start with loglevel set to DEBUG, overriding value set in config\n" + << endl; + exit(0); + } + else if (argument == "--debug") + Global::debug = true; + else if (argument == "-t" || argument == "--tty_on") { + Config::set("tty_mode", true); + Global::arg_tty = true; + } + else if (argument == "+t" || argument == "--tty_off") { + Config::set("tty_mode", false); + Global::arg_tty = true; + } + else { + cout << " Unknown argument: " << argument << "\n" << + " Use -h or --help for help." << endl; + exit(1); + } + } +} + +void clean_quit(int sig){ + if (Global::quitting) return; + if (Term::initialized) { + Term::restore(); + if (!Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush; + } + Global::quitting = true; + Config::write(); + Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time)); + if (sig != -1) exit(sig); +} + +void sleep_now(){ + if (Term::initialized) { + Term::restore(); + if (!Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush; + } + std::raise(SIGSTOP); +} + +void resume_now(){ + Term::init(); + if (!Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << flush; +} + +void _exit_handler() { clean_quit(-1); } + +void _signal_handler(int sig) { + switch (sig) { + case SIGINT: + clean_quit(0); + break; + case SIGTSTP: + sleep_now(); + break; + case SIGCONT: + resume_now(); + break; + } +} + +//? Generate the btop++ banner +void banner_gen() { + size_t z = 0; + string b_color, bg, fg, oc, letter; + bool truecolor = Config::getB("truecolor"); + int bg_i; + Global::banner.clear(); + Global::banner_width = 0; + auto tty_mode = (Config::getB("tty_mode")); + for (auto line: Global::Banner_src) { + if (auto w = ulen(line[1]); w > Global::banner_width) Global::banner_width = w; + fg = Theme::hex_to_color(line[0], !truecolor); + bg_i = 120-z*12; + bg = Theme::dec_to_color(bg_i, bg_i, bg_i, !truecolor); + for (size_t i = 0; i < line[1].size(); i += 3) { + if (line[1][i] == ' ') { + letter = ' '; + i -= 2; + } + else + letter = line[1].substr(i, 3); + + if (tty_mode && letter != "█" && letter != " ") letter = "░"; + b_color = (letter == "█") ? fg : bg; + if (b_color != oc) Global::banner += b_color; + Global::banner += letter; + oc = b_color; + } + if (++z < Global::Banner_src.size()) Global::banner += Mv::l(ulen(line[1])) + Mv::d(1); + } + Global::banner += Mv::r(18 - Global::Version.size()) + Fx::i + Theme::dec_to_color(0,0,0, !truecolor, "bg") + + Theme::dec_to_color(150, 150, 150, !truecolor) + "v" + Global::Version + Fx::ui; +} + +//* Threading test function +// string my_worker(int x){ +// for (int i = 0; i < 100 + (x * 100); i++){ +// sleep_ms(10); +// if (Global::stop_all.load()) return "Thread stopped! x=" + to_string(x); +// } +// return "Thread done! x=" + to_string(x); +// } + + +//? --------------------------------------------- Main starts here! --------------------------------------------------- +int main(int argc, char **argv){ + + //? Init + + Global::start_time = time_s(); + + cout.setf(std::ios::boolalpha); + if (argc > 1) argumentParser(argc, argv); + + std::atexit(_exit_handler); + std::at_quick_exit(_exit_handler); + std::signal(SIGINT, _signal_handler); + std::signal(SIGTSTP, _signal_handler); + std::signal(SIGCONT, _signal_handler); + + //? Linux init + #if defined(LINUX) + Global::coreCount = sysconf(_SC_NPROCESSORS_ONLN); + if (Global::coreCount < 1) Global::coreCount = 1; + + { + std::error_code ec; + Global::self_path = fs::read_symlink("/proc/self/exe", ec).remove_filename(); + } + #endif + + //? Setup paths for config, log and themes + for (auto env : {"XDG_CONFIG_HOME", "HOME"}) { + if (getenv(env) != NULL && access(getenv(env), W_OK) != -1) { + Config::conf_dir = fs::path(getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop"); + break; + } + } + if (!Config::conf_dir.empty()) { + if (std::error_code ec; !fs::is_directory(Config::conf_dir) && !fs::create_directories(Config::conf_dir, ec)) { + cout << "WARNING: Could not create or access btop config directory. Logging and config saving disabled." << endl; + cout << "Make sure your $HOME environment variable is correctly set to fix this." << endl; + } + else { + Config::conf_file = Config::conf_dir / "btop.conf"; + Logger::logfile = Config::conf_dir / "btop.log"; + Theme::user_theme_dir = Config::conf_dir / "themes"; + if (!fs::exists(Theme::user_theme_dir) && !fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear(); + } + } + if (std::error_code ec; !Global::self_path.empty()) { + Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec); + if (ec || access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear(); + } + + if (Theme::theme_dir.empty()) { + for (auto theme_path : {"/usr/local/share/btop/themes", "/usr/share/btop/themes"}) { + if (fs::exists(fs::path(theme_path)) && access(theme_path, R_OK) != -1) { + Theme::theme_dir = fs::path(theme_path); + break; + } + } + } + + //? Config init + { vector<string> load_errors; + Config::load(Config::conf_file, load_errors); + + if (Global::debug) Logger::set("DEBUG"); + else Logger::set(Config::getS("log_level")); + + Logger::debug("Logger set to DEBUG"); + + for (auto& err_str : load_errors) Logger::warning(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" + "Make sure your $LANG evironment variable is set and with a UTF-8 locale."; + Logger::warning(err_msg); + cout << "WARNING: " << err_msg << endl; + } + + //? Initialize terminal and set options + if (!Term::init()) { + string err_msg = "No tty detected!\nbtop++ needs an interactive shell to run."; + Logger::error(err_msg); + cout << "ERROR: " << err_msg << endl; + clean_quit(1); + } + + Logger::debug("Running on " + Term::current_tty); + if (!Global::arg_tty && Config::getB("force_tty")) { + Config::set("tty_mode", true); + Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols"); + } + else if (!Global::arg_tty && Term::current_tty.starts_with("/dev/tty")) { + Config::set("tty_mode", true); + Logger::info("Real tty detected, setting 16 color mode and using tty friendly graph symbols"); + } + + + #if defined(LINUX) + //? Linux init + Proc::init(); + #endif + + + // Config::set("truecolor", false); + + auto thts = time_ms(); + + //? Generate the theme + Theme::set(Theme::Default_theme); + + //? Create the btop++ banner + banner_gen(); + + + //* ------------------------------------------------ TESTING ------------------------------------------------------ + + + Global::debuginit = true; + + // cout << Theme("main_bg") << Term::clear << flush; + // bool thread_test = false; + + if (!Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << flush; + + cout << Theme::c("main_fg") << Theme::c("main_bg") << Term::clear << endl; + + cout << Mv::r(Term::width / 2 - Global::banner_width / 2) << Global::banner << endl; + // cout << string(Term::width - 1, '-') << endl; + size_t blen = (Term::width > 200) ? 200 : Term::width; + if (Term::width > 203) cout << Mv::r(Term::width / 2 - blen / 2) << flush; + int ill = 0; + for (int i : iota(0, (int)blen)){ + ill = (i <= (int)blen / 2) ? i : ill - 1; + cout << Theme::g("used")[ill] << Symbols::h_line; + } + cout << Fx::reset << endl; + + //* Test theme + if (false) { + cout << "Theme generation took " << time_ms() - thts << "ms" << endl; + + cout << "Colors:" << endl; + uint i = 0; + for(auto& item : Theme::test_colors()) { + cout << rjust(item.first, 15) << ":" << item.second << "■"s * 10 << Fx::reset << " "; + // << Theme::dec(item.first)[0] << ":" << Theme::dec(item.first)[1] << ":" << Theme::dec(item.first)[2] << ; + if (++i == 4) { + i = 0; + cout << endl; + } + } + cout << Fx::reset << endl; + + + cout << "Gradients:"; + for (auto& [name, cvec] : Theme::test_gradients()) { + cout << endl << rjust(name + ":", 10); + for (auto& color : cvec) { + cout << color << "■"; + } + + cout << Fx::reset << endl; + } + + + exit(0); + } + + + if (false) { + Draw::Meter kmeter; + kmeter(Term::width - 2, "cpu", false); + cout << kmeter(25) << endl; + cout << kmeter(0) << endl; + cout << kmeter(50) << endl; + cout << kmeter(100) << endl; + cout << kmeter(50) << endl; + exit(0); + } + + if (false) { + cout << fs::absolute(fs::current_path() / "..") << endl; + cout << Global::self_path << endl; + cout << Theme::theme_dir << endl; + cout << Config::conf_dir << endl; + exit(0); + } + + if (false) { + + vector<long long> mydata; + for (long long i = 0; i <= 100; i++) mydata.push_back(i); + for (long long i = 100; i >= 0; i--) mydata.push_back(i); + // mydata.push_back(0); + // mydata.push_back(0); + mydata.push_back(50); + + + // for (long long i = 0; i <= 100; i++) mydata.push_back(i); + // for (long long i = 100; i >= 0; i--) mydata.push_back(i); + + Draw::Graph kgraph {}; + Draw::Graph kgraph2 {}; + Draw::Graph kgraph3 {}; + + cout << Draw::createBox({.x = 5, .y = 10, .width = Term::width - 10, .height = 12, .line_color = Theme::c("proc_box"), .title = "braille", .fill = false, .num = 1}) << Mv::save; + cout << Draw::createBox({.x = 5, .y = 23, .width = Term::width - 10, .height = 12, .line_color = Theme::c("proc_box"), .title = "block", .fill = false, .num = 2}); + cout << Draw::createBox({.x = 5, .y = 36, .width = Term::width - 10, .height = 12, .line_color = Theme::c("proc_box"), .title = "tty", .fill = false, .num = 3}) << flush; + // Draw::Meter kmeter {}; + // Draw::Graph kgraph2 {}; + // Draw::Graph kgraph3 {}; + + auto kts = time_micros(); + kgraph(Term::width - 12, 10, "cpu", mydata, "braille", false, false); + kgraph2(Term::width - 12, 10, "cpu", mydata, "block", false, false); + kgraph3(Term::width - 12, 10, "cpu", mydata, "tty", false, false); + + // kmeter(Term::width - 12, "process"); + // cout << Mv::save << kgraph(mydata) << "\n\nInit took " << time_micros() - kts << " μs. " << endl; + + // exit(0); + // kgraph2(Term::width, 10, "process", mydata, true, false); + // kgraph3(Term::width, 1, "process", mydata, false, false); + // cout << kgraph() << endl; + // cout << kgraph2() << endl; + // exit(0); + + cout << Mv::restore << kgraph(mydata, true) + << Mv::restore << Mv::d(13) << kgraph2(mydata, true) + << Mv::restore << Mv::d(26) << kgraph3(mydata, true) << endl + << Mv::d(1) << "Init took " << time_micros() - kts << " μs. " << endl; + // cout << Mv::save << kgraph(mydata, true) << "\n" << kgraph2(mydata, true) << "\n" << kgraph3(mydata, true) << "\n" << kmeter(mydata.back()) << "\n\nInit took " << time_micros() - kts << " μs. " << endl; + // sleep_ms(1000); + // mydata.push_back(50); + // cout << Mv::restore << kgraph(mydata) << "\n" << kgraph2(mydata) << "\n\nInit took " << time_micros() - kts << " μs. " << endl; + // exit(0); + + // long long y = 0; + // bool flip = false; + list<uint64_t> ktavg; + while (true) { + mydata.back() = std::rand() % 101; + // mydata.back() = y; + kts = time_micros(); + // cout << Mv::restore << " "s * Term::width << "\n" << " "s * Term::width << endl; + cout << Mv::restore << kgraph(mydata) + << Mv::restore << Mv::d(13) << kgraph2(mydata) + << Mv::restore << Mv::d(26) << kgraph3(mydata) + << endl; + // cout << Mv::restore << kgraph(mydata) << "\n" << kgraph2(mydata) << "\n" << " "s * Term::width << Mv::l(Term::width) << kgraph3(mydata) << "\n" << kmeter(mydata.back()) << endl; + ktavg.push_front(time_micros() - kts); + if (ktavg.size() > 100) ktavg.pop_back(); + cout << Mv::d(1) << "Time: " << ktavg.front() << " μs. Avg: " << accumulate(ktavg.begin(), ktavg.end(), 0) / ktavg.size() << " μs. " << flush; + // if (flip) y--; + // else y++; + // if (y == 100 || y == 0) flip = !flip; + if (Input::poll()) { + if (Input::get() == "space") Input::wait(true); + else break; + } + sleep_ms(100); + } + Input::get(); + + exit(0); + + } + + + if (false) { + cout << Config::getS("log_level") << endl; + + vector<string> vv = {"hej", "vad", "du"}; + vector<int> 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<int, future<string>> runners; + // unordered_flat_map<int, string> outputs; + + // for (int i : iota(0, 10)){ + // runners[i] = async(my_worker, i); + // } + // // uint i = 0; + // while (outputs.size() < 10){ + + // for (int i : iota(0, 10)){ + // if (runners[i].valid() && runners[i].wait_for(std::chrono::milliseconds(10)) == future_status::ready) { + // outputs[i] = runners[i].get(); + // cout << "Thread " << i << " : " << outputs[i] << endl; + // } + // } + + // // if (++i >= 10) i = 0; + // if (outputs.size() >= 8) Global::stop_all.store(true); + // } + // } + + + + cout << "Up for " << sec_to_dhms(round(system_uptime())) << endl; + + +//*------>>>>>> Proc testing + + + auto timestamp = time_ms(); + + + + uint lc; + string ostring; + uint64_t tsl, timestamp2, rcount = 0; + list<uint64_t> avgtimes = {0}; + uint timer = 2000; + bool filtering = false; + vector<string> greyscale; + string filter; + string filter_cur; + string key; + + int xc; + for (uint i : iota(0, (int)Term::height - 19)){ + xc = 230 - i * 150 / (Term::height - 20); + greyscale.push_back(Theme::dec_to_color(xc, xc, xc)); + } + + string pbox = Draw::createBox({.x = 1, .y = 10, .width = Term::width, .height = Term::height - 16, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .fill = false, .num = 7}); + pbox += Mv::r(1) + Theme::c("title") + Fx::b + rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 70) + " Threads: " + + ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n" + Fx::reset + Mv::save; + + while (key != "q") { + timestamp = time_micros(); + tsl = time_ms() + timer; + auto plist = Proc::collect(); + timestamp2 = time_micros(); + timestamp = timestamp2 - timestamp; + ostring.clear(); + lc = 0; + + ostring = Mv::u(2) + Mv::l(Term::width) + Mv::r(12) + + trans("Filter: " + filter + (filtering ? Fx::bl + "█" + Fx::reset : "")) + Mv::l(Term::width) + + trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: " + + string(Config::getS("proc_sorting")), Term::width - 3)) + + Mv::restore; + + for (auto& p : plist){ + if (!Config::getB("proc_tree")) { + ostring += Mv::r(1) + greyscale[lc] + rjust(to_string(p.pid), 8) + " " + ljust(p.name, 16) + " " + ljust(p.cmd, Term::width - 66, true) + " " + + rjust(to_string(p.threads), 5) + " " + ljust(p.user, 10) + " " + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ') + + (p.cpu_p < 10 || p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4)) + + "\n"; + } + else { + string cmd_cond; + if (!p.cmd.empty()) { + cmd_cond = p.cmd.substr(0, std::min(p.cmd.find(' '), p.cmd.size())); + cmd_cond = cmd_cond.substr(std::min(cmd_cond.find_last_of('/') + 1, cmd_cond.size())); + } + ostring += Mv::r(1) + greyscale[lc] + ljust(p.prefix + to_string(p.pid) + " " + p.name + " " + (!cmd_cond.empty() && cmd_cond != p.name ? "(" + cmd_cond + ")" : ""), Term::width - 40, true) + " " + + rjust(to_string(p.threads), 5) + " " + ljust(p.user, 10) + " " + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ') + + (p.cpu_p < 10 || p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4)) + + "\n"; + } + if (lc++ > Term::height - 21) break; + } + + while (lc++ < Term::height - 19) ostring += Mv::r(1) + string(Term::width - 2, ' ') + "\n"; + + + + if (rcount > 0) avgtimes.push_front(timestamp); + if (avgtimes.size() > 10) avgtimes.pop_back(); + cout << pbox << ostring << Fx::reset << "\n" << endl; + cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " << + rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() << + " samples. Drawing took: " << time_micros() - timestamp2 << " μs.\nNumber of processes: " << Proc::numpids << ". Run count: " << + ++rcount << ". Time: " << strf_time("%X ") << endl; + + while (time_ms() < tsl) { + if (Input::poll(tsl - time_ms())) key = Input::get(); + else { key.clear() ; continue; } + if (filtering) { + if (key == "enter") filtering = false; + else if (key == "backspace") {if (!filter.empty()) filter = uresize(filter, ulen(filter) - 1);} + else if (key == "space") filter.push_back(' '); + else if (ulen(key) == 1 ) filter.append(key); + else { key.clear(); continue; } + if (filter != Config::getS("proc_filter")) Config::set("proc_filter", filter); + break; + } + else if (key == "q") break; + else if (key == "left") { + int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); + if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1; + Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + } + else if (key == "right") { + int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); + if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0; + Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + } + else if (key == "f") filtering = true; + else if (key == "t") Config::flip("proc_tree"); + else if (key == "r") Config::flip("proc_reversed"); + else if (key == "c") Config::flip("proc_per_core"); + else if (key == "delete") { filter.clear(); Config::set("proc_filter", filter); } + else continue; + break; + } + } + + // cout << "Found " << plist.size() << " pids\n" << endl; + +//*-----<<<<< + + return 0; +} diff --git a/src/btop_config.cpp b/src/btop_config.cpp new file mode 100644 index 0000000..c250a78 --- /dev/null +++ b/src/btop_config.cpp @@ -0,0 +1,337 @@ +/* Copyright 2021 Aristocratos (jakob@qvantnet.com) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +indent = tab +tab-size = 4 +*/ + +#include <array> +#include <robin_hood.h> +#include <ranges> +#include <atomic> +#include <fstream> + +#include <btop_config.h> +#include <btop_tools.h> + +using robin_hood::unordered_flat_map, std::map, std::array, std::atomic; +namespace fs = std::filesystem; +namespace rng = std::ranges; +using namespace Tools; + +//* Functions and variables for reading and writing the btop config file +namespace Config { + namespace { + atomic<bool> locked (false); + atomic<bool> writelock (false); + bool write_new; + + vector<array<string, 2>> 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" + "#* Prefix name by a plus sign (+) for a theme located in user themes folder, i.e. color_theme=\"+monokai\"." }, + {"theme_background", "#* If the theme set background should be shown, set to False if you want terminal background transparency."}, + {"truecolor", "#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false."}, + {"graph_symbol", "#* Default symbols to use for graph creation, \"braille\", \"block\" or \"tty\".\n" + "#* \"braille\" offers the highest resolution but might not be included in all fonts.\n" + "#* \"block\" has half the resolution of braille but uses more common characters.\n" + "#* \"tty\" uses only 3 different symbols but will work with most fonts and should work in a real TTY.\n" + "#* Note that \"tty\" only has half the horizontal resolution of the other two, so will show a shorter historical view."}, + {"graph_symbol_cpu", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."}, + {"graph_symbol_mem", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."}, + {"graph_symbol_net", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."}, + {"graph_symbol_proc", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."}, + {"force_tty", "#* Set to true to true to force tty mode regardless if a real tty has been detected or not."}, + {"shown_boxes", "#* Manually set which boxes to show. Available values are \"cpu mem net proc\", separate values with whitespace."}, + {"update_ms", "#* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs."}, + {"proc_update_mult", "#* Processes update multiplier, sets how often the process list is updated as a multiplier of \"update_ms\".\n" + "#* Set to 2 or higher to greatly decrease bpytop cpu usage. (Only integers)."}, + {"proc_sorting", "#* Processes sorting, \"pid\" \"program\" \"arguments\" \"threads\" \"user\" \"memory\" \"cpu lazy\" \"cpu responsive\",\n" + "#* \"cpu lazy\" updates top process over time, \"cpu responsive\" updates top process directly."}, + {"proc_reversed", "#* Reverse sorting order, True or False."}, + {"proc_tree", "#* Show processes as a tree."}, + {"proc_colors", "#* Use the cpu graph colors in the process list."}, + {"proc_gradient", "#* Use a darkening gradient in the process list."}, + {"proc_per_core", "#* If process cpu usage should be of the core it's running on or usage of the total available cpu power."}, + {"proc_mem_bytes", "#* Show process memory as bytes instead of percent."}, + {"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n" + "#* Select from a list of detected attributes from the options menu."}, + {"cpu_graph_lower", "#* Sets the CPU stat shown in lower half of the CPU graph, \"total\" is always available.\n" + "#* Select from a list of detected attributes from the options menu."}, + {"cpu_invert_lower", "#* Toggles if the lower CPU graph should be inverted."}, + {"cpu_single_graph", "#* Set to True to completely disable the lower CPU graph."}, + {"show_uptime", "#* Shows the system uptime in the CPU box."}, + {"check_temp", "#* Show cpu temperature."}, + {"cpu_sensor", "#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors."}, + {"show_coretemp", "#* Show temperatures for cpu cores also if check_temp is True and sensors has been found."}, + {"temp_scale", "#* Which temperature scale to use, available values: \"celsius\", \"fahrenheit\", \"kelvin\" and \"rankine\"."}, + {"show_cpu_freq", "#* Show CPU frequency."}, + {"draw_clock", "#* Draw a clock at top of screen, formatting according to strftime, empty string to disable."}, + {"background_update", "#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort."}, + {"custom_cpu_name", "#* Custom cpu model name, empty string to disable."}, + {"disks_filter", "#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with a comma \",\".\n" + "#* Begin line with \"exclude=\" to change to exclude filter, otherwise defaults to \"most include\" filter. Example: disks_filter=\"exclude=/boot, /home/user\"."}, + {"mem_graphs", "#* Show graphs instead of meters for memory values."}, + {"show_swap", "#* If swap memory should be shown in memory box."}, + {"swap_disk", "#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk."}, + {"show_disks", "#* If mem box should be split to also show disks info."}, + {"only_physical", "#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar."}, + {"use_fstab", "#* Read disks list from /etc/fstab. This also disables only_physical."}, + {"show_io_stat", "#* Toggles if io stats should be shown in regular disk usage view."}, + {"io_mode", "#* Toggles io mode for disks, showing only big graphs for disk read/write speeds."}, + {"io_graph_combined", "#* Set to True to show combined read/write io graphs in io mode."}, + {"io_graph_speeds", "#* Set the top speed for the io graphs in MiB/s (10 by default), use format \"device:speed\" separate disks with a comma \",\".\n" + "#* Example: \"/dev/sda:100, /dev/sdb:20\"."}, + {"net_download", "#* Set fixed values for network graphs, default \"10M\" = 10 Mibibytes, possible units \"K\", \"M\", \"G\", append with \"bit\" for bits instead of bytes, i.e \"100mbit\"."}, + {"net_upload", ""}, + {"net_auto", "#* Start in network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest."}, + {"net_sync", "#* Sync the scaling for download and upload to whichever currently has the highest scale."}, + {"net_color_fixed", "#* If the network graphs color gradient should scale to bandwidth usage or auto scale, bandwidth usage is based on \"net_download\" and \"net_upload\" values."}, + {"net_iface", "#* Starts with the Network Interface specified here."}, + {"show_battery", "#* Show battery stats in top right if battery is present."}, + {"log_level", "#* Set loglevel for \"~/.config/bpytop/error.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n" + "#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."} + }; + + unordered_flat_map<string, string> strings = { + {"color_theme", "Default"}, + {"shown_boxes", "cpu mem net proc"}, + {"graph_symbol", "braille"}, + {"graph_symbol_cpu", "default"}, + {"graph_symbol_mem", "default"}, + {"graph_symbol_net", "default"}, + {"graph_symbol_proc", "default"}, + {"proc_sorting", "cpu lazy"}, + {"cpu_graph_upper", "total"}, + {"cpu_graph_lower", "total"}, + {"cpu_sensor", "Auto"}, + {"temp_scale", "celsius"}, + {"draw_clock", "%X"}, + {"custom_cpu_name", ""}, + {"disks_filter", ""}, + {"io_graph_speeds", ""}, + {"net_download", "10M"}, + {"net_upload", "10M"}, + {"net_iface", ""}, + {"log_level", "WARNING"}, + {"proc_filter", ""} + }; + unordered_flat_map<string, string> stringsTmp; + + unordered_flat_map<string, bool> bools = { + {"theme_background", true}, + {"truecolor", true}, + {"proc_reversed", false}, + {"proc_tree", false}, + {"proc_colors", true}, + {"proc_gradient", true}, + {"proc_per_core", false}, + {"proc_mem_bytes", true}, + {"cpu_invert_lower", true}, + {"cpu_single_graph", false}, + {"show_uptime", true}, + {"check_temp", true}, + {"show_coretemp", true}, + {"show_cpu_freq", true}, + {"background_update", true}, + {"mem_graphs", true}, + {"show_swap", true}, + {"swap_disk", true}, + {"show_disks", true}, + {"only_physical", true}, + {"use_fstab", false}, + {"show_io_stat", true}, + {"io_mode", false}, + {"io_graph_combined", false}, + {"net_color_fixed", false}, + {"net_auto", true}, + {"net_sync", false}, + {"show_battery", true}, + {"tty_mode", false}, + {"force_tty", false}, + }; + unordered_flat_map<string, bool> boolsTmp; + + unordered_flat_map<string, int> ints = { + {"update_ms", 2000}, + {"proc_update_mult", 2}, + }; + unordered_flat_map<string, int> intsTmp; + + bool _locked(const string& name){ + atomic_wait(writelock); + if (!write_new && rng::find_if(descriptions, [&name](const auto& a){ return a.at(0) == name; }) != descriptions.end()) + write_new = true; + return locked.load(); + } + } + + fs::path conf_dir; + fs::path conf_file; + + vector<string> valid_graph_symbols = { "braille", "block", "tty" }; + + //* Return bool config value <name> + const bool& getB(string name){ + return bools.at(name); + } + + //* Return integer config value <name> + const int& getI(string name){ + return ints.at(name); + } + + //* Return string config value <name> + const string& getS(string name){ + return strings.at(name); + } + + //* Set config value <name> to bool <value> + void set(string name, bool value){ + if (_locked(name)) boolsTmp.insert_or_assign(name, value); + else bools.at(name) = value; + } + + //* Set config value <name> to int <value> + void set(string name, int value){ + if (_locked(name)) intsTmp.insert_or_assign(name, value); + ints.at(name) = value; + } + + //* |