diff --git a/btop.cpp b/btop.cpp index 522b1d9..44e76e1 100644 --- a/btop.cpp +++ b/btop.cpp @@ -22,12 +22,14 @@ tab-size = 4 #include #include #include +#include #include #include #include #include #include +#include #if defined(__linux__) #include @@ -43,8 +45,8 @@ tab-size = 4 #endif #endif -namespace fs = std::filesystem; -using namespace std; +using std::string, std::vector, std::map, std::atomic, std::endl, std::cout, std::views::iota; +using namespace Tools; //? ------------------------------------------------- GLOBALS --------------------------------------------------------- @@ -88,7 +90,7 @@ void argumentParser(int argc, char **argv){ } //* Generate the btop++ banner -auto create_banner(){ +auto createBanner(){ struct out_vals { uint w; string s; @@ -137,6 +139,8 @@ string my_worker(int x){ //? --------------------------------------------- Main starts here! --------------------------------------------------- int main(int argc, char **argv){ + using namespace std; + //? Init cout.setf(std::ios::boolalpha); @@ -161,11 +165,13 @@ int main(int argc, char **argv){ //? Read config file if present Config::load("____"); + auto thts = time_ms(); + //? Generate the theme Theme::set(Global::Default_theme); //? Create the btop++ banner - auto [banner_width, banner] = create_banner(); + auto [banner_width, banner] = createBanner(); Global::banner_width = move(banner_width); Global::banner = move(banner); @@ -186,31 +192,43 @@ int main(int argc, char **argv){ cout << string(Term::width - 1, '-') << endl; - //* Test MENUS - // for (auto& outer : Global::Menus){ - // for (auto& inner : outer.second){ - // for (auto& item : inner.second){ - // cout << item << endl; - // } - // } - // } + //* Test boxes + if (true){ + cout << Box::draw(Box::Conf(10, 5, 50, 10, Theme::c("title"), "testing", "testagain", true, 7)) << Mv::d(12) << endl; + exit(0); + } - - // cout << Config(Bool, "truecolor") << endl; - // cout << Config(Int, "tree_depth") << endl; - // cout << Config(String, "color_theme") << endl; - //* Test theme - int i = 0; - if (tests>0) for(auto& item : Global::Default_theme) { - 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; + if (false) { + cout << "Theme generation took " << time_ms() - thts << "ms" << endl; + + cout << "Colors:" << endl; + uint i = 0; + for(auto& item : Theme::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::gradients) { + cout << endl << rjust(name + ":", 10); + for (auto& color : cvec) { + cout << color << "■"; + } + + cout << Fx::reset << endl; + } + + + exit(0); } @@ -220,13 +238,13 @@ int main(int argc, char **argv){ map> runners; map outputs; - for (int i = 0; i < 10; i++){ + for (int i : iota(0, 10)){ runners[i] = async(my_worker, i); } - i = 0; + // uint i = 0; while (outputs.size() < 10){ - for (int i = 0; i < 10; i++){ + for (int i : iota(0, 10)){ if (runners[i].valid() && runners[i].wait_for(chrono::milliseconds(10)) == future_status::ready) { outputs[i] = runners[i].get(); cout << "Thread " << i << " : " << outputs[i] << endl; @@ -272,8 +290,8 @@ int main(int argc, char **argv){ 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; + cout << rjust("Pid:", 8) << " " << ljust("Program:", 16) << " " << ljust("Command:", Term::width - 69) << " Threads: " << + ljust("User:", 10) << " " << rjust("MemB", 5) << " " << rjust("Cpu%", 14) << "\n" << Mv::save << flush; while (key != "q") { timestamp = time_ms(); @@ -284,13 +302,13 @@ int main(int argc, char **argv){ 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, ' ') + + cout << Mv::restore << Mv::u(2) << Mv::r(20) << rjust("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') + "Sorting: " + Proc::sort_vector[sortint], Term::width - 22, true, filtering) << Mv::restore << flush; for (Proc::proc_info& procs : plist){ - ostring += rjustify(to_string(procs.pid), 8) + " " + ljustify(procs.name, 16) + " " + ljustify(procs.cmd, Term::width - 66, true) + " " + - rjustify(to_string(procs.threads), 5) + " " + ljustify(procs.user, 10) + " " + rjustify(floating_humanizer(procs.mem, true), 5) + string(11, ' '); - ostring += (procs.cpu_p > 100) ? rjustify(to_string(procs.cpu_p), 3) + " " : rjustify(to_string(procs.cpu_p), 4); + ostring += rjust(to_string(procs.pid), 8) + " " + ljust(procs.name, 16) + " " + ljust(procs.cmd, Term::width - 66, true) + " " + + rjust(to_string(procs.threads), 5) + " " + ljust(procs.user, 10) + " " + rjust(floating_humanizer(procs.mem, true), 5) + string(11, ' '); + ostring += (procs.cpu_p > 100) ? rjust(to_string(procs.cpu_p), 3) + " " : rjust(to_string(procs.cpu_p), 4); ostring += "\n"; if (lc++ > Term::height - 20) break; } @@ -329,19 +347,6 @@ int main(int argc, char **argv){ - if (tests>3){ - 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 = Theme::dec_to_color(100, 255, 100); - cout << "\n" << ccc << "Testing..." << endl; - } - if (tests>4){ string trim_test1 = "-*vad "; diff --git a/src/btop_config.h b/src/btop_config.h index 60f8a42..41f95f1 100644 --- a/src/btop_config.h +++ b/src/btop_config.h @@ -25,7 +25,8 @@ tab-size = 4 #include -using namespace std; +using std::string, std::vector, std::map; +using namespace Tools; //* Functions and variables for reading and writing the btop config file diff --git a/src/btop_draw.h b/src/btop_draw.h new file mode 100644 index 0000000..de2e897 --- /dev/null +++ b/src/btop_draw.h @@ -0,0 +1,90 @@ +/* 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 +#include +#include +#include + +#include +#include +#include + +#ifndef _btop_draw_included_ +#define _btop_draw_included_ 1 + +using std::string, std::vector, std::map, std::round, std::views::iota; + +namespace Box { + + struct Conf { + uint x=0, y=0; + uint width=0, height=0; + string line_color = "", title = "", title2 = ""; + bool fill = true; + uint num=0; + }; + + + string draw(Conf c){ + string out; + string lcolor = (c.line_color.empty()) ? Theme::c("div_line") : c.line_color; + string numbering = (c.num == 0) ? "" : Theme::c("hi_fg") + Symbols::superscript[c.num]; + + out = Fx::reset + lcolor; + + //* Draw horizontal lines + for (uint hpos : {c.y, c.y + c.height - 1}){ + out += Mv::to(hpos, c.x) + Symbols::h_line * (c.width - 1); + } + + //* Draw vertical lines and fill if enabled + for (uint hpos : iota(c.y + 1, c.y + c.height - 1)){ + out += Mv::to(hpos, c.x) + Symbols::v_line + + ((c.fill) ? string(c.width - 2, ' ') : Mv::r(c.width - 2)) + + Symbols::v_line; + } + + //* Draw corners + out += Mv::to(c.y, c.x) + Symbols::left_up + + Mv::to(c.y, c.x + c.width - 1) + Symbols::right_up + + Mv::to(c.y + c.height - 1, c.x) + Symbols::left_down + + Mv::to(c.y + c.height - 1, c.x + c.width - 1) + Symbols::right_down; + + //* Draw titles if defined + if (!c.title.empty()){ + out += Mv::to(c.y, c.x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + c.title + + Fx::ub + lcolor + Symbols::title_right; + } + + return out + Fx::reset + Mv::to(c.y + 1, c.x + 1); + } + +} + +namespace Proc { + + // Box::Conf box; + +} + + + +#endif \ No newline at end of file diff --git a/src/btop_globs.h b/src/btop_globs.h index 0fba894..5443367 100644 --- a/src/btop_globs.h +++ b/src/btop_globs.h @@ -24,7 +24,7 @@ tab-size = 4 #include #include -using namespace std; +using std::string, std::vector, std::map, std::atomic; namespace Global { @@ -120,4 +120,19 @@ namespace Global { } +namespace Symbols { + const string h_line = "─"; + const string v_line = "│"; + const string left_up = "┌"; + const string right_up = "┐"; + const string left_down = "└"; + const string right_down = "┘"; + const string title_left = "┤"; + const string title_right = "├"; + const string div_up = "┬"; + const string div_down = "┴"; + + const vector superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" }; +} + #endif diff --git a/src/btop_input.h b/src/btop_input.h index 5f1cc29..f712408 100644 --- a/src/btop_input.h +++ b/src/btop_input.h @@ -26,7 +26,8 @@ tab-size = 4 #include #include -using namespace std; +using std::string, std::map, std::cin; +using namespace Tools; //* Functions and variables for handling keyboard and mouse input diff --git a/src/btop_linux.h b/src/btop_linux.h index 8d5ca33..56a504b 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -21,7 +21,6 @@ tab-size = 4 #include #include -#include #include #include #include @@ -34,13 +33,15 @@ tab-size = 4 #include #include + +using std::string, std::vector, std::map, std::ifstream, std::atomic, std::numeric_limits, std::streamsize; namespace fs = std::filesystem; -using namespace std; +using namespace Tools; namespace Global { const string System = "linux"; - filesystem::path proc_path; + fs::path proc_path; } @@ -254,7 +255,7 @@ namespace Proc { // auto st = time_ms(); //* Sort processes vector - ranges::sort(procs, [&sortint, &reverse](proc_info& a, proc_info& b) + std::ranges::sort(procs, [&sortint, &reverse](proc_info& a, proc_info& b) { switch (sortint) { case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid; @@ -281,15 +282,12 @@ namespace Proc { } } - //* Clear all cached values at a regular interval to get rid of dead processes - if (++counter >= 5 || (filter.empty() && cache.size() > procs.size() + 100)) { + //* Clear dead processes from cache at a regular interval + if (++counter >= 10000 || (filter.empty() && cache.size() > procs.size() + 100)) { map r_cache; counter = 0; - for (auto& p : c_pids){ - r_cache[p] = cache[p]; - } + for (auto& p : c_pids) r_cache[p] = cache[p]; cache = move(r_cache); - } tstamp = time_ms(); diff --git a/src/btop_theme.h b/src/btop_theme.h index 64126fe..d0f4854 100644 --- a/src/btop_theme.h +++ b/src/btop_theme.h @@ -23,12 +23,14 @@ tab-size = 4 #include #include #include +#include #include #include #include -using namespace std; +using std::string, std::round, std::vector, std::map, std::stoi, std::views::iota; +using namespace Tools; namespace Theme { @@ -97,7 +99,7 @@ namespace Theme { } //* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string - map rgb(string c_string){ + map esc_to_rgb(string c_string){ map rgb = {{"r", 0}, {"g", 0}, {"b", 0}}; if (c_string.size() >= 14){ c_string.erase(0, 7); @@ -111,48 +113,122 @@ namespace Theme { return rgb; } - namespace { - map color; - map> gradient; - //* Generate the theme - map generate(map& source){ - map out; + namespace { + map colors; + map> rgbs; + map> gradients; + + //* Convert hex color to a vector of decimals + vector hex_to_dec(string hexa){ + if (hexa.size() > 1){ + hexa.erase(0, 1); + for (auto& c : hexa) if (!isxdigit(c)) return vector{-1, -1, -1}; + + if (hexa.size() == 2){ + int h_int = stoi(hexa, 0, 16); + return vector{h_int, h_int, h_int}; + } + else if (hexa.size() == 6){ + return vector{ + stoi(hexa.substr(0, 2), 0, 16), + stoi(hexa.substr(2, 2), 0, 16), + stoi(hexa.substr(4, 2), 0, 16) + }; + } + } + return vector{-1 ,-1 ,-1}; + } + + //* Generate colors and rgb decimal vectors for the theme + void generateColors(map& source){ 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); + colors.clear(); rgbs.clear(); + for (auto& [name, color] : Global::Default_theme) { + depth = (name.ends_with("bg")) ? "bg" : "fg"; + if (source.contains(name)) { + if (source.at(name)[0] == '#') { + colors[name] = hex_to_color(source.at(name), !Config::getB("truecolor"), depth); + rgbs[name] = hex_to_dec(source.at(name)); + } 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); + t_rgb = ssplit(source.at(name), " "); + colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !Config::getB("truecolor"), depth); + rgbs[name] = vector{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])}; } } - else out[item.first] = ""; - if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !Config::getB("truecolor"), depth); + else colors[name] = ""; + if (colors[name].empty()) { + colors[name] = hex_to_color(color, !Config::getB("truecolor"), depth); + rgbs[name] = vector{-1, -1, -1}; + } + } + } + + //* Generate color gradients from one, two or three colors, 101 values indexed 0-100 + void generateGradients(){ + gradients.clear(); + vector c_gradient; + string wname; + vector> rgb_vec; + map> dec_map; + int f, s, r, o; + for (auto& [name, s_vector] : rgbs){ + dec_map.clear(); c_gradient.clear(); + if (!name.ends_with("_start")) continue; + wname = rtrim(name, "_start"); + rgb_vec = {s_vector, rgbs[wname + "_mid"], rgbs[wname + "_end"]}; + + //? Only start iteration if gradient has a _end color value defined + if (rgb_vec[2][0] >= 0) { + + //? Split iteration in two passes of 50 + 51 instead of 101 if gradient has _start, _mid and _end values defined + r = (rgb_vec[1][0] >= 0) ? 50 : 100; + for (int i : iota(0, 3)){ + f = 0; s = (r == 50) ? 1 : 2; o = 0; + for (int c : iota(0, 101)){ + dec_map[c].push_back(rgb_vec[f][i] + (c - o) * (rgb_vec[s][i] - rgb_vec[f][i]) / r); + + //? Switch source vectors from _start/_mid to _mid/_end at 50 passes if _mid is defined + if (c == r) { ++f; ++s; o = 50;} + } + } + } + if (!dec_map.empty()) { + for (auto& vec : dec_map) c_gradient.push_back(dec_to_color(vec.second[0], vec.second[1], vec.second[2], !Config::getB("truecolor"))); + } else { + //? If only _start was defined create vector of 101 copies of _start color + c_gradient = vector(101, colors[name]); + } + gradients[wname] = c_gradient; } - return out; } } //* Set current theme using map void set(map source){ - color = generate(source); - Term::fg = color.at("main_fg"); - Term::bg = color.at("main_bg"); + generateColors(source); + generateGradients(); + Term::fg = colors.at("main_fg"); + Term::bg = colors.at("main_bg"); Fx::reset = Fx::reset_base + Term::fg + Term::bg; } //* Return escape code for color auto c(string name){ - return color.at(name); + return colors.at(name); } //* Return vector of escape codes for color gradient auto g(string name){ - return gradient.at(name); + return gradients.at(name); + } + + //* Return vector of red, green and blue in decimal for color + auto dec(string name){ + return rgbs.at(name); } }; diff --git a/src/btop_tools.h b/src/btop_tools.h index 3367f25..90fabfe 100644 --- a/src/btop_tools.h +++ b/src/btop_tools.h @@ -25,15 +25,15 @@ tab-size = 4 #include #include #include +#include #include #include #include #include -#include -using namespace std; +using std::string, std::vector, std::map, std::regex, std::max, std::to_string, std::cin; //? ------------------------------------------------- NAMESPACES ------------------------------------------------------ @@ -42,23 +42,29 @@ namespace Fx { //* Escape sequence start const string e = "\x1b["; - //* Bold on + //* Bold on/off const string b = e + "1m"; + const string ub = e + "22m"; - //* Dark on + //* Dark on/off const string d = e + "2m"; + const string ud = e + "22m"; - //* Italic on + //* Italic on/off const string i = e + "3m"; + const string ui = e + "23m"; - //* Underline on + //* Underline on/off const string ul = e + "4m"; + const string uul = e + "24m"; - //* Blink on + //* Blink on/off const string bl = e + "5m"; + const string ubl = e + "25m"; - //* Strike / crossed-out on + //* Strike / crossed-out on/off const string s = e + "9m"; + const string us = e + "29m"; //* Reset foreground/background color and text effects const string reset_base = e + "0m"; @@ -212,177 +218,202 @@ namespace Term { //? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- -//* 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 (static_cast(c) & 0xC0) != 0x80; } ); -} +namespace Tools { -//* Return current time since epoch in milliseconds -uint64_t time_ms(){ - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); -} + //* 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 = std::regex_replace(s, Fx::escape_regex, ""); + return std::count_if(s.begin(), s.end(), + [](char c) { return (static_cast(c) & 0xC0) != 0x80; } ); + } -//* Check if a string is a valid bool value -bool isbool(string str){ - return (str == "true") || (str == "false") || (str == "True") || (str == "False"); -} + //* Return current time since epoch in milliseconds + uint64_t time_ms(){ + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + } -//* Check if a string is a valid integer value -bool isint(string str){ - return all_of(str.begin(), str.end(), ::isdigit); -} + //* Check if a string is a valid bool value + bool isbool(string str){ + return (str == "true") || (str == "false") || (str == "True") || (str == "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){ + return all_of(str.begin(), 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 string + string ltrim(string str, string t_str = " "){ + while (str.starts_with(t_str)) str.erase(0, t_str.size()); + return str; + } -//* Left-right-trim from and return string -string trim(string str, string t_str = " "){ - return ltrim(rtrim(str, t_str), t_str); -} + //* 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; + } -//* Split at