Proc::draw() and SIGWINCH resizing handler

This commit is contained in:
aristocratos 2021-07-15 23:49:16 +02:00
parent e3b297e82a
commit 1121978214
8 changed files with 395 additions and 425 deletions

View file

@ -88,6 +88,7 @@ namespace Global {
uint64_t start_time;
atomic<bool> resized (false);
atomic<bool> quitting (false);
bool arg_tty = false;
@ -137,19 +138,51 @@ void argumentParser(int argc, char **argv){
}
}
void clean_quit(int sig){
//* Handler for SIGWINCH and general resizing events
void _resize(bool force=false){
if (Term::refresh(false) or force) {
Global::resized = true;
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
Term::refresh();
}
else return;
while (true) {
sleep_ms(100);
if (not Term::refresh()) break;
}
Input::interrupt = true;
Draw::calcSizes();
}
//* Exit handler; stops threads, restores terminal and saves config changes
void clean_quit(int sig=-1){
if (Global::quitting) return;
Global::quitting = true;
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
if (Term::initialized) {
Term::restore();
if (not 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(){
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
void _sleep(){
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
if (Term::initialized) {
Term::restore();
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
@ -157,12 +190,16 @@ void sleep_now(){
std::raise(SIGSTOP);
}
void resume_now(){
//* Handler for SIGCONT; re-initialize terminal and force a resize event
void _resume(){
Term::init();
if (not Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << flush;
_resize(true);
}
void _exit_handler() { clean_quit(-1); }
void _exit_handler() {
clean_quit(-1);
}
void _signal_handler(int sig) {
switch (sig) {
@ -170,10 +207,13 @@ void _signal_handler(int sig) {
clean_quit(0);
break;
case SIGTSTP:
sleep_now();
_sleep();
break;
case SIGCONT:
resume_now();
_resume();
break;
case SIGWINCH:
_resize();
break;
}
}
@ -221,6 +261,7 @@ void banner_gen() {
namespace Runner {
atomic<bool> active (false);
atomic<bool> stop (false);
}
@ -239,6 +280,7 @@ int main(int argc, char **argv){
std::signal(SIGINT, _signal_handler);
std::signal(SIGTSTP, _signal_handler);
std::signal(SIGCONT, _signal_handler);
std::signal(SIGWINCH, _signal_handler);
//? Linux init
#if defined(LINUX)
@ -344,28 +386,15 @@ int main(int argc, char **argv){
//* ------------------------------------------------ TESTING ------------------------------------------------------
Global::debuginit = true;
Global::debuginit = false;
Draw::calcSizes();
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
// cout << Theme("main_bg") << Term::clear << flush;
// bool thread_test = false;
if (not Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << Term::clear << endl;
// cout << Theme::c("main_fg") << Theme::c("main_bg") << Term::clear << endl;
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
// 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;
if (false) {
Draw::calcSizes();
@ -374,16 +403,22 @@ int main(int argc, char **argv){
exit(0);
}
// if (true) {
// cout << Term::clear << flush;
// unordered_flat_map<string, string(*)(string)> korvs = {
// {"korv1", korv1},
// {"korv2", korv2},
// };
// // auto hej = korv1;
// cout << korvs["korv1"]("hejsan") << endl;
// cout << korvs["korv2"]("hejsan igen") << endl;
// exit(0);
// }
//* Test theme
if (false) {
// cout << Theme::theme_dir << ", " << Theme::user_theme_dir << endl;
// for (auto& s : Theme::themes) {
// cout << s << endl;
// }
// exit(0);
string key;
bool no_redraw = false;
auto theme_index = v_index(Theme::themes, Config::getS("color_theme"));
@ -397,7 +432,6 @@ int main(int argc, char **argv){
size_t 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;
@ -440,46 +474,14 @@ int main(int argc, char **argv){
exit(0);
}
if (false) {
string first = "Test number 1 OF 45, or?";
cout << str_to_lower(first) << endl;
cout << str_to_upper(first) << 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) {
deque<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 {};
@ -487,54 +489,28 @@ int main(int argc, char **argv){
cout << Draw::createBox(5, 10, Term::width - 10, 12, Theme::c("proc_box"), false, "braille", "", 1) << Mv::save;
cout << Draw::createBox(5, 23, Term::width - 10, 12, Theme::c("proc_box"), false, "block", "", 2);
cout << Draw::createBox(5, 36, Term::width - 10, 12, Theme::c("proc_box"), false, "tty", "", 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);
kgraph(Term::width - 13, 10, "cpu", mydata, "braille", false, false);
kgraph2(Term::width - 13, 10, "cpu", mydata, "block", false, false);
kgraph3(Term::width - 13, 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::restore << Mv::d(26) << kgraph3(mydata, true) << '\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);
// 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)
cout << Term::sync_start << 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;
<< Term::sync_end << 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 or y == 0) flip = not flip;
if (Input::poll()) {
if (Input::get() == "space") Input::wait();
else break;
@ -565,34 +541,6 @@ int main(int argc, char **argv){
}
// 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() and 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
@ -616,13 +564,10 @@ int main(int argc, char **argv){
greyscale.push_back(Theme::dec_to_color(xc, xc, xc));
}
// string pbox = Draw::createBox(1, 10, Term::width, Term::height - 18, Theme::c("proc_box"), false, "testbox", "below", 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;
Config::lock();
try {
plist = Proc::collect();
}
@ -633,51 +578,21 @@ int main(int argc, char **argv){
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 : " "))
// + trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: "
// + string(Config::getS("proc_sorting")), Term::width - 23 - ulen(filter)))
// + Mv::restore;
// for (auto& p : plist){
// if (not 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 or p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4))
// + "\n";
// }
// else {
// string cmd_cond;
// if (not 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) + (Config::getB("tty_mode") ? "" : greyscale[lc]) + ljust(p.prefix + to_string(p.pid) + " " + p.name + " "
// + (not cmd_cond.empty() and 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 or p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4))
// + "\n";
// }
// if (lc++ > Term::height - 23) break;
// }
// while (lc++ < Term::height - 21) ostring += Mv::r(1) + string(Term::width - 2, ' ') + "\n";
ostring = Proc::draw(plist);
Config::unlock();
avgtimes.push_front(timestamp);
if (avgtimes.size() > 30) avgtimes.pop_back();
cout << ostring << Fx::reset << Mv::to(2, 2) << endl;
cout << Term::sync_start << ostring << Fx::reset << Mv::to(2, 2) << '\n';
cout << " Details for " << Proc::detailed.entry.name << " (" << Proc::detailed.entry.pid << ") Status: " << Proc::detailed.status << " Elapsed: " << Proc::detailed.elapsed
<< " Mem: " << floating_humanizer(Proc::detailed.entry.mem) << " "
<< "\n Parent: " << Proc::detailed.parent << " IO in/out: " << Proc::detailed.io_read << "/" << Proc::detailed.io_write << " " << endl;
<< "\n Parent: " << Proc::detailed.parent << " IO in/out: " << Proc::detailed.io_read << "/" << Proc::detailed.io_write << " ";
cout << Mv::to(4, 2) << "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 << ". Number in vector: " << plist.size() << ". Run count: " << ++rcount << ". Time: " << strf_time("%X ") << endl;
" samples. Drawing took: " << time_micros() - timestamp2 << " μs.\nNumber of processes: " << Proc::numpids << ". Number in vector: " << plist.size() << ". Run count: " << ++rcount << ". Time: " << strf_time("%X ") << Term::sync_end << flush;
while (time_ms() < tsl) {
while (time_ms() < tsl and not Global::resized) {
if (Input::poll(tsl - time_ms())) key = Input::get();
else { key.clear() ; continue; }
if (Config::getB("proc_filtering")) {
@ -707,20 +622,23 @@ int main(int argc, char **argv){
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 if (key == "d" ) {
Config::flip("show_detailed");
if (Config::getB("show_detailed")) {
Config::set("detailed_pid", (int)plist.at(0).pid);
}
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
Proc::selection(key);
cout << Proc::draw(plist) << flush;
continue;
}
else continue;
Proc::redraw = true;
break;
}
if (Global::resized) {
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
Global::resized = false;
}
cout << Mv::to(Term::height - 3, 1) << flush;
}
// cout << "Found " << plist.size() << " pids\n" << endl;
//*-----<<<<<

View file

@ -221,6 +221,9 @@ namespace Config {
{"update_ms", 2000},
{"proc_update_mult", 2},
{"detailed_pid", 0},
{"selected_pid", 0},
{"proc_start", 0},
{"proc_selected", 0},
};
unordered_flat_map<string, int> intsTmp;
@ -298,6 +301,12 @@ namespace Config {
}
boolsTmp.clear();
if (Proc::shown) {
ints.at("selected_pid") = Proc::selected_pid;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
}
locked = false;
writelock = false;
}

View file

@ -302,18 +302,53 @@ namespace Proc {
int width_p = 55, height_p = 68;
int min_w = 44, min_h = 16;
int x, y, width, height;
int select_max;
int start, selected, select_max;
bool shown = true, redraw = true;
int selected_pid = 0;
string box;
void selection(string cmd_key) {
auto start = Config::getI("proc_start");
auto selected = Config::getI("proc_selected");
if (cmd_key == "up" and selected > 0) {
if (start > 0 and selected == 1) start--;
else selected--;
}
else if (cmd_key == "down") {
if (start < numpids - select_max and selected == select_max) start++;
else selected++;
}
else if (cmd_key == "page_up") {
if (start == 0) selected = 0;
else start = max(0, start - (height - 3));
}
else if (cmd_key == "page_down") {
if (start >= numpids - select_max) selected = select_max;
else start = clamp(start + select_max, 0, max(0, numpids - select_max));
}
else if (cmd_key == "home") {
start = 0;
selected = (selected > 0) ? 1 : 0;
}
else if (cmd_key == "end") {
start = max(0, numpids - select_max);
selected = select_max;
}
Config::set("proc_start", start);
Config::set("proc_selected", selected);
}
string draw(vector<proc_info> plist){
auto& filter = Config::getS("proc_filter");
auto& filtering = Config::getB("proc_filtering");
auto& proc_tree = Config::getB("proc_tree");
bool show_detailed = (Config::getB("show_detailed") and Proc::detailed.last_pid == (size_t)Config::getI("detailed_pid") );
bool show_detailed = (Config::getB("show_detailed") and Proc::detailed.last_pid == (size_t)Config::getI("detailed_pid"));
bool proc_gradient = (Config::getB("proc_gradient") and not Config::getB("tty_mode"));
auto& proc_colors = Config::getB("proc_colors");
start = Config::getI("proc_start");
selected = Config::getI("proc_selected");
uint64_t total_mem = 16328872 << 10;
int y = show_detailed ? Proc::y + 9 : Proc::y;
int height = show_detailed ? Proc::height - 9 : Proc::height;
@ -322,83 +357,121 @@ namespace Proc {
if (redraw) {
redraw = false;
out = box;
select_max = height - 3;
out += Mv::to(y, x) + Mv::r(12)
+ trans("Filter: " + filter + (filtering ? Fx::bl + "" + Fx::reset : " "))
+ trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: "
+ string(Config::getS("proc_sorting")), width - 23 - ulen(filter)));
if (not proc_tree)
out += Mv::to(y+1, x+1) + Theme::c("title") + Fx::b + rjust("Pid:", 8) + " " + ljust("Program:", 16) + " "
+ ljust("Command:", width - 70) + " Threads: " + ljust("User:", 10) + " " + rjust("MemB", 5)
+ " " + rjust("Cpu%", 14) + Fx::ub;
out += Mv::to(y+1, x+1) + Theme::c("title") + Fx::b
+ rjust("Pid:", 8) + " "
+ ljust("Program:", (width < 70 ? width - 45 : 16))
+ (width >= 70 ? ljust("Command:", width - 61) : "")
+ (width >= 77 ? Mv::l(4) + "Threads: " : " Tr: ")
+ ljust("User:", 10) + " " + rjust("MemB", 5)
+ " " + rjust("Cpu%", 10) + Fx::ub;
else
out += Mv::to(y+1, x+1) + Theme::c("title") + Fx::b + ljust("Tree:", width - 44)
+ " Threads: " + ljust("User:", 10) + " " + rjust("MemB", 5)
+ " " + rjust("Cpu%", 14) + Fx::ub;
out += Mv::to(y+1, x+1) + Theme::c("title") + Fx::b + ljust("Tree:", width - 40)
+ "Threads: " + ljust("User:", 10) + " " + rjust("MemB", 5)
+ " " + rjust("Cpu%", 10) + Fx::ub;
}
//* Check bounds of current selection and view
if (start > 0 and numpids <= select_max)
start = 0;
if (start > numpids - select_max)
start = max(0, numpids - select_max);
if (selected > select_max)
selected = select_max;
if (selected > numpids)
selected = numpids;
//* Iteration over processes
int lc = 0;
for (auto& p : plist){
//? Set correct gradient colors if enabled
string c_color, m_color, t_color, g_color;
string end = proc_colors ? Theme::c("main_fg") + Fx::ub : Fx::ub;
if (proc_colors) { //! and not is_selected
array<string, 3> colors;
for (int i = 0; int v : {(int)round(p.cpu_p), (int)round(p.mem * 100 / total_mem), (int)p.threads / 3}) {
if (proc_gradient) {
int val = (min(v, 100) + 100) - lc * 100 / select_max;
if (val < 100) colors[i++] = Theme::g("proc_color")[val];
else colors[i++] = Theme::g("process")[val - 100];
}
else
colors[i++] = Theme::g("process")[min(v, 100)];
}
c_color = colors[0]; m_color = colors[1]; t_color = colors[2];
}
else
c_color = m_color = t_color = Fx::b;
if (proc_gradient) { //! and not is_selected
g_color = Theme::g("proc")[lc * 100 / select_max];
}
for (int n=0; auto& p : plist){
if (n++ < start) continue;
bool is_selected = (lc + 1 == selected);
if (is_selected) selected_pid = (int)p.pid;
string cpu_str = to_string(p.cpu_p);
if (p.cpu_p < 10 or p.cpu_p >= 100) cpu_str.resize(3);
out += Fx::reset;
//? Set correct gradient colors if enabled
string c_color, m_color, t_color, g_color, end;
if (is_selected) {
c_color = m_color = t_color = g_color = Fx::b;
end = Fx::ub;
out += Theme::c("selected_bg") + Theme::c("selected_fg") + Fx::b;
}
else {
int calc = (selected > lc) ? selected - lc : lc - selected;
if (proc_colors) {
end = Theme::c("main_fg") + Fx::ub;
array<string, 3> colors;
for (int i = 0; int v : {(int)round(p.cpu_p), (int)round(p.mem * 100 / total_mem), (int)p.threads / 3}) {
if (proc_gradient) {
int val = (min(v, 100) + 100) - calc * 100 / select_max;
if (val < 100) colors[i++] = Theme::g("proc_color")[val];
else colors[i++] = Theme::g("process")[val - 100];
}
else
colors[i++] = Theme::g("process")[min(v, 100)];
}
c_color = colors[0]; m_color = colors[1]; t_color = colors[2];
}
else {
c_color = m_color = t_color = Fx::b;
end = Fx::ub;
}
if (proc_gradient) {
g_color = Theme::g("proc")[calc * 100 / select_max];
}
}
//? Normal view line
if (not proc_tree) {
out += Mv::to(y+2+lc, x+1)
+ g_color + rjust(to_string(p.pid), 8) + " "
+ c_color + ljust(p.name, 16) + end + " "
+ g_color + ljust(p.cmd, width - 67, true) + " ";
+ g_color + rjust(to_string(p.pid), 8) + ' '
+ c_color + ljust(p.name, (width < 70 ? width - 46 : 16)) + end
+ (width >= 70 ? g_color + ljust(p.cmd, width - 62, true) : "");
}
//? Tree view line
else {
//? Add process executable name if not same as /proc/comm
string cmd_cond;
if (not p.cmd.empty()) {
cmd_cond = p.cmd.substr(0, min(p.cmd.find(' '), p.cmd.size()));
cmd_cond = cmd_cond.substr(min(cmd_cond.find_last_of('/') + 1, cmd_cond.size()));
string prefix_pid = p.prefix + to_string(p.pid);
int width_left = width - 38;
out += Mv::to(y+2+lc, x+1) + g_color + uresize(prefix_pid, width_left) + ' ';
width_left -= ulen(prefix_pid);
if (width_left > 0) {
out += c_color + uresize(p.name, width_left - 1) + end + ' ';
width_left -= (ulen(p.name) + 1);
}
string pid_str = to_string(p.pid);
int size_justify = ulen(p.prefix) + pid_str.size() + p.name.size() + 43;
out += Mv::to(y+2+lc, x+1)
+ g_color + p.prefix + pid_str + " "
+ c_color + p.name + end + " "
+ g_color + ljust((not cmd_cond.empty() and cmd_cond != p.name ? "(" + cmd_cond + ")" : ""), width - size_justify, true) + " ";
if (width_left > 7 and not p.cmd.empty()) {
out += g_color + uresize(p.cmd, width_left - 1) + ' ';
width_left -= (ulen(p.cmd) + 1);
}
out += string(max(0, width_left), ' ');
}
//? Common end of line
out += t_color + rjust(to_string(p.threads), 5) + end + " "
+ g_color + ljust(p.user, 10) + " "
+ m_color + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ') + end
+ c_color + rjust(cpu_str, 4) + end;
string cpu_str = to_string(p.cpu_p);
if (p.cpu_p < 10 or p.cpu_p >= 100) cpu_str.resize(3);
out += t_color + rjust(to_string(p.threads), 5) + end + ' '
+ g_color + ljust(p.user, 10) + ' '
+ m_color + rjust(floating_humanizer(p.mem, true), 5) + end + ' '
+ (is_selected ? "" : Theme::c("inactive_fg")) + ""s * 5 + ' ' + end
+ c_color + rjust(cpu_str, 4) + ' ' + end;
if (lc++ > height - 5) break;
}
out += Fx::reset;
while (lc++ < height - 4) out += Mv::to(y+lc+2, x+1) + string(width - 3, ' ');
return out;
//* Current selection and number of processes
string location = to_string(start + selected) + '/' + to_string(numpids);
string loc_clear = Symbols::h_line * max(0ul, 9 - location.size());
out += Mv::to(y+height, x+width - 3 - max(9, (int)location.size())) + Theme::c("proc_box") + loc_clear
+ Symbols::title_left + Theme::c("title") + Fx::b + location + Fx::ub + Theme::c("proc_box") + Symbols::title_right;
if (selected == 0 and selected_pid != 0) selected_pid = 0;
return out + Fx::reset;
}
}
@ -417,10 +490,10 @@ namespace Draw {
Cpu::height = Mem::height = Net::height = Proc::height = 0;
Cpu::redraw = Mem::redraw = Net::redraw = Proc::redraw = true;
Cpu::shown = s_contains(boxes, "cpu") ? true : false;
Mem::shown = s_contains(boxes, "mem") ? true : false;
Net::shown = s_contains(boxes, "net") ? true : false;
Proc::shown = s_contains(boxes, "proc") ? true : false;
Cpu::shown = s_contains(boxes, "cpu");
Mem::shown = s_contains(boxes, "mem");
Net::shown = s_contains(boxes, "net");
Proc::shown = s_contains(boxes, "proc");
//* Calculate and draw cpu box outlines
if (Cpu::shown) {
@ -537,7 +610,7 @@ namespace Draw {
height = Term::height - Cpu::height;
x = Term::width - width + 1;
y = Cpu::height + 1;
select_max = height - 3;
box = createBox(x, y, width, height, Theme::c("proc_box"), true, "proc", "", 4);
}

View file

@ -39,6 +39,8 @@ namespace Symbols {
extern const string div_down;
}
//! See btop_shared.hpp for draw & misc functions for boxes
namespace Draw {
//* Create a box and return as a string
@ -83,24 +85,4 @@ namespace Draw {
//* Calculate sizes of boxes, draw outlines and save to enabled boxes namespaces
void calcSizes();
}
namespace Cpu {
}
namespace Mem {
}
namespace Net {
}
namespace Proc {
}

View file

@ -117,7 +117,9 @@ namespace Proc {
int counter = 0;
}
uint64_t old_cputimes = 0;
size_t numpids = 500;
int numpids = 0;
size_t reserve_pids = 500;
bool tree_state = false;
atomic<bool> stop (false);
atomic<bool> collecting (false);
vector<string> sort_vector = {
@ -165,8 +167,18 @@ namespace Proc {
}
}
if (not collapsed and not filtering)
if (not collapsed and not filtering) {
out_procs.push_back(cur_proc);
if (auto& cmdline = cache.at(cur_proc.pid).cmd; not cmdline.empty() and not cmdline.starts_with("(")) {
cmdline = cmdline.substr(0, std::min(cmdline.find(' '), cmdline.size()));
cmdline = cmdline.substr(std::min(cmdline.find_last_of('/') + 1, cmdline.size()));
if (cmdline == cur_proc.name)
cmdline.clear();
else
cmdline = '(' + cmdline + ')';
out_procs.back().cmd = cmdline;
}
}
int children = 0;
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
@ -180,10 +192,10 @@ namespace Proc {
}
if (collapsed or filtering) return;
if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("] "))
if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("]"))
out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ ");
out_procs.at(cur_pos).prefix = ""s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : " ├─ ");
out_procs.at(cur_pos).prefix = ""s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+]" : "[-]─") : " ├─ ");
}
//* Get detailed info for selected process
@ -286,6 +298,10 @@ namespace Proc {
auto& filter = Config::getS("proc_filter");
auto per_core = Config::getB("proc_per_core");
auto tree = Config::getB("proc_tree");
if (tree_state != tree) {
cache.clear();
tree_state = tree;
}
auto show_detailed = Config::getB("show_detailed");
size_t detailed_pid = Config::getI("detailed_pid");
ifstream pread;
@ -293,7 +309,7 @@ namespace Proc {
string short_str;
double uptime = system_uptime();
vector<proc_info> procs;
procs.reserve((numpids + 10));
procs.reserve(reserve_pids + 10);
int npids = 0;
int cmult = (per_core) ? Global::coreCount : 1;
bool got_detailed = false;
@ -437,7 +453,12 @@ namespace Proc {
}
case 20: { //? Number of threads
new_proc.threads = stoull(short_str);
next_x = (cache[new_proc.pid].cpu_s == 0) ? 22 : 24;
if (cache[new_proc.pid].cpu_s == 0) {
next_x = 22;
cache[new_proc.pid].cpu_t = cpu_t;
}
else
next_x = 24;
continue;
}
case 22: { //? Save cpu seconds to cache if missing
@ -550,8 +571,9 @@ namespace Proc {
}
old_cputimes = cputimes;
numpids = (int)procs.size();
current_procs.swap(procs);
numpids = npids;
reserve_pids = npids;
collecting = false;
return current_procs;
}

View file

@ -25,7 +25,7 @@ tab-size = 4
#include <deque>
#include <robin_hood.h>
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map;
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic;
namespace Global {
extern const string Version;
@ -35,7 +35,8 @@ namespace Global {
namespace Runner {
extern std::atomic<bool> active;
extern atomic<bool> active;
extern atomic<bool> stop;
}
@ -65,13 +66,15 @@ namespace Net {
}
namespace Proc {
extern size_t numpids;
extern std::atomic<bool> stop;
extern std::atomic<bool> collecting;
extern int numpids;
extern atomic<bool> stop;
extern atomic<bool> collecting;
extern string box;
extern bool shown, redraw;
extern int current_y, current_h, select_max;
extern atomic<int> detailed_pid;
extern int selected_pid, start, selected;
//? Contains the valid sorting options for processes
extern vector<string> sort_vector;
@ -103,8 +106,12 @@ namespace Proc {
extern detail_container detailed;
//* Collects and sorts process information from /proc, saves and returns reference to Proc::current_procs;
//* Collect and sort process information from /proc, saves and returns reference to Proc::current_procs;
vector<proc_info>& collect(bool return_last=false);
//* Update current selection and view
void selection(string cmd_key);
//* Draw contents of proc box using <plist> as data source
string draw(vector<proc_info> plist);
}

View file

@ -19,13 +19,11 @@ tab-size = 4
#include <cmath>
#include <iostream>
#include <fstream>
#include <chrono>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <utility>
#include <robin_hood.h>
#include <thread>
#include <unistd.h>
#include <termios.h>
@ -34,8 +32,7 @@ tab-size = 4
#include <btop_shared.hpp>
#include <btop_tools.hpp>
using std::string_view, std::array, std::regex, std::max, std::to_string, std::cin,
std::atomic, robin_hood::unordered_flat_map;
using std::string_view, std::array, std::regex, std::max, std::to_string, std::cin, robin_hood::unordered_flat_map;
namespace fs = std::filesystem;
namespace rng = std::ranges;
@ -62,10 +59,6 @@ namespace Fx {
const regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}");
const regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}");
string uncolor(const string& s){
return regex_replace(s, color_regex, "");
}
}
//* Collection of escape codes and functions for cursor manipulation
@ -83,10 +76,9 @@ namespace Mv {
//* Collection of escape codes and functions for terminal manipulation
namespace Term {
bool initialized = false;
bool resized = false;
int width = 0;
int height = 0;
atomic<bool> initialized = false;
atomic<int> width = 0;
atomic<int> height = 0;
string fg, bg, current_tty;
const string hide_cursor = Fx::e + "?25l";
@ -100,6 +92,8 @@ namespace Term {
const string mouse_off = Fx::e + "?1002l";
const string mouse_direct_on = Fx::e + "?1003h";
const string mouse_direct_off = Fx::e + "?1003l";
const string sync_start = Fx::e + "?2026h";
const string sync_end = Fx::e + "?2026l";
namespace {
struct termios initial_settings;
@ -126,15 +120,17 @@ namespace Term {
}
}
bool refresh(){
bool refresh(bool update){
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (width != w.ws_col or height != w.ws_row) {
width = w.ws_col;
height = w.ws_row;
resized = true;
if (update) {
width = w.ws_col;
height = w.ws_row;
}
return true;
}
return resized;
return false;
}
bool init(){
@ -148,7 +144,6 @@ namespace Term {
echo(false);
linebuffered(false);
refresh();
resized = false;
}
}
return initialized;
@ -168,13 +163,7 @@ namespace Term {
namespace Tools {
size_t ulen(string str, const bool escape){
if (escape) str = std::regex_replace(str, Fx::escape_regex, "");
return rng::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
}
string uresize(string str, const size_t len){
if (str.size() < 1) return str;
if (len < 1) return "";
for (size_t x = 0, i = 0; i < str.size(); i++) {
if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) x++;
@ -187,61 +176,19 @@ namespace Tools {
return str;
}
string str_to_upper(const string& str){
string out = str;
rng::for_each(out, [](auto& c){ c = ::toupper(c); } );
return out;
}
string str_to_lower(const string& str){
string out = str;
rng::for_each(out, [](char& c){ c = ::tolower(c); } );
return out;
}
uint64_t time_s(){
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
uint64_t time_ms(){
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
uint64_t time_micros(){
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
bool isbool(string& str){
return (str == "true") or (str == "false") or (str == "True") or (str == "False");
}
bool stobool(string& str){
return (str == "true" or str == "True");
}
bool isint(string& str){
if (str.empty()) return false;
size_t offset = (str[0] == '-' ? 1 : 0);
return all_of(str.begin() + offset, str.end(), ::isdigit);
}
string ltrim(const string& str, const string t_str){
string ltrim(const string& str, const string& t_str){
string_view str_v = str;
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
return (string)str_v;
}
string rtrim(const string& str, const string t_str){
string rtrim(const string& str, const string& t_str){
string_view str_v = str;
while (str_v.ends_with(t_str)) str_v.remove_suffix(t_str.size());
return (string)str_v;
}
string trim(const string& str, const string t_str){
return ltrim(rtrim(str, t_str), t_str);
}
vector<string> ssplit(const string& str, const char delim){
vector<string> ssplit(const string& str, const char& delim){
vector<string> out;
for (const auto& s : str | rng::views::split(delim)
| rng::views::transform([](auto &&rng) {
@ -252,28 +199,24 @@ namespace Tools {
return out;
}
void sleep_ms(const size_t& ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
string ljust(string str, const size_t x, bool utf, bool escape, bool lim){
if (utf or escape) {
if (not escape and lim and ulen(str) > x) str = uresize(str, x);
return str + string(max((int)(x - ulen(str, escape)), 0), ' ');
string ljust(string str, const size_t x, const bool utf, const bool limit){
if (utf) {
if (limit and ulen(str) > x) str = uresize(str, x);
return str + string(max((int)(x - ulen(str)), 0), ' ');
}
else {
if (lim and str.size() > x) str.resize(x);
if (limit and str.size() > x) str.resize(x);
return str + string(max((int)(x - str.size()), 0), ' ');
}
}
string rjust(string str, const size_t x, bool utf, bool escape, bool lim){
if (utf or escape) {
if (not escape and lim and ulen(str) > x) str = uresize(str, x);
return string(max((int)(x - ulen(str, escape)), 0), ' ') + str;
string rjust(string str, const size_t x, const bool utf, const bool limit){
if (utf) {
if (limit and ulen(str) > x) str = uresize(str, x);
return string(max((int)(x - ulen(str)), 0), ' ') + str;
}
else {
if (lim and str.size() > x) str.resize(x);
if (limit and str.size() > x) str.resize(x);
return string(max((int)(x - str.size()), 0), ' ') + str;
}
}
@ -344,13 +287,13 @@ namespace Tools {
}
std::string operator*(string str, size_t n){
string out;
out.reserve(str.size() * n);
while (n-- > 0) out.append(str);
return out;
if (n == 0) return "";
str.reserve(str.size() * n);
for (string org_str = str; n > 1; n--) str.append(org_str);
return str;
}
string strf_time(string strf){
string strf_time(const string& strf){
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
std::tm bt {};
@ -359,24 +302,6 @@ namespace Tools {
return ss.str();
}
#if (__GNUC__ > 10)
//* Redirects to atomic wait
void atomic_wait(const atomic<bool>& atom, bool val){
atom.wait(val);
}
#else
//* Crude implementation of atomic wait for GCC 10
void atomic_wait(const atomic<bool>& atom, bool val){
while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1));
}
#endif
void atomic_wait_set(atomic<bool>& atom, bool val){
atomic_wait(atom, val);
atom = val;
}
}
namespace Logger {
@ -387,7 +312,7 @@ namespace Logger {
string tdf = "%Y/%m/%d (%T) | ";
}
vector<string> log_levels = {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
@ -398,11 +323,11 @@ namespace Logger {
size_t loglevel;
fs::path logfile;
void set(string level){
void set(const string level){
loglevel = v_index(log_levels, level);
}
void log_write(size_t level, string& msg){
void log_write(const size_t level, const string& msg){
if (loglevel < level or logfile.empty()) return;
atomic_wait_set(busy, true);
std::error_code ec;
@ -421,20 +346,4 @@ namespace Logger {
else logfile.clear();
busy = false;
}
void error(string msg){
log_write(1, msg);
}
void warning(string msg){
log_write(2, msg);
}
void info(string msg){
log_write(3, msg);
}
void debug(string msg){
log_write(4, msg);
}
}

View file

@ -24,9 +24,11 @@ tab-size = 4
#include <atomic>
#include <filesystem>
#include <ranges>
#include <chrono>
#include <thread>
using std::string, std::vector;
using std::string, std::vector, std::atomic;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
@ -60,7 +62,9 @@ namespace Fx {
extern const std::regex color_regex;
//* Return a string with all colors and text styling removed
string uncolor(const string& s);
inline string uncolor(const string& s){
return regex_replace(s, color_regex, "");
};
}
//* Collection of escape codes and functions for cursor manipulation
@ -89,10 +93,9 @@ namespace Mv {
//* Collection of escape codes and functions for terminal manipulation
namespace Term {
extern bool initialized;
extern bool resized;
extern int width;
extern int height;
extern atomic<bool> initialized;
extern atomic<int> width;
extern atomic<int> height;
extern string fg, bg, current_tty;
//* Hide terminal cursor
@ -128,8 +131,14 @@ namespace Term {
//* Disable direct mouse reporting
extern const string mouse_direct_off;
//* Refresh variables holding current terminal width and height and return true if resized
bool refresh();
//* Escape sequence for start of synchronized output
extern const string sync_start;
//* Escape sequence for end of synchronized output
extern const string sync_end;
//* Returns true if terminal has been resized, updates width and height if update=true
bool refresh(bool update=true);
//* Check for a valid tty, save terminal options and set new options
bool init();
@ -141,76 +150,104 @@ namespace Term {
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Tools {
const auto SSmax = std::numeric_limits<std::streamsize>::max();
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
//* Return number of UTF8 characters in a string with option to disregard escape sequences
size_t ulen(string str, const bool escape=false);
//* Return number of UTF8 characters in a string
inline size_t ulen(const string& str){
return std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
};
//* Resize a string consisting of UTF8 characters (only reduces size)
string uresize(string str, const size_t len);
string uresize(const string str, const size_t len);
//* Return <str> with only uppercase characters
string str_to_upper(const string& str);
inline string str_to_upper(string str){
std::ranges::for_each(str, [](auto& c){ c = ::toupper(c); } );
return str;
}
//* Return <str> with only lowercase characters
string str_to_lower(const string& str);
inline string str_to_lower(string str){
std::ranges::for_each(str, [](char& c){ c = ::tolower(c); } );
return str;
}
//* Check if vector <vec> contains value <find_val>
template <typename T, typename T2>
bool v_contains(const vector<T>& vec, const T2 find_val) {
inline bool v_contains(const vector<T>& vec, const T2& find_val) {
return std::ranges::find(vec, find_val) != vec.end();
}
//* Check if string <str> contains value <find_val>
template <typename T>
bool s_contains(const string& str, const T find_val) {
inline bool s_contains(const string& str, const T& find_val) {
return str.find(find_val) != string::npos;
}
//* Return index of <find_val> from vector <vec>, returns size of <vec> if <find_val> is not present
template <typename T>
size_t v_index(vector<T>& vec, T find_val) {
inline size_t v_index(const vector<T>& vec, const T& find_val) {
return std::ranges::distance(vec.begin(), std::ranges::find(vec, find_val));
}
//* Compare <first> with all following values
template<typename First, typename ... T>
bool is_in(First &&first, T && ... t) {
return ((first == t) || ...);
}
//* Return current time since epoch in seconds
uint64_t time_s();
inline uint64_t time_s(){
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Return current time since epoch in milliseconds
uint64_t time_ms();
inline uint64_t time_ms(){
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Return current time since epoch in microseconds
uint64_t time_micros();
inline uint64_t time_micros(){
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Check if a string is a valid bool value
bool isbool(string& str);
inline bool isbool(const string& str){
return (str == "true") or (str == "false") or (str == "True") or (str == "False");
}
//* Convert string to bool, returning any value not equal to "true" or "True" as false
bool stobool(string& str);
inline bool stobool(const string& str){
return (str == "true" or str == "True");
}
//* Check if a string is a valid integer value
bool isint(string& str);
inline bool isint(const string& str){
return all_of(str.begin() + (str[0] == '-' ? 1 : 0), str.end(), ::isdigit);
}
//* Left-trim <t_str> from <str> and return new string
string ltrim(const string& str, const string t_str = " ");
string ltrim(const string& str, const string& t_str = " ");
//* Right-trim <t_str> from <str> and return new string
string rtrim(const string& str, const string t_str = " ");
string rtrim(const string& str, const string& t_str = " ");
//* Left-right-trim <t_str> from <str> and return new string
string trim(const string& str, const string t_str = " ");
//* Left/right-trim <t_str> from <str> and return new string
inline string trim(const string& str, const string& t_str = " "){
return ltrim(rtrim(str, t_str), t_str);
};
//* Split <string> at all occurrences of <delim> and return as vector of strings
vector<string> ssplit(const string& str, const char delim = ' ');
vector<string> ssplit(const string& str, const char& delim = ' ');
//* Put current thread to sleep for <ms> milliseconds
void sleep_ms(const size_t& ms);
inline void sleep_ms(const size_t& ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); };
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
string ljust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true);
string ljust(string str, const size_t x, const bool utf=false, const bool limit=true);
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
string rjust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true);
string rjust(string str, const size_t x, const bool utf=false, const bool limit=true);
//* Replace whitespaces " " with escape code for move right
string trans(const string& str);
@ -228,26 +265,39 @@ namespace Tools {
std::string operator*(string str, size_t n);
//* Return current time in <strf> format
string strf_time(string strf);
string strf_time(const string& strf);
//* Waits for <atom> to not be <val>
void atomic_wait(const std::atomic<bool>& atom, bool val=true);
#if (__GNUC__ > 10)
//* Redirects to atomic wait
inline void atomic_wait(const atomic<bool>& atom, bool val=true){
atom.wait(val);
}
#else
//* Crude implementation of atomic wait for GCC 10
inline void atomic_wait(const atomic<bool>& atom, bool val=true){
while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1));
}
#endif
//* Waits for <atom> to not be <val> and then sets it to <val> again
void atomic_wait_set(std::atomic<bool>& atom, bool val=true);
inline void atomic_wait_set(std::atomic<bool>& atom, bool val=true){
atomic_wait(atom, val);
atom = val;
};
}
//* Simple logging implementation
namespace Logger {
extern vector<string> log_levels;
extern const vector<string> log_levels;
extern std::filesystem::path logfile;
void set(string level); //* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void log_write(size_t level, string& msg);
void error(string msg);
void warning(string msg);
void info(string msg);
void debug(string msg);
void set(const string level); //* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void log_write(const size_t level, const string& msg);
inline void error(const string msg){ log_write(1, msg); }
inline void warning(const string msg){ log_write(2, msg); }
inline void info(const string msg){ log_write(3, msg); }
inline void debug(const string msg){ log_write(4, msg); }
}