mirror of
https://github.com/aristocratos/btop.git
synced 2024-05-21 04:43:36 +12:00
Proc::draw() and SIGWINCH resizing handler
This commit is contained in:
parent
e3b297e82a
commit
1121978214
254
src/btop.cpp
254
src/btop.cpp
|
@ -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;
|
||||
|
||||
//*-----<<<<<
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue