diff --git a/btop.cpp b/btop.cpp index 21ac562..310887a 100644 --- a/btop.cpp +++ b/btop.cpp @@ -39,17 +39,15 @@ namespace Global { {"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"}, {"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"}, }; - const uint banner_width = 49; - - const std::string Version = "0.0.10"; + const std::string Version = "0.0.20"; } -#include #include #include #include #include #include +#include #if defined(__linux__) #define LINUX 1 @@ -79,6 +77,7 @@ using namespace Tools; namespace Global { string banner; + size_t banner_width = 0; fs::path self_path; @@ -113,35 +112,6 @@ void argumentParser(int argc, char **argv){ } } -//* Generate the btop++ banner -string createBanner(){ - size_t z = 0; - string b_color, bg, fg, out, oc, letter; - bool truecolor = Config::getB("truecolor"); - int bg_i; - for (auto line: Global::Banner_src) { - 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); - } - b_color = (letter == "█") ? fg : bg; - if (b_color != oc) out += b_color; - out += letter; - oc = b_color; - } - if (++z < Global::Banner_src.size()) out += Mv::l(ulen(line[1])) + 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::ui; - return out; -} - void clean_quit(int signal){ if (Global::quitting) return; if (Term::initialized) { @@ -155,15 +125,46 @@ void clean_quit(int signal){ void _exit_handler() { clean_quit(-1); } -//* 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); +//? Generate the btop++ banner +void banner_gen() { + size_t z = 0, w = 0; + string b_color, bg, fg, oc, letter; + bool truecolor = Config::getB("truecolor"); + int bg_i; + Global::banner.clear(); + Global::banner_width = 0; + for (auto line: Global::Banner_src) { + if ( (w = ulen(line[1])) > 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); + } + 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); } - return "Thread done! x=" + to_string(x); + 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){ @@ -254,7 +255,7 @@ int main(int argc, char **argv){ Theme::set(Theme::Default_theme); //? Create the btop++ banner - Global::banner = createBanner(); + banner_gen(); //* ------------------------------------------------ TESTING ------------------------------------------------------ @@ -263,7 +264,7 @@ int main(int argc, char **argv){ Global::debuginit = true; // cout << Theme("main_bg") << Term::clear << flush; - bool thread_test = false; + // bool thread_test = false; if (!Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << flush; @@ -343,53 +344,53 @@ int main(int argc, char **argv){ for (long long i = 100; i >= 0; i--) mydata.push_back(i); // mydata.push_back(0); // mydata.push_back(0); - // mydata.push_back(50); + 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::Meter kmeter {}; - 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 = "graph", .fill = false, .num = 7}) << Mv::save << flush; + // Draw::Meter kmeter {}; + // Draw::Graph kgraph2 {}; + // Draw::Graph kgraph3 {}; auto kts = time_micros(); - kgraph(Term::width, 10, "process", mydata, false, false); - kmeter(Term::width, "process"); + kgraph(Term::width - 12, 10, "cpu", mydata, 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); + // 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::save << kgraph(mydata) << "\n" << kmeter(mydata.back()) << "\n\nInit 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; + cout << Mv::restore << kgraph(mydata, true) << "\n\n" << 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); - // int x = 0q; - long long y = 0; - bool flip = false; + // long long y = 0; + // bool flip = false; list ktavg; while (true) { - // mydata.back() = std::rand() % 101; - mydata.back() = y; + 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) << "\n" << kmeter(mydata.back()) << endl; - cout << Mv::restore << kgraph(mydata) << "\n" << kgraph2(mydata) << "\n" << " "s * Term::width << Mv::l(Term::width) << kgraph3(mydata) << "\n" << kmeter(mydata.back()) << endl; + cout << Mv::restore << kgraph(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 << "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; + 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; @@ -403,28 +404,28 @@ int main(int argc, char **argv){ } - if (thread_test){ + // if (thread_test){ - unordered_flat_map> runners; - unordered_flat_map outputs; + // unordered_flat_map> runners; + // unordered_flat_map 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)){ + // 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; - } - } + // 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); - } - } + // // if (++i >= 10) i = 0; + // if (outputs.size() >= 8) Global::stop_all.store(true); + // } + // } diff --git a/src/btop_draw.h b/src/btop_draw.h index 1f782d4..7d56f11 100644 --- a/src/btop_draw.h +++ b/src/btop_draw.h @@ -71,6 +71,8 @@ namespace Symbols { namespace Draw { + using namespace Tools; + struct BoxConf { uint x=0, y=0; uint width=0, height=0; @@ -101,9 +103,9 @@ namespace Draw { //* Draw corners out += Mv::to(c.y, c.x) + Symbols::left_up + - Mv::to(c.y, c.x + c.width) + Symbols::right_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) + Symbols::right_down; + Mv::to(c.y + c.height - 1, c.x + c.width - 1) + Symbols::right_down; //* Draw titles if defined if (!c.title.empty()){ @@ -115,12 +117,12 @@ namespace Draw { Fx::ub + lcolor + Symbols::title_right; } - return out + Fx::reset + Mv::to(c.y + 1, c.x + 2); + return out + Fx::reset + Mv::to(c.y + 1, c.x + 1); } //* Class holding a percentage meter class Meter { - string out, color_gradient; + string color_gradient; int width = 0; bool invert = false; vector cache; @@ -130,20 +132,18 @@ namespace Draw { this->width = width; this->color_gradient = color_gradient; this->invert = invert; - out.clear(); cache.clear(); cache.insert(cache.begin(), 101, ""); } //* Return a string representation of the meter with given value string operator()(int value) { - if (width < 1) return out; + if (width < 1) return ""; value = clamp(value, 0, 100); - if (!cache.at(value).empty()) return out = cache.at(value); - out.clear(); - int y; + if (!cache.at(value).empty()) return cache.at(value); + string& out = cache.at(value); for (int i : iota(1, width + 1)) { - y = round((double)i * 100.0 / width); + int y = round((double)i * 100.0 / width); if (value >= y) out += Theme::g(color_gradient)[invert ? 100 - y : y] + Symbols::meter; else { @@ -152,28 +152,25 @@ namespace Draw { } } out += Fx::reset; - return cache.at(value) = out; - } - - string operator()() { return out; } + }; //* Class holding a graph class Graph { string out, color_gradient; - int width = 0, height = 0, lowest = 0; + int width = 0, height = 0; long long last = 0, max_value = 0, offset = 0; bool current = true, no_zero = false, invert = false; unordered_flat_map> graphs = { {true, {}}, {false, {}}}; - vector graph_symbol; //* Create two representations of the graph to switch between to represent two values for each braille character void _create(const vector& data, int data_offset) { const bool mult = (data.size() - data_offset > 1); if (mult && (data.size() - data_offset) % 2 != 0) data_offset--; + auto& graph_symbol = (invert) ? Symbols::graph_down : Symbols::graph_up; array result; const float mod = (height == 1) ? 0.3 : 0.1; long long data_value = 0; @@ -183,10 +180,10 @@ namespace Draw { } //? Horizontal iteration over values in - for (int i = data_offset; i < (int)data.size(); i++) { + for (int i : iota(data_offset, (int)data.size())) { + current = !current; if (i == -1) { data_value = 0; last = 0; } else data_value = data[i]; - if (mult) current = !current; if (max_value > 0) data_value = clamp((data_value + offset) * 100 / max_value, 0ll, 100ll); //? Vertical iteration over height of graph for (int horizon : iota(0, height)){ @@ -197,7 +194,7 @@ namespace Draw { for (auto value : {last, data_value}) { if (value >= cur_high) result[ai] = 4; - else if (value < cur_low) + else if (value <= cur_low) result[ai] = 0; else { result[ai] = round((float)(value - cur_low) * 4 / (cur_high - cur_low) + mod); @@ -233,40 +230,34 @@ namespace Draw { this->invert = invert; this->offset = offset; this->no_zero = no_zero; this->max_value = max_value; this->color_gradient = color_gradient; - // if (height == 1) graph_symbol = (invert) ? Symbols::graph_down_small : Symbols::graph_up_small; - graph_symbol = (invert) ? Symbols::graph_down : Symbols::graph_up; - if (no_zero) lowest = 1; - // current = true; int value_width = ceil((float)data.size() / 2); int data_offset = 0; if (value_width > width) data_offset = data.size() - width * 2; - //? Populate the two switching graph vectors and fill empty space if width > data size - for (int i : iota(0, height)) { - (void) i; - graphs[true].push_back((value_width < width) ? graph_symbol[0.0] * (width - value_width) : ""); - graphs[false].push_back((value_width < width) ? graph_symbol[0.0] * (width - value_width) : ""); + //? Populate the two switching graph vectors and fill empty space if data size < width + auto& graph_symbol = (invert) ? Symbols::graph_down : Symbols::graph_up; + for (int i : iota(0, height * 2)) { + graphs[(i % 2 != 0)].push_back((value_width < width) ? ((height == 1) ? Mv::r(1) : graph_symbol[0]) * (width - value_width) : ""); } if (data.size() == 0) return; this->_create(data, data_offset); } //* Add last value from back of and return string representation of graph - string operator()(const vector& data, bool data_same = false) { + string& operator()(const vector& data, bool data_same = false) { if (data_same) return out; - current = !current; //? Make room for new characters on graph for (int i : iota(0, height)) { - if (graphs[current][i].starts_with(Fx::e)) graphs[current][i].erase(0, 4); - else graphs[current][i].erase(0, 3); + if (graphs[(!current)][i].starts_with(Fx::e)) graphs[current][i].erase(0, 4); + else graphs[(!current)][i].erase(0, 3); } this->_create(data, (int)data.size() - 1); return out; } //* Return string representation of graph - string operator()() { + string& operator()() { return out; } }; diff --git a/src/btop_input.h b/src/btop_input.h index a73c25d..2dc98f4 100644 --- a/src/btop_input.h +++ b/src/btop_input.h @@ -81,11 +81,10 @@ namespace Input { //* 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 (timer * 10 <= timeout) { + int timer = 0; + while (timer++ * 10 <= timeout) { if (cin.rdbuf()->in_avail() > 0) return true; - sleep_ms(10); - ++timer; + sleep_ms( (timer * 10 <= timeout) ? 10 : timeout % 10); } return false; } diff --git a/src/btop_linux.h b/src/btop_linux.h index 8fcbdea..2854b9b 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -36,7 +36,6 @@ tab-size = 4 #include #include -#include #include diff --git a/src/btop_globs.h b/src/btop_menu.h similarity index 94% rename from src/btop_globs.h rename to src/btop_menu.h index ea1efd8..d11177a 100644 --- a/src/btop_globs.h +++ b/src/btop_menu.h @@ -16,8 +16,8 @@ indent = tab tab-size = 4 */ -#ifndef _btop_globs_included_ -#define _btop_globs_included_ 1 +#ifndef _btop_menu_included_ +#define _btop_menu_included_ 1 #include #include @@ -27,11 +27,9 @@ tab-size = 4 using std::string, std::vector, std::unordered_map, std::array, std::atomic, robin_hood::unordered_flat_map; -namespace Global { - - atomic stop_all(false); - +namespace Menu { + atomic active(false); const unordered_flat_map>> Menus = { { "options", { diff --git a/src/btop_theme.h b/src/btop_theme.h index 9573c2d..c354cc0 100644 --- a/src/btop_theme.h +++ b/src/btop_theme.h @@ -27,7 +27,6 @@ tab-size = 4 #include #include -#include #include #include diff --git a/src/btop_tools.h b/src/btop_tools.h index cc91993..5d9eb59 100644 --- a/src/btop_tools.h +++ b/src/btop_tools.h @@ -38,9 +38,7 @@ tab-size = 4 #include #include -#include - -using std::string, std::vector, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map; +using std::string, std::vector, std::array, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map; namespace fs = std::filesystem; //? ------------------------------------------------- NAMESPACES ------------------------------------------------------ @@ -95,19 +93,19 @@ namespace Fx { //* Collection of escape codes and functions for cursor manipulation namespace Mv { //* Move cursor to , - inline string to(int line, int col){ return Fx::e + to_string(line) + ";" + to_string(col) + "f";} + const string to(int line, int col){ return Fx::e + to_string(line) + ";" + to_string(col) + "f";} //* Move cursor right columns - inline string r(int x){ return Fx::e + to_string(x) + "C";} + const string r(int x){ return Fx::e + to_string(x) + "C";} //* Move cursor left columns - inline string l(int x){ return Fx::e + to_string(x) + "D";} + const string l(int x){ return Fx::e + to_string(x) + "D";} //* Move cursor up x lines - inline string u(int x){ return Fx::e + to_string(x) + "A";} + const string u(int x){ return Fx::e + to_string(x) + "A";} //* Move cursor down x lines - inline string d(int x) { return Fx::e + to_string(x) + "B";} + const string d(int x) { return Fx::e + to_string(x) + "B";} //* Save cursor position const string save = Fx::e + "s"; @@ -229,51 +227,51 @@ namespace Term { namespace Tools { //* Return number of UTF8 characters in a string with option to disregard escape sequences - inline size_t ulen(string s, bool escape=false){ + 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; } ); } //* Return current time since epoch in seconds - inline uint64_t time_s(){ + uint64_t time_s(){ return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } //* Return current time since epoch in milliseconds - inline uint64_t time_ms(){ + uint64_t time_ms(){ return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } - //* Return current time since epoch in milliseconds - inline uint64_t time_micros(){ + //* Return current time since epoch in microseconds + uint64_t time_micros(){ return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } //* Check if a string is a valid bool value - inline bool isbool(string& str){ + bool isbool(string& str){ return (str == "true") || (str == "false") || (str == "True") || (str == "False"); } //* Check if a string is a valid integer value - inline bool isint(string& str){ + bool isint(string& str){ return all_of(str.begin(), str.end(), ::isdigit); } //* Left-trim from and return string - inline string ltrim(string str, string t_str = " "){ + string ltrim(string str, string t_str = " "){ while (str.starts_with(t_str)) str.erase(0, t_str.size()); return str; } //* Right-trim from and return string - inline string rtrim(string str, string t_str = " "){ + string rtrim(string str, string t_str = " "){ while (str.ends_with(t_str)) str.resize(str.size() - t_str.size()); return str; } //* Left-right-trim from and return string - inline string trim(string str, string t_str = " "){ + string trim(string str, string t_str = " "){ return ltrim(rtrim(str, t_str), t_str); } @@ -452,8 +450,6 @@ namespace Tools { namespace Logger { namespace { std::atomic busy (false); - fs::path logfile; - uint loglevel = 2; bool first = true; string tdf = "%Y/%m/%d (%T) | "; unordered_flat_map log_levels = { @@ -465,19 +461,25 @@ namespace Logger { }; } + fs::path logfile; + uint loglevel = 2; + void log_write(uint level, string& msg){ - if (logfile.empty() || loglevel < level) return; + if (loglevel < level || logfile.empty()) return; busy.wait(true); busy.store(true); - if (fs::exists(logfile) && fs::file_size(logfile) > 1024 << 10) { + std::error_code ec; + if (fs::file_size(logfile, ec) > 1024 << 10) { auto old_log = logfile; old_log += ".1"; - if (fs::exists(old_log)) fs::remove(old_log); - fs::rename(logfile, old_log); + if (fs::exists(old_log)) fs::remove(old_log, ec); + fs::rename(logfile, old_log, ec); + } + if (!ec) { + std::ofstream lwrite(logfile, std::ios::app); + if (first) { first = false; lwrite << "\n" << Tools::strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";} + lwrite << Tools::strf_time(tdf) << log_levels[level] << ": " << msg << "\n"; + lwrite.close(); } - std::ofstream lwrite(logfile, std::ios::app); - if (first) { first = false; lwrite << "\n" << Tools::strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";} - lwrite << Tools::strf_time(tdf) << log_levels[level] << ": " << msg << "\n"; - lwrite.close(); busy.store(false); }