mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-14 08:25:29 +12:00
Added Input::process for input actions and Runner:: namespace for multithreading collection and drawing
This commit is contained in:
parent
1121978214
commit
0c1feb909e
18
Makefile
18
Makefile
|
@ -4,17 +4,11 @@ DOCDIR ?= $(PREFIX)/share/btop/doc
|
||||||
#Compiler and Linker
|
#Compiler and Linker
|
||||||
CXX := g++
|
CXX := g++
|
||||||
|
|
||||||
#If using g++ try to make sure we are using version 11 or 10
|
#Try to make sure we are using GCC/G++ version 11 or later
|
||||||
ifeq ($(CXX),g++)
|
CXX_VERSION = $(shell $(CXX) -dumpversion)
|
||||||
CXX_VERSION = $(shell $(CXX) -dumpversion)
|
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
|
||||||
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
|
ifneq ($(shell command -v g++-11),)
|
||||||
ifneq ($(shell command -v g++-11),)
|
CXX := g++-11
|
||||||
CXX := g++-11
|
|
||||||
else ifneq ($(shell test $(CXX_VERSION) -eq 10; echo $$?),0)
|
|
||||||
ifneq ($(shell command -v g++-10),)
|
|
||||||
CXX := g++-10
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -75,7 +69,7 @@ uninstall:
|
||||||
|
|
||||||
#Link
|
#Link
|
||||||
btop: $(OBJECTS)
|
btop: $(OBJECTS)
|
||||||
$(CXX) -o $(TARGETDIR)/btop $^
|
$(CXX) -o $(TARGETDIR)/btop $^ -pthread
|
||||||
|
|
||||||
#Compile
|
#Compile
|
||||||
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
||||||
|
|
|
@ -126,7 +126,7 @@ Options menu.
|
||||||
|
|
||||||
#### Manual compilation and installation
|
#### Manual compilation and installation
|
||||||
|
|
||||||
Needs at least GCC/G++ 10, preferably 11 (or higher) to make use of some C++20 functionality.
|
Needs GCC/G++ 11 or higher, (GCC 10 is missing some C++20 features).
|
||||||
|
|
||||||
>Install dependencies (Ubuntu 21.04 Hirsute)
|
>Install dependencies (Ubuntu 21.04 Hirsute)
|
||||||
|
|
||||||
|
|
281
src/btop.cpp
281
src/btop.cpp
|
@ -81,6 +81,9 @@ namespace Global {
|
||||||
string banner;
|
string banner;
|
||||||
size_t banner_width = 0;
|
size_t banner_width = 0;
|
||||||
|
|
||||||
|
string exit_error_msg;
|
||||||
|
atomic<bool> thread_exception (false);
|
||||||
|
|
||||||
fs::path self_path;
|
fs::path self_path;
|
||||||
|
|
||||||
bool debuginit = false;
|
bool debuginit = false;
|
||||||
|
@ -142,10 +145,7 @@ void argumentParser(int argc, char **argv){
|
||||||
void _resize(bool force=false){
|
void _resize(bool force=false){
|
||||||
if (Term::refresh(false) or force) {
|
if (Term::refresh(false) or force) {
|
||||||
Global::resized = true;
|
Global::resized = true;
|
||||||
if (Runner::active) {
|
Runner::stop();
|
||||||
Runner::stop = true;
|
|
||||||
atomic_wait(Runner::active);
|
|
||||||
}
|
|
||||||
Term::refresh();
|
Term::refresh();
|
||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
|
@ -160,29 +160,23 @@ void _resize(bool force=false){
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Exit handler; stops threads, restores terminal and saves config changes
|
//* Exit handler; stops threads, restores terminal and saves config changes
|
||||||
void clean_quit(int sig=-1){
|
void clean_quit(int sig){
|
||||||
if (Global::quitting) return;
|
if (Global::quitting) return;
|
||||||
Global::quitting = true;
|
Global::quitting = true;
|
||||||
if (Runner::active) {
|
Runner::stop();
|
||||||
Runner::stop = true;
|
|
||||||
atomic_wait(Runner::active);
|
|
||||||
}
|
|
||||||
if (Term::initialized) {
|
if (Term::initialized) {
|
||||||
Term::restore();
|
Term::restore();
|
||||||
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::write();
|
Config::write();
|
||||||
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
||||||
|
if (not Global::exit_error_msg.empty()) cout << Global::exit_error_msg << endl;
|
||||||
if (sig != -1) exit(sig);
|
if (sig != -1) exit(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
|
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
|
||||||
void _sleep(){
|
void _sleep(){
|
||||||
if (Runner::active) {
|
Runner::stop();
|
||||||
Runner::stop = true;
|
|
||||||
atomic_wait(Runner::active);
|
|
||||||
}
|
|
||||||
if (Term::initialized) {
|
if (Term::initialized) {
|
||||||
Term::restore();
|
Term::restore();
|
||||||
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
||||||
|
@ -259,9 +253,97 @@ void banner_gen() {
|
||||||
+ Fx::i + "v" + Global::Version + Fx::ui;
|
+ Fx::i + "v" + Global::Version + Fx::ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//? Manages secondary thread for collection and drawing of boxes
|
||||||
namespace Runner {
|
namespace Runner {
|
||||||
atomic<bool> active (false);
|
atomic<bool> active (false);
|
||||||
atomic<bool> stop (false);
|
atomic<bool> stopping (false);
|
||||||
|
atomic<bool> has_output (false);
|
||||||
|
atomic<uint64_t> time_spent (0);
|
||||||
|
|
||||||
|
string output;
|
||||||
|
|
||||||
|
void _runner(const vector<string> boxes, const bool no_update, const bool force_redraw, const bool interrupt) {
|
||||||
|
auto timestamp = time_micros();
|
||||||
|
string out;
|
||||||
|
out.reserve(output.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (auto& box : boxes) {
|
||||||
|
if (stopping) break;
|
||||||
|
try {
|
||||||
|
if (box == "cpu") {
|
||||||
|
out += Cpu::draw(Cpu::collect(no_update), force_redraw);
|
||||||
|
}
|
||||||
|
else if (box == "mem") {
|
||||||
|
out += Mem::draw(Mem::collect(no_update), force_redraw);
|
||||||
|
}
|
||||||
|
else if (box == "net") {
|
||||||
|
out += Net::draw(Net::collect(no_update), force_redraw);
|
||||||
|
}
|
||||||
|
else if (box == "proc") {
|
||||||
|
out += Proc::draw(Proc::collect(no_update), force_redraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
string fname = box;
|
||||||
|
fname[0] = toupper(fname[0]);
|
||||||
|
throw std::runtime_error(fname + "::draw(" + fname + "::collect(no_update=" + (no_update ? "true" : "false")
|
||||||
|
+ "), force_redraw=" + (force_redraw ? "true" : "false") + ") : " + (string)e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e) {
|
||||||
|
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
|
||||||
|
Logger::error(Global::exit_error_msg);
|
||||||
|
Global::thread_exception = true;
|
||||||
|
Input::interrupt = true;
|
||||||
|
stopping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopping) {
|
||||||
|
active = false;
|
||||||
|
active.notify_all();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out.empty()) {
|
||||||
|
out += "No boxes shown!";
|
||||||
|
}
|
||||||
|
|
||||||
|
output.swap(out);
|
||||||
|
time_spent = time_micros() - timestamp;
|
||||||
|
has_output = true;
|
||||||
|
if (interrupt) Input::interrupt = true;
|
||||||
|
|
||||||
|
active = false;
|
||||||
|
active.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(const string box, const bool no_update, const bool force_redraw, const bool input_interrupt) {
|
||||||
|
active.wait(true);
|
||||||
|
if (stopping) return;
|
||||||
|
active = true;
|
||||||
|
Config::lock();
|
||||||
|
vector<string> boxes;
|
||||||
|
if (box.empty()) boxes = Config::current_boxes;
|
||||||
|
else boxes.push_back(box);
|
||||||
|
std::thread run_thread(_runner, boxes, no_update, force_redraw, input_interrupt);
|
||||||
|
run_thread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
stopping = true;
|
||||||
|
active.wait(true);
|
||||||
|
Config::unlock();
|
||||||
|
stopping = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_output() {
|
||||||
|
active.wait(true);
|
||||||
|
Config::unlock();
|
||||||
|
has_output = false;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -396,27 +478,6 @@ int main(int argc, char **argv){
|
||||||
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
|
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
|
||||||
|
|
||||||
|
|
||||||
if (false) {
|
|
||||||
Draw::calcSizes();
|
|
||||||
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
|
|
||||||
Input::wait();
|
|
||||||
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
|
//* Test theme
|
||||||
if (false) {
|
if (false) {
|
||||||
string key;
|
string key;
|
||||||
|
@ -474,7 +535,7 @@ int main(int argc, char **argv){
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//* Test graphs
|
||||||
if (false) {
|
if (false) {
|
||||||
|
|
||||||
deque<long long> mydata;
|
deque<long long> mydata;
|
||||||
|
@ -524,119 +585,49 @@ int main(int argc, char **argv){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (false) {
|
|
||||||
cout << Config::getS("log_level") << endl;
|
|
||||||
|
|
||||||
vector<string> vv = {"hej", "vad", "du"};
|
//* ------------------------------------------------ MAIN LOOP ----------------------------------------------------
|
||||||
vector<int> vy;
|
|
||||||
|
|
||||||
cout << v_contains(vv, "vad"s) << endl;
|
uint64_t future_time = time_ms(), rcount = 0;
|
||||||
cout << v_index(vv, "hej"s) << endl;
|
|
||||||
cout << v_index(vv, "du"s) << endl;
|
|
||||||
cout << v_index(vv, "kodkod"s) << endl;
|
|
||||||
cout << v_index(vy, 4) << endl;
|
|
||||||
|
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//*------>>>>>> Proc testing
|
|
||||||
|
|
||||||
|
|
||||||
auto timestamp = time_ms();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string ostring;
|
|
||||||
uint64_t tsl, timestamp2, rcount = 0;
|
|
||||||
list<uint64_t> avgtimes;
|
list<uint64_t> avgtimes;
|
||||||
size_t timer = 2000;
|
|
||||||
vector<string> greyscale;
|
|
||||||
string filter;
|
|
||||||
string filter_cur;
|
|
||||||
string key;
|
|
||||||
vector<Proc::proc_info> plist;
|
|
||||||
|
|
||||||
int xc;
|
try {
|
||||||
for (size_t i : iota(0, (int)Term::height - 19)){
|
while (true) {
|
||||||
xc = 230 - i * 150 / (Term::height - 20);
|
if (Global::thread_exception) clean_quit(1);
|
||||||
greyscale.push_back(Theme::dec_to_color(xc, xc, xc));
|
|
||||||
|
if (Runner::has_output) {
|
||||||
|
cout << Term::sync_start << Runner::get_output() << Term::sync_end << flush;
|
||||||
|
|
||||||
|
//! DEBUG stats
|
||||||
|
avgtimes.push_front(Runner::time_spent);
|
||||||
|
if (avgtimes.size() > 30) avgtimes.pop_back();
|
||||||
|
cout << Fx::reset << Mv::to(2, 2) << "Runner took: " << rjust(to_string(avgtimes.front()), 5) << " μs. Average: " <<
|
||||||
|
rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
|
||||||
|
" samples. Run count: " << ++rcount << ". " << flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_ms() >= future_time) {
|
||||||
|
Runner::run();
|
||||||
|
future_time = time_ms() + Config::getI("update_ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (time_ms() < future_time and not Global::resized) {
|
||||||
|
if (Input::poll(future_time - time_ms()))
|
||||||
|
Input::process(Input::get());
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Global::resized) {
|
||||||
|
// cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
|
||||||
|
Global::resized = false;
|
||||||
|
future_time = time_ms();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (std::exception& e) {
|
||||||
while (key != "q") {
|
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
|
||||||
timestamp = time_micros();
|
Logger::error(Global::exit_error_msg);
|
||||||
tsl = time_ms() + timer;
|
clean_quit(1);
|
||||||
Config::lock();
|
|
||||||
try {
|
|
||||||
plist = Proc::collect();
|
|
||||||
}
|
|
||||||
catch (std::exception const& e) {
|
|
||||||
Logger::error("Caught exception in Proc::collect() : "s + e.what());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
timestamp2 = time_micros();
|
|
||||||
timestamp = timestamp2 - timestamp;
|
|
||||||
ostring.clear();
|
|
||||||
|
|
||||||
ostring = Proc::draw(plist);
|
|
||||||
Config::unlock();
|
|
||||||
|
|
||||||
avgtimes.push_front(timestamp);
|
|
||||||
if (avgtimes.size() > 30) avgtimes.pop_back();
|
|
||||||
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 << " ";
|
|
||||||
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 ") << Term::sync_end << flush;
|
|
||||||
|
|
||||||
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")) {
|
|
||||||
if (key == "enter") Config::set("proc_filtering", false);
|
|
||||||
else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1);
|
|
||||||
else if (key == "space") filter.push_back(' ');
|
|
||||||
else if (ulen(key) == 1 ) filter.append(key);
|
|
||||||
else { key.clear(); continue; }
|
|
||||||
if (filter != Config::getS("proc_filter")) Config::set("proc_filter", filter);
|
|
||||||
key.clear();
|
|
||||||
Proc::redraw = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (key == "q") break;
|
|
||||||
else if (key == "left") {
|
|
||||||
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
|
|
||||||
if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1;
|
|
||||||
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
|
|
||||||
}
|
|
||||||
else if (key == "right") {
|
|
||||||
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
|
|
||||||
if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0;
|
|
||||||
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
|
|
||||||
}
|
|
||||||
else if (key == "f") Config::flip("proc_filtering");
|
|
||||||
else if (key == "t") Config::flip("proc_tree");
|
|
||||||
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 (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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,7 @@ namespace Config {
|
||||||
vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
|
vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
|
||||||
|
|
||||||
bool _locked(const string& name){
|
bool _locked(const string& name){
|
||||||
atomic_wait(writelock);
|
writelock.wait(true);
|
||||||
if (not write_new and rng::find_if(descriptions, [&name](const auto& a){ return a.at(0) == name; }) != descriptions.end())
|
if (not write_new and rng::find_if(descriptions, [&name](const auto& a){ return a.at(0) == name; }) != descriptions.end())
|
||||||
write_new = true;
|
write_new = true;
|
||||||
return locked.load();
|
return locked.load();
|
||||||
|
@ -240,17 +240,19 @@ namespace Config {
|
||||||
fs::path conf_dir;
|
fs::path conf_dir;
|
||||||
fs::path conf_file;
|
fs::path conf_file;
|
||||||
|
|
||||||
|
vector<string> current_boxes;
|
||||||
|
|
||||||
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
||||||
|
|
||||||
const bool& getB(string name){
|
const bool& getB(const string& name){
|
||||||
return bools.at(name);
|
return bools.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int& getI(string name){
|
const int& getI(const string& name){
|
||||||
return ints.at(name);
|
return ints.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& getS(string name){
|
const string& getS(const string& name){
|
||||||
return strings.at(name);
|
return strings.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,13 +280,19 @@ namespace Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock(){
|
void lock(){
|
||||||
atomic_wait_set(locked);
|
writelock.wait(true);
|
||||||
|
locked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock(){
|
void unlock(){
|
||||||
if (not locked) return;
|
if (not locked) return;
|
||||||
atomic_wait(Runner::active);
|
writelock = true;
|
||||||
atomic_wait_set(writelock);
|
|
||||||
|
if (Proc::shown) {
|
||||||
|
ints.at("selected_pid") = Proc::selected_pid;
|
||||||
|
ints.at("proc_start") = Proc::start;
|
||||||
|
ints.at("proc_selected") = Proc::selected;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& item : stringsTmp){
|
for (auto& item : stringsTmp){
|
||||||
strings.at(item.first) = item.second;
|
strings.at(item.first) = item.second;
|
||||||
|
@ -301,20 +309,17 @@ namespace Config {
|
||||||
}
|
}
|
||||||
boolsTmp.clear();
|
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;
|
locked = false;
|
||||||
writelock = false;
|
writelock = false;
|
||||||
|
writelock.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_boxes(string boxes){
|
bool check_boxes(string boxes){
|
||||||
for (auto& box : ssplit(boxes)) {
|
auto new_boxes = ssplit(boxes);
|
||||||
|
for (auto& box : new_boxes) {
|
||||||
if (not v_contains(valid_boxes, box)) return false;
|
if (not v_contains(valid_boxes, box)) return false;
|
||||||
}
|
}
|
||||||
|
current_boxes.swap(new_boxes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,10 +347,12 @@ namespace Config {
|
||||||
}
|
}
|
||||||
string name, value;
|
string name, value;
|
||||||
getline(cread, name, '=');
|
getline(cread, name, '=');
|
||||||
|
if (name.ends_with(' ')) name = trim(name);
|
||||||
if (not v_contains(valid_names, name)) {
|
if (not v_contains(valid_names, name)) {
|
||||||
cread.ignore(SSmax, '\n');
|
cread.ignore(SSmax, '\n');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
cread >> std::ws;
|
||||||
|
|
||||||
if (bools.contains(name)) {
|
if (bools.contains(name)) {
|
||||||
cread >> value;
|
cread >> value;
|
||||||
|
@ -362,7 +369,6 @@ namespace Config {
|
||||||
ints.at(name) = stoi(value);
|
ints.at(name) = stoi(value);
|
||||||
}
|
}
|
||||||
else if (strings.contains(name)) {
|
else if (strings.contains(name)) {
|
||||||
cread >> std::ws;
|
|
||||||
if (cread.peek() == '"') {
|
if (cread.peek() == '"') {
|
||||||
cread.ignore(1);
|
cread.ignore(1);
|
||||||
getline(cread, value, '"');
|
getline(cread, value, '"');
|
||||||
|
@ -393,10 +399,14 @@ namespace Config {
|
||||||
if (cwrite.good()) {
|
if (cwrite.good()) {
|
||||||
cwrite << "#? Config file for btop v. " << Global::Version;
|
cwrite << "#? Config file for btop v. " << Global::Version;
|
||||||
for (auto [name, description] : descriptions) {
|
for (auto [name, description] : descriptions) {
|
||||||
cwrite << "\n\n" << (description.empty() ? "" : description + "\n") << name << "=";
|
cwrite << "\n\n" << (description.empty() ? "" : description + "\n")
|
||||||
if (strings.contains(name)) cwrite << "\"" << strings.at(name) << "\"";
|
<< name << " = ";
|
||||||
else if (ints.contains(name)) cwrite << ints.at(name);
|
if (strings.contains(name))
|
||||||
else if (bools.contains(name)) cwrite << (bools.at(name) ? "True" : "False");
|
cwrite << "\"" << strings.at(name) << "\"";
|
||||||
|
else if (ints.contains(name))
|
||||||
|
cwrite << ints.at(name);
|
||||||
|
else if (bools.contains(name))
|
||||||
|
cwrite << (bools.at(name) ? "True" : "False");
|
||||||
}
|
}
|
||||||
cwrite.close();
|
cwrite.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,17 +32,19 @@ namespace Config {
|
||||||
|
|
||||||
extern const vector<string> valid_graph_symbols;
|
extern const vector<string> valid_graph_symbols;
|
||||||
|
|
||||||
|
extern vector<string> current_boxes;
|
||||||
|
|
||||||
//* Check if string only contains space seperated valid names for boxes
|
//* Check if string only contains space seperated valid names for boxes
|
||||||
bool check_boxes(string boxes);
|
bool check_boxes(string boxes);
|
||||||
|
|
||||||
//* Return bool for config key <name>
|
//* Return bool for config key <name>
|
||||||
const bool& getB(string name);
|
const bool& getB(const string& name);
|
||||||
|
|
||||||
//* Return integer for config key <name>
|
//* Return integer for config key <name>
|
||||||
const int& getI(string name);
|
const int& getI(const string& name);
|
||||||
|
|
||||||
//* Return string for config key <name>
|
//* Return string for config key <name>
|
||||||
const string& getS(string name);
|
const string& getS(const string& name);
|
||||||
|
|
||||||
//* Set config key <name> to bool <value>
|
//* Set config key <name> to bool <value>
|
||||||
void set(string name, bool value);
|
void set(string name, bool value);
|
||||||
|
|
|
@ -275,6 +275,16 @@ namespace Cpu {
|
||||||
bool shown = true, redraw = true;
|
bool shown = true, redraw = true;
|
||||||
string box;
|
string box;
|
||||||
|
|
||||||
|
string draw(const cpu_info& cpu, bool force_redraw) {
|
||||||
|
(void)cpu;
|
||||||
|
string out;
|
||||||
|
if (redraw or force_redraw) {
|
||||||
|
redraw = false;
|
||||||
|
out += box;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Mem {
|
namespace Mem {
|
||||||
|
@ -285,6 +295,16 @@ namespace Mem {
|
||||||
bool shown = true, redraw = true;
|
bool shown = true, redraw = true;
|
||||||
string box;
|
string box;
|
||||||
|
|
||||||
|
string draw(const mem_info& mem, bool force_redraw) {
|
||||||
|
(void)mem;
|
||||||
|
string out;
|
||||||
|
if (redraw or force_redraw) {
|
||||||
|
redraw = false;
|
||||||
|
out += box;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
|
@ -296,6 +316,16 @@ namespace Net {
|
||||||
bool shown = true, redraw = true;
|
bool shown = true, redraw = true;
|
||||||
string box;
|
string box;
|
||||||
|
|
||||||
|
string draw(const net_info& net, bool force_redraw) {
|
||||||
|
(void)net;
|
||||||
|
string out;
|
||||||
|
if (redraw or force_redraw) {
|
||||||
|
redraw = false;
|
||||||
|
out += box;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Proc {
|
namespace Proc {
|
||||||
|
@ -311,6 +341,7 @@ namespace Proc {
|
||||||
void selection(string cmd_key) {
|
void selection(string cmd_key) {
|
||||||
auto start = Config::getI("proc_start");
|
auto start = Config::getI("proc_start");
|
||||||
auto selected = Config::getI("proc_selected");
|
auto selected = Config::getI("proc_selected");
|
||||||
|
int numpids = Proc::numpids;
|
||||||
if (cmd_key == "up" and selected > 0) {
|
if (cmd_key == "up" and selected > 0) {
|
||||||
if (start > 0 and selected == 1) start--;
|
if (start > 0 and selected == 1) start--;
|
||||||
else selected--;
|
else selected--;
|
||||||
|
@ -340,7 +371,7 @@ namespace Proc {
|
||||||
Config::set("proc_selected", selected);
|
Config::set("proc_selected", selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
string draw(vector<proc_info> plist){
|
string draw(const vector<proc_info>& plist, bool force_redraw){
|
||||||
auto& filter = Config::getS("proc_filter");
|
auto& filter = Config::getS("proc_filter");
|
||||||
auto& filtering = Config::getB("proc_filtering");
|
auto& filtering = Config::getB("proc_filtering");
|
||||||
auto& proc_tree = Config::getB("proc_tree");
|
auto& proc_tree = Config::getB("proc_tree");
|
||||||
|
@ -352,9 +383,10 @@ namespace Proc {
|
||||||
uint64_t total_mem = 16328872 << 10;
|
uint64_t total_mem = 16328872 << 10;
|
||||||
int y = show_detailed ? Proc::y + 9 : Proc::y;
|
int y = show_detailed ? Proc::y + 9 : Proc::y;
|
||||||
int height = show_detailed ? Proc::height - 9 : Proc::height;
|
int height = show_detailed ? Proc::height - 9 : Proc::height;
|
||||||
|
int numpids = Proc::numpids;
|
||||||
string out;
|
string out;
|
||||||
//* If true, redraw elements not needed to be updated every cycle
|
//* If true, redraw elements not needed to be updated every cycle
|
||||||
if (redraw) {
|
if (redraw or force_redraw) {
|
||||||
redraw = false;
|
redraw = false;
|
||||||
out = box;
|
out = box;
|
||||||
out += Mv::to(y, x) + Mv::r(12)
|
out += Mv::to(y, x) + Mv::r(12)
|
||||||
|
|
|
@ -22,8 +22,10 @@ tab-size = 4
|
||||||
|
|
||||||
#include <btop_input.hpp>
|
#include <btop_input.hpp>
|
||||||
#include <btop_tools.hpp>
|
#include <btop_tools.hpp>
|
||||||
|
#include <btop_config.hpp>
|
||||||
|
#include <btop_shared.hpp>
|
||||||
|
|
||||||
using std::string, robin_hood::unordered_flat_map, std::cin;
|
using std::string, robin_hood::unordered_flat_map, std::cin, std::string_literals::operator""s;
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
|
@ -95,7 +97,7 @@ namespace Input {
|
||||||
|
|
||||||
string wait(){
|
string wait(){
|
||||||
while (cin.rdbuf()->in_avail() < 1) {
|
while (cin.rdbuf()->in_avail() < 1) {
|
||||||
if (interrupt) { interrupt = false; return string(); }
|
if (interrupt) { interrupt = false; return ""; }
|
||||||
sleep_ms(10);
|
sleep_ms(10);
|
||||||
}
|
}
|
||||||
return get();
|
return get();
|
||||||
|
@ -105,4 +107,59 @@ namespace Input {
|
||||||
last.clear();
|
last.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void process(const string key){
|
||||||
|
if (key.empty()) return;
|
||||||
|
try {
|
||||||
|
auto& filtering = Config::getB("proc_filtering");
|
||||||
|
if (not filtering and key == "q") clean_quit(0);
|
||||||
|
bool recollect = true;
|
||||||
|
bool redraw = true;
|
||||||
|
|
||||||
|
//* Input actions for proc
|
||||||
|
if (Proc::shown) {
|
||||||
|
bool keep_going = false;
|
||||||
|
if (filtering) {
|
||||||
|
string filter = Config::getS("proc_filter");
|
||||||
|
if (key == "enter") Config::set("proc_filtering", false);
|
||||||
|
else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1);
|
||||||
|
else if (key == "space") filter.push_back(' ');
|
||||||
|
else if (ulen(key) == 1) filter.append(key);
|
||||||
|
else return;
|
||||||
|
Config::set("proc_filter", filter);
|
||||||
|
}
|
||||||
|
else if (key == "left") {
|
||||||
|
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
|
||||||
|
if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1;
|
||||||
|
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
|
||||||
|
}
|
||||||
|
else if (key == "right") {
|
||||||
|
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
|
||||||
|
if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0;
|
||||||
|
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
|
||||||
|
}
|
||||||
|
else if (key == "f") { Config::flip("proc_filtering"); recollect = false; }
|
||||||
|
else if (key == "t") Config::flip("proc_tree");
|
||||||
|
else if (key == "r") Config::flip("proc_reversed");
|
||||||
|
else if (key == "c") Config::flip("proc_per_core");
|
||||||
|
else if (key == "delete" and not Config::getS("proc_filter").empty()) Config::set("proc_filter", ""s);
|
||||||
|
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
|
||||||
|
Proc::selection(key);
|
||||||
|
recollect = false;
|
||||||
|
redraw = false;
|
||||||
|
}
|
||||||
|
else keep_going = true;
|
||||||
|
|
||||||
|
if (not keep_going) {
|
||||||
|
Runner::run("proc", not recollect, redraw);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
throw std::runtime_error("Input::process(\"" + key + "\") : " + (string)e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -47,4 +47,7 @@ namespace Input {
|
||||||
//* Clears last entered key
|
//* Clears last entered key
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
//* Process actions for input <key>
|
||||||
|
void process(const std::string key);
|
||||||
|
|
||||||
}
|
}
|
|
@ -93,10 +93,34 @@ namespace Shared {
|
||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
bool got_sensors = false;
|
bool got_sensors = false;
|
||||||
string cpuName = "";
|
string cpuName = "";
|
||||||
|
|
||||||
|
cpu_info current_cpu;
|
||||||
|
|
||||||
|
cpu_info& collect(const bool return_last) {
|
||||||
|
(void)return_last;
|
||||||
|
return current_cpu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Mem {
|
namespace Mem {
|
||||||
bool has_swap = false;
|
bool has_swap = false;
|
||||||
|
|
||||||
|
mem_info current_mem;
|
||||||
|
|
||||||
|
mem_info& collect(const bool return_last) {
|
||||||
|
(void)return_last;
|
||||||
|
return current_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
net_info current_net;
|
||||||
|
|
||||||
|
net_info& collect(const bool return_last) {
|
||||||
|
(void)return_last;
|
||||||
|
return current_net;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Proc {
|
namespace Proc {
|
||||||
|
@ -117,11 +141,9 @@ namespace Proc {
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
}
|
}
|
||||||
uint64_t old_cputimes = 0;
|
uint64_t old_cputimes = 0;
|
||||||
int numpids = 0;
|
atomic<int> numpids = 0;
|
||||||
size_t reserve_pids = 500;
|
size_t reserve_pids = 500;
|
||||||
bool tree_state = false;
|
bool tree_state = false;
|
||||||
atomic<bool> stop (false);
|
|
||||||
atomic<bool> collecting (false);
|
|
||||||
vector<string> sort_vector = {
|
vector<string> sort_vector = {
|
||||||
"pid",
|
"pid",
|
||||||
"name",
|
"name",
|
||||||
|
@ -150,6 +172,7 @@ namespace Proc {
|
||||||
|
|
||||||
//* Generate process tree list
|
//* Generate process tree list
|
||||||
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){
|
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){
|
||||||
|
if (Runner::stopping) return;
|
||||||
auto cur_pos = out_procs.size();
|
auto cur_pos = out_procs.size();
|
||||||
bool filtering = false;
|
bool filtering = false;
|
||||||
|
|
||||||
|
@ -290,9 +313,8 @@ namespace Proc {
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Collects and sorts process information from /proc
|
//* Collects and sorts process information from /proc
|
||||||
vector<proc_info>& collect(bool return_last){
|
vector<proc_info>& collect(const bool return_last){
|
||||||
if (return_last) return current_procs;
|
if (return_last) return current_procs;
|
||||||
atomic_wait_set(collecting);
|
|
||||||
auto& sorting = Config::getS("proc_sorting");
|
auto& sorting = Config::getS("proc_sorting");
|
||||||
auto reverse = Config::getB("proc_reversed");
|
auto reverse = Config::getB("proc_reversed");
|
||||||
auto& filter = Config::getS("proc_filter");
|
auto& filter = Config::getS("proc_filter");
|
||||||
|
@ -345,12 +367,9 @@ namespace Proc {
|
||||||
|
|
||||||
//* Iterate over all pids in /proc
|
//* Iterate over all pids in /proc
|
||||||
for (auto& d: fs::directory_iterator(Shared::proc_path)){
|
for (auto& d: fs::directory_iterator(Shared::proc_path)){
|
||||||
if (pread.is_open()) pread.close();
|
if (Runner::stopping)
|
||||||
if (stop) {
|
|
||||||
collecting = false;
|
|
||||||
stop = false;
|
|
||||||
return current_procs;
|
return current_procs;
|
||||||
}
|
if (pread.is_open()) pread.close();
|
||||||
|
|
||||||
string pid_str = d.path().filename();
|
string pid_str = d.path().filename();
|
||||||
if (not isdigit(pid_str[0])) continue;
|
if (not isdigit(pid_str[0])) continue;
|
||||||
|
@ -554,6 +573,8 @@ namespace Proc {
|
||||||
for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
|
for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
|
||||||
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter);
|
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Runner::stopping) return current_procs;
|
||||||
procs.swap(tree_procs);
|
procs.swap(tree_procs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +595,6 @@ namespace Proc {
|
||||||
numpids = (int)procs.size();
|
numpids = (int)procs.size();
|
||||||
current_procs.swap(procs);
|
current_procs.swap(procs);
|
||||||
reserve_pids = npids;
|
reserve_pids = npids;
|
||||||
collecting = false;
|
|
||||||
return current_procs;
|
return current_procs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,16 @@ tab-size = 4
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic;
|
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array;
|
||||||
|
|
||||||
|
void clean_quit(int sig=-1);
|
||||||
|
|
||||||
namespace Global {
|
namespace Global {
|
||||||
extern const string Version;
|
extern const string Version;
|
||||||
|
extern string exit_error_msg;
|
||||||
|
extern atomic<bool> thread_exception;
|
||||||
extern int coreCount;
|
extern int coreCount;
|
||||||
extern string banner;
|
extern string banner;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +41,10 @@ namespace Global {
|
||||||
namespace Runner {
|
namespace Runner {
|
||||||
|
|
||||||
extern atomic<bool> active;
|
extern atomic<bool> active;
|
||||||
extern atomic<bool> stop;
|
extern atomic<bool> stopping;
|
||||||
|
|
||||||
|
void run(const string box="", const bool no_update=false, const bool force_redraw=false, const bool input_interrupt=true);
|
||||||
|
void stop();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,22 +61,65 @@ namespace Shared {
|
||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
extern string box, cpuName;
|
extern string box, cpuName;
|
||||||
extern bool shown, redraw, got_sensors;
|
extern bool shown, redraw, got_sensors;
|
||||||
|
|
||||||
|
struct cpu_info {
|
||||||
|
vector<deque<long long>> percent;
|
||||||
|
vector<deque<long long>> temp;
|
||||||
|
array<float, 3> load_avg;
|
||||||
|
};
|
||||||
|
|
||||||
|
//* Collect cpu stats and temperatures
|
||||||
|
cpu_info& collect(const bool return_last=false);
|
||||||
|
|
||||||
|
//* Draw contents of cpu box using <cpu> as source
|
||||||
|
string draw(const cpu_info& cpu, bool force_redraw=false);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Mem {
|
namespace Mem {
|
||||||
extern string box;
|
extern string box;
|
||||||
extern bool has_swap, shown, redraw;
|
extern bool has_swap, shown, redraw;
|
||||||
|
|
||||||
|
struct disk_info {
|
||||||
|
uint64_t total = 0, used = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mem_info {
|
||||||
|
uint64_t total = 0, available = 0, cached = 0, free = 0;
|
||||||
|
unordered_flat_map<string, deque<long long>> percent;
|
||||||
|
unordered_flat_map<string, deque<long long>> disks_io;
|
||||||
|
unordered_flat_map<string, disk_info> disks;
|
||||||
|
};
|
||||||
|
|
||||||
|
//* Collect mem & disks stats
|
||||||
|
mem_info& collect(const bool return_last=false);
|
||||||
|
|
||||||
|
//* Draw contents of mem box using <mem> as source
|
||||||
|
string draw(const mem_info& mem, bool force_redraw=false);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
extern string box;
|
extern string box;
|
||||||
extern bool shown, redraw;
|
extern bool shown, redraw;
|
||||||
|
|
||||||
|
struct net_stat {
|
||||||
|
uint64_t speed = 0, top = 0, total = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_info {
|
||||||
|
unordered_flat_map<string, deque<long long>> bandwidth;
|
||||||
|
unordered_flat_map<string, net_stat> stat;
|
||||||
|
string ip_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
//* Collect net upload/download stats
|
||||||
|
net_info& collect(const bool return_last=false);
|
||||||
|
|
||||||
|
//* Draw contents of net box using <net> as source
|
||||||
|
string draw(const net_info& net, bool force_redraw=false);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Proc {
|
namespace Proc {
|
||||||
extern int numpids;
|
extern atomic<int> numpids;
|
||||||
extern atomic<bool> stop;
|
|
||||||
extern atomic<bool> collecting;
|
|
||||||
|
|
||||||
extern string box;
|
extern string box;
|
||||||
extern bool shown, redraw;
|
extern bool shown, redraw;
|
||||||
|
@ -106,12 +157,12 @@ namespace Proc {
|
||||||
|
|
||||||
extern detail_container detailed;
|
extern detail_container detailed;
|
||||||
|
|
||||||
//* Collect and sort process information from /proc, saves and returns reference to Proc::current_procs;
|
//* Collect and sort process information from /proc
|
||||||
vector<proc_info>& collect(bool return_last=false);
|
vector<proc_info>& collect(const bool return_last=false);
|
||||||
|
|
||||||
//* Update current selection and view
|
//* Update current selection and view
|
||||||
void selection(string cmd_key);
|
void selection(string cmd_key);
|
||||||
|
|
||||||
//* Draw contents of proc box using <plist> as data source
|
//* Draw contents of proc box using <plist> as data source
|
||||||
string draw(vector<proc_info> plist);
|
string draw(const vector<proc_info>& plist, bool force_redraw=false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,7 +329,8 @@ namespace Logger {
|
||||||
|
|
||||||
void log_write(const size_t level, const string& msg){
|
void log_write(const size_t level, const string& msg){
|
||||||
if (loglevel < level or logfile.empty()) return;
|
if (loglevel < level or logfile.empty()) return;
|
||||||
atomic_wait_set(busy, true);
|
busy.wait(true);
|
||||||
|
busy = true;
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
||||||
auto old_log = logfile;
|
auto old_log = logfile;
|
||||||
|
@ -345,5 +346,6 @@ namespace Logger {
|
||||||
}
|
}
|
||||||
else logfile.clear();
|
else logfile.clear();
|
||||||
busy = false;
|
busy = false;
|
||||||
|
busy.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,25 +267,6 @@ namespace Tools {
|
||||||
//* Return current time in <strf> format
|
//* Return current time in <strf> format
|
||||||
string strf_time(const string& strf);
|
string strf_time(const string& strf);
|
||||||
|
|
||||||
//* Waits for <atom> to not be <val>
|
|
||||||
#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
|
|
||||||
inline void atomic_wait_set(std::atomic<bool>& atom, bool val=true){
|
|
||||||
atomic_wait(atom, val);
|
|
||||||
atom = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Simple logging implementation
|
//* Simple logging implementation
|
||||||
|
|
Loading…
Reference in a new issue