diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 139878e..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -file(GLOB sources *.h *.c *.cxx *.cpp *.hxx) -add_executable(btop ${sources}) \ No newline at end of file diff --git a/Makefile b/Makefile index cbda712..5eb9277 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ PREFIX ?= /usr/local DOCDIR ?= $(PREFIX)/share/btop/doc CXX = g++ -CXXFLAGS = -std=c++20 -pthread -Wall +override CXXFLAGS += -std=c++20 -pthread -Wall -Wextra INCLUDES = -I./src btop: btop.cpp diff --git a/btop.cpp b/btop.cpp index 2fd9e3d..84650dc 100644 --- a/btop.cpp +++ b/btop.cpp @@ -66,6 +66,9 @@ namespace Global { }; const string Version = "0.0.1"; + + string banner; + uint banner_width; } @@ -90,46 +93,42 @@ void argumentParser(int argc, char **argv){ } } -class C_Banner { - string banner_str; - -public: - int width = 0; - - C_Banner(){ - size_t z = 0; - string b_color, bg, fg, out, oc, letter; - int bg_i; - int new_len; - for (auto line: Global::Banner_src) { - new_len = ulen(line[1]); - if (new_len > width) width = new_len; - fg = hex_to_color(line[0]); - bg_i = 120-z*12; - bg = dec_to_color(bg_i, bg_i, bg_i); - for (uint i = 0; i < line[1].size(); i += 3) { - if (line[1][i] == ' '){ - letter = ' '; - i -= 2; - } else{ - letter = line[1].substr(i, 3); - } - b_color = (letter == "█") ? fg : bg; - if (b_color != oc) out += b_color; - out += letter; - oc = b_color; +//* Generate the btop++ banner +auto create_banner(){ + struct out_vals { + uint w; + string s; + }; + size_t z = 0; + uint width=0, new_len=0; + string b_color, bg, fg, out, oc, letter; + bool truecolor = Config::getB("truecolor"); + int bg_i; + for (auto line: Global::Banner_src) { + if ((new_len = ulen(line[1])) > width) width = new_len; + 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 (uint i = 0; i < line[1].size(); i += 3) { + if (line[1][i] == ' '){ + letter = ' '; + i -= 2; + } else{ + letter = line[1].substr(i, 3); } - z++; - if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1); + b_color = (letter == "█") ? fg : bg; + if (b_color != oc) out += b_color; + out += letter; + oc = b_color; } - banner_str = out + Mv::r(18 - Global::Version.size()) + Fx::i + dec_to_color(0,0,0, State::truecolor, "bg") + dec_to_color(150, 150, 150) + "v" + Global::Version; + z++; + if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1); } + out += 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::reset; + return out_vals {width, out}; +} - //* Returns the pre-generated btop++ banner - string operator() (){ - return banner_str + Fx::reset; - } -}; //* Threading test function string my_worker(int x){ @@ -159,41 +158,38 @@ int main(int argc, char **argv){ } //? Initialize terminal and set options - C_Term Term; - if (!Term.initialized) { + if (!Term::init()) { cout << "ERROR: No tty detected!" << endl; cout << "Sorry, btop++ needs an interactive shell to run." << endl; exit(1); } //? Read config file if present - C_Config Config; + Config::load("____"); //? Generate the theme - C_Theme Theme(Global::Default_theme); + Theme::set(Global::Default_theme); //? Create the btop++ banner - C_Banner Banner; + auto [banner_width, banner] = create_banner(); + Global::banner_width = move(banner_width); + Global::banner = move(banner); - //? Initialize the Input class - C_Input Input; //* ------------------------------------------------ TESTING ------------------------------------------------------ - int debug = 2; + int debug = 0; int tests = 0; - // cout << Theme("main_bg") << Term.clear << flush; + // cout << Theme("main_bg") << Term::clear << flush; bool thread_test = false; - if (debug < 2) cout << Term.alt_screen << Term.clear << Term.hide_cursor << flush; - - cout << Theme("main_fg") << Term.clear << endl; - - cout << Mv::r(Term.width / 2 - Banner.width / 2) << Banner() << endl; - cout << string(Term.width - 1, '-') << endl; + if (debug < 2) 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; //* Test MENUS @@ -215,7 +211,7 @@ int main(int argc, char **argv){ int i = 0; if (tests>0) for(auto& item : Global::Default_theme) { - cout << Theme(item.first) << item.first << ":" << Theme("main_fg") << Theme(item.first).erase(0, 2) << Fx::reset << " "; + cout << Theme::c(item.first) << item.first << ":" << Theme::c("main_fg") << Theme::c(item.first).erase(0, 2) << Fx::reset << " "; if (++i == 4) { i = 0; cout << endl; @@ -258,11 +254,14 @@ int main(int argc, char **argv){ auto timestamp = time_ms(); - Processes Proc; + Proc::init(); cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl; + cout << "Press any key to start!" << Mv::l(100) << flush; - sleep_ms(1000); + // sleep_ms(1000); + // Input::wait(); + // Input::clear(); // insert Processes call here @@ -270,26 +269,63 @@ int main(int argc, char **argv){ uint lc; string ostring; - cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term.width - 69) << " Threads: " << + uint64_t tsl, timestamp2; + uint timer = 2000; + bool filtering = false; + vector sorting; + bool reversing = false; + int sortint = Proc::sort_map["cpu lazy"]; + string filter; + string filter_cur; + string key; + cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term::width - 69) << " Threads: " << ljustify("User:", 10) << " " << rjustify("MemB", 5) << " " << rjustify("Cpu%", 14) << "\n" << Mv::save << flush; - while (Input() != "q") { + while (key != "q") { timestamp = time_ms(); - auto plist = Proc.collect("cpu lazy", false); - timestamp = time_ms() - timestamp; + tsl = timestamp + timer; + auto plist = Proc::collect(Proc::sort_vector[sortint], reversing, filter); + timestamp2 = time_ms(); + timestamp = timestamp2 - timestamp; ostring.clear(); lc = 0; + filter_cur = (filtering) ? Fx::bl + "█" + Fx::reset : ""; + cout << Mv::restore << Mv::u(2) << Mv::r(20) << rjustify("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') + + "Sorting: " + Proc::sort_vector[sortint], Term::width - 22, true, filtering) << Mv::restore << flush; + for (auto& [lpid, lname, lcmd, lthread, luser, lmem, lcpu, lcpu_s] : plist){ (void) lcpu_s; - ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term.width - 66, true) + " " + - rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true, 1), 5) + string(11, ' '); + ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term::width - 66, true) + " " + + rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true), 5) + string(11, ' '); ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4); ostring += "\n"; - if (lc++ > Term.height - 20) break; + if (lc++ > Term::height - 20) break; + } + + cout << Mv::restore << ostring << Term::clear_end << endl; + cout << "Processes call took: " << timestamp << "ms. Drawing took: " << time_ms() - timestamp2 << "ms." << endl; + + while (time_ms() < tsl) { + if (Input::poll(tsl - time_ms())) key = Input::get(); + else { key.clear() ; continue; } + // if (key != "") 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; } + break; + } + else if (key == "q") break; + else if (key == "left") { if (--sortint < 0) sortint = (int)Proc::sort_vector.size() - 1; } + else if (key == "right") { if (++sortint > (int)Proc::sort_vector.size() - 1) sortint = 0; } + else if (key == "f") filtering = true; + else if (key == "r") reversing = !reversing; + else if (key == "delete") filter.clear(); + else continue; + break; } - cout << Mv::restore << ostring << endl; - cout << "Processes call took: " << timestamp << "ms." << endl; - Input(2000); } // cout << "Found " << plist.size() << " pids\n" << endl; @@ -301,15 +337,15 @@ int main(int argc, char **argv){ if (tests>3){ - auto nbcolor = hex_to_color(Global::Default_theme.at("net_box")); - auto nbcolor_rgb = c_to_rgb(nbcolor); + auto nbcolor = Theme::hex_to_color(Global::Default_theme.at("net_box")); + auto nbcolor_rgb = Theme::rgb(nbcolor); auto nbcolor_man = ssplit(nbcolor, ";"); cout << nbcolor << "Some color" << endl; cout << "nbcolor_rgb size=" << nbcolor_rgb.size() << endl; cout << "R:" << nbcolor_rgb.at("r") << " G:" << nbcolor_rgb.at("g") << " B:" << nbcolor_rgb.at("b") << endl; cout << "MANUAL R:" << nbcolor_man.at(2) << " G:" << nbcolor_man.at(3) << " B:" << nbcolor_man.at(4) << endl; - auto ccc = dec_to_color(100, 255, 100); + auto ccc = Theme::dec_to_color(100, 255, 100); cout << "\n" << ccc << "Testing..." << endl; } @@ -357,9 +393,9 @@ int main(int argc, char **argv){ - if (debug == 0){ - cout << Theme("main_fg"); - cout << Mv::to(Term.height - 1, 0) << "Press q to exit! Timeout" << flush; + if (debug == 3){ + cout << Theme::c("main_fg"); + cout << Mv::to(Term::height - 1, 0) << "Press q to exit! Timeout" << flush; string full, key; int wt = 90; bool qp = false; @@ -367,13 +403,13 @@ int main(int argc, char **argv){ int wtm = wt / 60; int wts = wt - wtm * 60; wt--; - cout << Mv::to(Term.height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush; + cout << Mv::to(Term::height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush; //chr = Key(1000); - if (Input(1000)) { - key = Input(); - cout << Mv::to(Term.height - 2, 1) << "Last key: LEN=" << key.size() << " ULEN=" << ulen(key) << " KEY=\"" << key << "\" CODE=" << (int)key.at(0) << " " << flush; + if (Input::poll(1000)) { + key = Input::get(); + cout << Mv::to(Term::height - 2, 1) << "Last key: LEN=" << key.size() << " ULEN=" << ulen(key) << " KEY=\"" << key << "\" CODE=" << (int)key.at(0) << " " << flush; full += key; - cout << Mv::to(Term.height - 5, 1) << full << flush; + cout << Mv::to(Term::height - 5, 1) << full << flush; if (key == "q") qp = true; key = ""; wt++; @@ -381,8 +417,8 @@ int main(int argc, char **argv){ } } - if (debug == 1) Input(-1); - Term.restore(); - if (debug < 2) cout << Term.normal_screen << Term.show_cursor << flush; + if (debug == 1) Input::wait(); + Term::restore(); + if (debug < 2) cout << Term::normal_screen << Term::show_cursor << flush; return 0; } diff --git a/src/btop_config.h b/src/btop_config.h index b5971b9..5a0e813 100644 --- a/src/btop_config.h +++ b/src/btop_config.h @@ -28,98 +28,105 @@ tab-size = 4 using namespace std; -//? Classes, functions and variables for reading and writing the btop config file +//* Functions and variables for reading and writing the btop config file +namespace Config { + namespace { -#define Bool bool() -#define Int int() -#define String string() + bool changed = false; - -//* Used for classes and functions needing pre-initialised values -namespace State { - bool truecolor = true; - string fg, bg; - uint width, height; -} - -class C_Config { - map strings = { - {"color_theme", "Default"}, - {"shown_boxes", "cpu mem net proc"}, - {"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"} + map strings = { + {"color_theme", "Default"}, + {"shown_boxes", "cpu mem net proc"}, + {"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"} + }; + map 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}, + {"update_check", 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}, + {"show_init", false} + }; + map ints = { + {"update_ms", 2000}, + {"proc_update_mult", 2}, + {"tree_depth", 3} + }; }; - map 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}, - {"update_check", 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}, - {"show_init", false} - }; - map ints = { - {"update_ms", 2000}, - {"proc_update_mult", 2}, - {"tree_depth", 3} - }; -public: - C_Config(){ - bools["truecolor"] = "true"; - strings["color_theme"] = "Default"; - ints["tree_depth"] = 3; - - State::truecolor = bools["truecolor"]; - } //* Return config value as a bool - bool operator()(bool b_type, string name){ + bool& getB(string name){ return bools.at(name); } //* Return config value as a int - int operator()(int i_type, string name){ + int& getI(string name){ return ints.at(name); } //* Return config value as a string - string operator()(string s_type, string name){ + string& getS(string name){ return strings.at(name); } + + //* Set config value to bool + void setB(string name, bool value){ + bools.at(name) = value; + changed = true; + } + + //* Set config value to int + void setI(string name, int value){ + ints.at(name) = value; + changed = true; + } + + //* Set config value to string + void setS(string name, string value){ + strings.at(name) = value; + changed = true; + } + + bool load(string source){ + (void)source; + return true; + } }; #endif \ No newline at end of file diff --git a/src/btop_input.h b/src/btop_input.h index 06b9cfb..3e16168 100644 --- a/src/btop_input.h +++ b/src/btop_input.h @@ -29,59 +29,68 @@ tab-size = 4 using namespace std; -//* Class for handling keyboard and mouse input -class C_Input { - - string last = ""; - - //* Map for translating key codes to readable values - const map Key_escapes = { - {"\033", "escape"}, - {"\n", "enter"}, - {" ", "space"}, - {"\x7f", "backspace"}, - {"\x08", "backspace"}, - {"[A", "up"}, - {"OA", "up"}, - {"[B", "down"}, - {"OB", "down"}, - {"[D", "left"}, - {"OD", "left"}, - {"[C", "right"}, - {"OC", "right"}, - {"[2~", "insert"}, - {"[3~", "delete"}, - {"[H", "home"}, - {"[F", "end"}, - {"[5~", "page_up"}, - {"[6~", "page_down"}, - {"\t", "tab"}, - {"[Z", "shift_tab"}, - {"OP", "f1"}, - {"OQ", "f2"}, - {"OR", "f3"}, - {"OS", "f4"}, - {"[15~", "f5"}, - {"[17~", "f6"}, - {"[18~", "f7"}, - {"[19~", "f8"}, - {"[20~", "f9"}, - {"[21~", "f10"}, - {"[23~", "f11"}, - {"[24~", "f12"} +//* Functions and variables for handling keyboard and mouse input +namespace Input { + namespace { + //* Map for translating key codes to readable values + const map Key_escapes = { + {"\033", "escape"}, + {"\n", "enter"}, + {" ", "space"}, + {"\x7f", "backspace"}, + {"\x08", "backspace"}, + {"[A", "up"}, + {"OA", "up"}, + {"[B", "down"}, + {"OB", "down"}, + {"[D", "left"}, + {"OD", "left"}, + {"[C", "right"}, + {"OC", "right"}, + {"[2~", "insert"}, + {"[3~", "delete"}, + {"[H", "home"}, + {"[F", "end"}, + {"[5~", "page_up"}, + {"[6~", "page_down"}, + {"\t", "tab"}, + {"[Z", "shift_tab"}, + {"OP", "f1"}, + {"OQ", "f2"}, + {"OR", "f3"}, + {"OS", "f4"}, + {"[15~", "f5"}, + {"[17~", "f6"}, + {"[18~", "f7"}, + {"[19~", "f8"}, + {"[20~", "f9"}, + {"[21~", "f10"}, + {"[23~", "f11"}, + {"[24~", "f12"} + }; }; - bool wait(int timeout=0){ - if (timeout == 0) return cin.rdbuf()->in_avail() > 0; + //* Last entered key + string last = ""; + + //* Poll keyboard & mouse input for ms and return input availabilty as a bool + bool poll(int timeout=0){ + if (timeout < 1) return cin.rdbuf()->in_avail() > 0; auto timer = 0; - while (timeout == -1 || timer * 10 <= timeout) { + while (timer * 10 <= timeout) { if (cin.rdbuf()->in_avail() > 0) return true; sleep_ms(10); - if (timeout >= 0) ++timer; + ++timer; } return false; } + //* Wait until input is available + void wait(){ + while (cin.rdbuf()->in_avail() < 1) sleep_ms(10); + } + + //* Get a key or mouse action from input string get(){ string key; while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get(); @@ -89,28 +98,15 @@ class C_Input { 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 = ""; + last = key; } return key; } -public: - - //* Wait ms for input on stdin and return true if available - //* -1 for infinite wait - bool operator()(int timeout){ - if (wait(timeout)) { - last = get(); - return !last.empty(); - } else { - last = ""; - return false; - } + void clear(){ + last.clear(); } - //* Return last entered key - string operator()(){ - return last; - } }; #endif \ No newline at end of file diff --git a/src/btop_linux.h b/src/btop_linux.h index 1b9bea3..b062e3f 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -44,30 +44,35 @@ namespace Global { } -class Processes { - uint64_t tstamp; - long int clk_tck; - map> cache; - map sorts = { - {"pid", 0}, - {"name", 1}, - {"command", 2}, - {"threads", 3}, - {"user", 4}, - {"memory", 5}, - {"cpu direct", 6}, - {"cpu lazy", 7} - }; - map uid_user; - fs::path passwd_path; - fs::file_time_type passwd_time; - map cpu_times; - map cpu_second; - uint counter = 0; - long page_size = sysconf(_SC_PAGE_SIZE); -public: +namespace Proc { + namespace { + uint64_t tstamp; + long int clk_tck; + map> cache; + map uid_user; + fs::path passwd_path; + fs::file_time_type passwd_time; + map cpu_times; + map cpu_second; + uint counter = 0; + long page_size = sysconf(_SC_PAGE_SIZE); + } + atomic stop; atomic running; + vector sort_vector = { + "pid", + "name", + "command", + "threads", + "user", + "memory", + "cpu direct", + "cpu lazy", + }; + map sort_map; + + //* Collects process information from /proc and returns a vector of tuples auto collect(string sorting="pid", bool reverse=false, string filter=""){ @@ -81,7 +86,7 @@ public: auto since_last = time_ms() - tstamp; if (since_last < 1) since_last = 1; auto uptime = system_uptime(); - auto sortint = (sorts.contains(sorting)) ? sorts[sorting] : 5; + auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7; vector pstat; //? Return type! Values in tuple: pid, program, command, threads, username, mem KiB, cpu%, cpu cumulative @@ -121,7 +126,7 @@ public: if (d.is_directory() && isdigit(pid_str[0])) { pid = stoi(pid_str); - //* Get cpu usage, threads and rss mem from [pid]/stat + //* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat if (fs::exists((string)d.path() + "/stat")) { pread.clear(); pstat.clear(); ifstream pread((string)d.path() + "/stat"); @@ -140,9 +145,6 @@ public: //? Cache process start time if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]); - //? Get RSS memory in KiB (will be overriden by /status if available) - rss_mem = (stoull(pstat[23]) * page_size) >> 10; - //? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion cpu = static_cast(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck; @@ -151,23 +153,16 @@ public: cpu_times[pid] = cpu_t; } - //* Get RSS memory in KiB - if (fs::exists((string)d.path() + "/status")) { - pread.clear(); status.clear(); tmpstr.clear(); - ifstream pread((string)d.path() + "/status"); + //* Get RSS memory in bytes from /proc/[pid]/statm + if (fs::exists((string)d.path() + "/statm")) { + pread.clear(); tmpstr.clear(); + ifstream pread((string)d.path() + "/statm"); if (pread.good()) { - while (getline(pread, status, ':')){ - if (status == "VmRSS") { - pread.ignore(); - pread >> ws; - getline(pread, tmpstr, 'k'); - tmpstr.pop_back(); - break; - } else pread.ignore(numeric_limits::max(), '\n'); - } + pread.ignore(numeric_limits::max(), ' '); + pread >> rss_mem; + rss_mem *= page_size; } pread.close(); - if (!tmpstr.empty()) rss_mem = stoull(tmpstr); } //* Cache program name, command and username @@ -263,12 +258,14 @@ public: return procs; } - Processes() { + void init(){ clk_tck = sysconf(_SC_CLK_TCK); tstamp = time_ms(); stop.store(false); passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path; - collect(); + uint i = 0; + for (auto& item : sort_vector) sort_map[item] = i++; + // collect(); } }; diff --git a/src/btop_theme.h b/src/btop_theme.h index b8b70ca..ae75c5c 100644 --- a/src/btop_theme.h +++ b/src/btop_theme.h @@ -30,138 +30,129 @@ tab-size = 4 using namespace std; -//? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- +namespace Theme { -//* Convert 24-bit colors to 256 colors using 6x6x6 color cube -int truecolor_to_256(uint r, uint g, uint b){ - if (r / 11 == g / 11 && g / 11 == b / 11) { - return 232 + r / 11; - } else { - return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16; + namespace { + //* Convert 24-bit colors to 256 colors using 6x6x6 color cube + int truecolor_to_256(uint r, uint g, uint b){ + if (r / 11 == g / 11 && g / 11 == b / 11) { + return 232 + r / 11; + } else { + return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16; + } + } } -} -//* Generate escape sequence for 24-bit or 256 color and return as a string -//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale -//* t_to_256: [true|false] convert 24bit value to 256 color value -//* depth: ["fg"|"bg"] for either a foreground color or a background color -string hex_to_color(string hexa, bool t_to_256=false, string depth="fg"){ - if (hexa.size() > 1){ - hexa.erase(0, 1); - for (auto& c : hexa) if (!isxdigit(c)) return ""; + //* Generate escape sequence for 24-bit or 256 color and return as a string + //* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale + //* t_to_256: [true|false] convert 24bit value to 256 color value + //* depth: ["fg"|"bg"] for either a foreground color or a background color + string hex_to_color(string hexa, bool t_to_256=false, string depth="fg"){ + if (hexa.size() > 1){ + hexa.erase(0, 1); + for (auto& c : hexa) if (!isxdigit(c)) return ""; + depth = (depth == "fg") ? "38" : "48"; + string pre = Fx::e + depth + ";"; + pre += (t_to_256) ? "5;" : "2;"; + + if (hexa.size() == 2){ + uint h_int = stoi(hexa, 0, 16); + if (t_to_256){ + return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m"; + } else { + string h_str = to_string(h_int); + return pre + h_str + ";" + h_str + ";" + h_str + "m"; + } + } + else if (hexa.size() == 6){ + if (t_to_256){ + return pre + to_string(truecolor_to_256( + stoi(hexa.substr(0, 2), 0, 16), + stoi(hexa.substr(2, 2), 0, 16), + stoi(hexa.substr(4, 2), 0, 16))) + "m"; + } else { + return pre + + to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" + + to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" + + to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m"; + } + } + } + return ""; + } + + //* Generate escape sequence for 24-bit or 256 color and return as a string + //* Args r: [0-255], g: [0-255], b: [0-255] + //* t_to_256: [true|false] convert 24bit value to 256 color value + //* depth: ["fg"|"bg"] for either a foreground color or a background color + string dec_to_color(uint r, uint g, uint b, bool t_to_256=false, string depth="fg"){ depth = (depth == "fg") ? "38" : "48"; string pre = Fx::e + depth + ";"; pre += (t_to_256) ? "5;" : "2;"; + r = (r > 255) ? 255 : r; + g = (g > 255) ? 255 : g; + b = (b > 255) ? 255 : b; + if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m"; + else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m"; + } - if (hexa.size() == 2){ - uint h_int = stoi(hexa, 0, 16); - if (t_to_256){ - return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m"; - } else { - string h_str = to_string(h_int); - return pre + h_str + ";" + h_str + ";" + h_str + "m"; - } - } - else if (hexa.size() == 6){ - if (t_to_256){ - return pre + to_string(truecolor_to_256( - stoi(hexa.substr(0, 2), 0, 16), - stoi(hexa.substr(2, 2), 0, 16), - stoi(hexa.substr(4, 2), 0, 16))) + "m"; - } else { - return pre + - to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" + - to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" + - to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m"; + //* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string + map rgb(string c_string){ + map rgb = {{"r", 0}, {"g", 0}, {"b", 0}}; + if (c_string.size() >= 14){ + c_string.erase(0, 7); + auto c_split = ssplit(c_string, ";"); + if (c_split.size() == 3){ + rgb["r"] = stoi(c_split[0]); + rgb["g"] = stoi(c_split[1]); + rgb["b"] = stoi(c_split[2].erase(c_split[2].size())); } } + return rgb; } - return ""; -} -//* Generate escape sequence for 24-bit or 256 color and return as a string -//* Args r: [0-255], g: [0-255], b: [0-255] -//* t_to_256: [true|false] convert 24bit value to 256 color value -//* depth: ["fg"|"bg"] for either a foreground color or a background color -string dec_to_color(uint r, uint g, uint b, bool t_to_256=false, string depth="fg"){ - depth = (depth == "fg") ? "38" : "48"; - string pre = Fx::e + depth + ";"; - pre += (t_to_256) ? "5;" : "2;"; - r = (r > 255) ? 255 : r; - g = (g > 255) ? 255 : g; - b = (b > 255) ? 255 : b; - if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m"; - else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m"; -} + namespace { + map color; + map> gradient; -//* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string -map c_to_rgb(string c_string){ - map rgb = {{"r", 0}, {"g", 0}, {"b", 0}}; - if (c_string.size() >= 14){ - c_string.erase(0, 7); - auto c_split = ssplit(c_string, ";"); - if (c_split.size() == 3){ - rgb["r"] = stoi(c_split[0]); - rgb["g"] = stoi(c_split[1]); - rgb["b"] = stoi(c_split[2].erase(c_split[2].size())); - } - } - return rgb; -} - -//? --------------------------------------------------- CLASSES ----------------------------------------------------- - -class C_Theme { - map c; - map> g; - - map generate(map& source){ - map out; - vector t_rgb; - string depth; - for (auto& item : Global::Default_theme) { - depth = (item.first.ends_with("bg")) ? "bg" : "fg"; - if (source.contains(item.first)) { - if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !State::truecolor, depth); - else { - t_rgb = ssplit(source.at(item.first), " "); - out[item.first] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !State::truecolor, depth); + //* Generate the theme + map generate(map& source){ + map out; + vector t_rgb; + string depth; + for (auto& item : Global::Default_theme) { + depth = (item.first.ends_with("bg")) ? "bg" : "fg"; + if (source.contains(item.first)) { + if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !Config::getB("truecolor"), depth); + else { + t_rgb = ssplit(source.at(item.first), " "); + out[item.first] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !Config::getB("truecolor"), depth); + } } + else out[item.first] = ""; + if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !Config::getB("truecolor"), depth); } - else out[item.first] = ""; - if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !State::truecolor, depth); + return out; } - return out; } -public: - //* Change to theme - void change(map source){ - c = generate(source); - State::fg = c.at("main_fg"); - State::bg = c.at("main_bg"); - Fx::reset = Fx::reset_base + State::fg + State::bg; - } - - //* Generate theme from map, default to DEFAULT_THEME on missing or malformatted values - C_Theme(map source){ - change(source); + //* Set current theme using map + void set(map source){ + color = generate(source); + Term::fg = color.at("main_fg"); + Term::bg = color.at("main_bg"); + Fx::reset = Fx::reset_base + Term::fg + Term::bg; } //* Return escape code for color - auto operator()(string name){ - return c.at(name); + auto c(string name){ + return color.at(name); } //* Return vector of escape codes for color gradient - auto gradient(string name){ - return g.at(name); - } - - //* Return map of decimal int's (r, g, b) for color - auto rgb(string name){ - return c_to_rgb(c.at(name)); + auto g(string name){ + return gradient.at(name); } }; diff --git a/src/btop_tools.h b/src/btop_tools.h index 6623e61..f1c99c6 100644 --- a/src/btop_tools.h +++ b/src/btop_tools.h @@ -27,6 +27,7 @@ tab-size = 4 #include #include #include +#include #include #include @@ -42,7 +43,7 @@ using namespace std; //* Collection of escape codes for text style and formatting namespace Fx { //* Escape sequence start - const string e = "\033["; + const string e = "\x1b["; //* Bold on const string b = e + "1m"; @@ -67,6 +68,17 @@ namespace Fx { //* Reset text effects and restore default foregrund and background color < Changed by C_Theme string reset = reset_base; + + //* Regex for matching color, style and curse move escape sequences + const regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}"); + + //* Regex for matching only color and style escape sequences + const regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}"); + + //* Return a string with all colors and text styling removed + string uncolor(string& s){ + return regex_replace(s, color_regex, ""); + } }; //* Collection of escape codes and functions for cursor manipulation @@ -93,12 +105,121 @@ namespace Mv { const string restore = Fx::e + "u"; }; +//* Collection of escape codes and functions for terminal manipulation +namespace Term { + + bool initialized = false; + bool resized = false; + uint width = 0; + uint height = 0; + string fg, bg; + + namespace { + struct termios initial_settings; + + //* Toggle terminal input echo + bool echo(bool on=true){ + struct termios settings; + if (tcgetattr(STDIN_FILENO, &settings)) return false; + if (on) settings.c_lflag |= ECHO; + else settings.c_lflag &= ~(ECHO); + return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &settings); + } + + //* Refresh variables holding current terminal width and height and return true if resized + bool refresh(){ + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + resized = (width != w.ws_col || height != w.ws_row) ? true : false; + width = w.ws_col; + height = w.ws_row; + return resized; + } + }; + + + //* Hide terminal cursor + const string hide_cursor = Fx::e + "?25l"; + + //* Show terminal cursor + const string show_cursor = Fx::e + "?25h"; + + //* Switch to alternate screen + const string alt_screen = Fx::e + "?1049h"; + + //* Switch to normal screen + const string normal_screen = Fx::e + "?1049l"; + + //* Clear screen and set cursor to position 0,0 + const string clear = Fx::e + "2J" + Fx::e + "0;0f"; + + //* Clear from cursor to end of screen + const string clear_end = Fx::e + "0J"; + + //* Clear from cursor to beginning of screen + const string clear_begin = Fx::e + "1J"; + + //* Enable reporting of mouse position on click and release + const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h"; + + //* Disable mouse reporting + const string mouse_off = Fx::e + "?1002l"; + + //* Enable reporting of mouse position at any movement + const string mouse_direct_on = Fx::e + "?1003h"; + + //* Disable direct mouse reporting + const string mouse_direct_off = Fx::e + "?1003l"; + + //* Toggle need for return key when reading input + bool linebuffered(bool on=true){ + struct termios settings; + if (tcgetattr(STDIN_FILENO, &settings)) return false; + if (on) settings.c_lflag |= ICANON; + else settings.c_lflag &= ~(ICANON); + if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false; + if (on) setlinebuf(stdin); + else setbuf(stdin, NULL); + return true; + } + + + + //* Check for a valid tty, save terminal options and set new options + bool init(){ + if (!initialized){ + initialized = (bool)isatty(STDIN_FILENO); + if (initialized) { + initialized = (0 == tcgetattr(STDIN_FILENO, &initial_settings)); + cin.sync_with_stdio(false); + cin.tie(NULL); + echo(false); + linebuffered(false); + refresh(); + resized = false; + } + } + return initialized; + } + + //* Restore terminal options + void restore(){ + if (initialized) { + echo(true); + linebuffered(true); + tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings); + initialized = false; + } + } +}; + //? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- -//* Return number of UTF8 characters in a string -inline size_t ulen(string s){ +//* Return number of UTF8 characters in a string with option to disregard escape sequences +inline size_t ulen(string s, bool escape=false){ + if (escape) s = regex_replace(s, Fx::escape_regex, ""); return std::count_if(s.begin(), s.end(), - [](char& c) { return (c & 0xC0) != 0x80; } ); + [](char c) { return (static_cast(c) & 0xC0) != 0x80; } ); } //* Return current time since epoch in milliseconds @@ -157,38 +278,40 @@ void sleep_ms(uint ms) { } //* Left justify string if is greater than length, limit return size to by default -string ljustify(string str, size_t x, bool utf=false, bool lim=true){ - if (!utf) { - if (lim && str.size() > x) str.resize(x); - return str + string(max((int)(x - str.size()), 0), ' '); - } else { - if (lim && ulen(str) > x) { +string ljustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){ + if (utf || escape) { + if (!escape && lim && ulen(str) > x) { auto i = str.size(); while (ulen(str) > x) str.resize(--i); } - return str + string(max((int)(x - ulen(str)), 0), ' '); + return str + string(max((int)(x - ulen(str, escape)), 0), ' '); + } + else { + if (lim && str.size() > x) str.resize(x); + return str + string(max((int)(x - str.size()), 0), ' '); } } //* Right justify string if is greater than length, limit return size to by default -string rjustify(string str, size_t x, bool utf=false, bool lim=true){ - if (!utf) { - if (lim && str.size() > x) str.resize(x); - return string(max((int)(x - str.size()), 0), ' ') + str; - } else { - if (lim && ulen(str) > x) { +string rjustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){ + if (utf || escape) { + if (!escape && lim && ulen(str) > x) { auto i = str.size(); while (ulen(str) > x) str.resize(--i); } - return string(max((int)(x - ulen(str)), 0), ' ') + str; + return string(max((int)(x - ulen(str, escape)), 0), ' ') + str; + } + else { + if (lim && str.size() > x) str.resize(x); + return string(max((int)(x - str.size()), 0), ' ') + str; } } //* Trim trailing characters if utf8 string length is greatear than string uresize(string str, size_t x){ - auto i = str.size(); - if (i < 1 || x < 1) return str; - while (ulen(str) > x) str.resize(--i); + // auto i = str.size(); + if (str.empty()) return str; + while (ulen(str) > x) str.pop_back(); return str; } @@ -269,104 +392,7 @@ string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool //? --------------------------------------------------- CLASSES ----------------------------------------------------- -//* Collection of escape codes and functions for terminal manipulation -class C_Term { - struct termios initial_settings; -public: - bool initialized = false; - bool resized = false; - uint width = 0; - uint height = 0; - //* Hide terminal cursor - const string hide_cursor = Fx::e + "?25l"; - - //* Show terminal cursor - const string show_cursor = Fx::e + "?25h"; - - //* Switch to alternate screen - const string alt_screen = Fx::e + "?1049h"; - - //* Switch to normal screen - const string normal_screen = Fx::e + "?1049l"; - - //* Clear screen and set cursor to position 0,0 - const string clear = Fx::e + "2J" + Fx::e + "0;0f"; - - //* Enable reporting of mouse position on click and release - const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h"; - - //* Disable mouse reporting - const string mouse_off = Fx::e + "?1002l"; - - //* Enable reporting of mouse position at any movement - const string mouse_direct_on = Fx::e + "?1003h"; - - //* Disable direct mouse reporting - const string mouse_direct_off = Fx::e + "?1003l"; - - //* Toggle need for return key when reading input - bool linebuffered(bool on=true){ - struct termios settings; - if (tcgetattr(STDIN_FILENO, &settings)) return false; - if (on) settings.c_lflag |= ICANON; - else settings.c_lflag &= ~(ICANON); - if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false; - if (on) setlinebuf(stdin); - else setbuf(stdin, NULL); - return true; - } - - //* Toggle terminal input echo - bool echo(bool on=true){ - struct termios settings; - if (tcgetattr(STDIN_FILENO, &settings)) return false; - if (on) settings.c_lflag |= ECHO; - else settings.c_lflag &= ~(ECHO); - return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &settings); - } - - //* Refresh variables holding current terminal width and height and return true if resized - bool refresh(){ - struct winsize w; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - resized = (width != w.ws_col || height != w.ws_row) ? true : false; - State::width = width = w.ws_col; - State::height = height = w.ws_row; - return resized; - } - - //* Check for a valid tty, save terminal options and set new options - bool init(){ - if (!initialized){ - initialized = (bool)isatty(STDIN_FILENO); - if (initialized) { - initialized = (0 == tcgetattr(STDIN_FILENO, &initial_settings)); - cin.sync_with_stdio(false); - cin.tie(NULL); - echo(false); - linebuffered(false); - } - } - return initialized; - } - - //* Restore terminal options - void restore(){ - if (initialized) { - echo(true); - linebuffered(true); - tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings); - initialized = false; - } - } - - C_Term() { - init(); - refresh(); - resized = false; - } -}; //? --------------------------------------------------- STRUCTS -------------------------------------------------------