summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraristocratos <gnmjpl@gmail.com>2021-07-18 15:44:32 +0200
committeraristocratos <gnmjpl@gmail.com>2021-07-18 15:44:32 +0200
commit0c1feb909e9ca0719d47be3059de4b5fa1cc416a (patch)
treed2dde0868f08b0c44c70dfdfd885879b242747cf
parent1121978214b43214df348eb75b9a5e5a8b416d18 (diff)
Added Input::process for input actions and Runner:: namespace for multithreading collection and drawing
-rw-r--r--Makefile18
-rw-r--r--README.md2
-rw-r--r--src/btop.cpp269
-rw-r--r--src/btop_config.cpp48
-rw-r--r--src/btop_config.hpp8
-rw-r--r--src/btop_draw.cpp36
-rw-r--r--src/btop_input.cpp61
-rw-r--r--src/btop_input.hpp3
-rw-r--r--src/btop_linux.cpp42
-rw-r--r--src/btop_shared.hpp67
-rw-r--r--src/btop_tools.cpp4
-rw-r--r--src/btop_tools.hpp19
12 files changed, 360 insertions, 217 deletions
diff --git a/Makefile b/Makefile
index 922fea5..30c9d60 100644
--- a/Makefile
+++ b/Makefile
@@ -4,17 +4,11 @@ DOCDIR ?= $(PREFIX)/share/btop/doc
#Compiler and Linker
CXX := g++
-#If using g++ try to make sure we are using version 11 or 10
-ifeq ($(CXX),g++)
- CXX_VERSION = $(shell $(CXX) -dumpversion)
- ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
- ifneq ($(shell command -v 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
+#Try to make sure we are using GCC/G++ version 11 or later
+CXX_VERSION = $(shell $(CXX) -dumpversion)
+ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
+ ifneq ($(shell command -v g++-11),)
+ CXX := g++-11
endif
endif
@@ -75,7 +69,7 @@ uninstall:
#Link
btop: $(OBJECTS)
- $(CXX) -o $(TARGETDIR)/btop $^
+ $(CXX) -o $(TARGETDIR)/btop $^ -pthread
#Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
diff --git a/README.md b/README.md
index 581c4bd..b8b9d83 100644
--- a/README.md
+++ b/README.md
@@ -126,7 +126,7 @@ Options menu.
#### 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)
diff --git a/src/btop.cpp b/src/btop.cpp
index 31fd07a..1a35181 100644
--- a/src/btop.cpp
+++ b/src/btop.cpp
@@ -81,6 +81,9 @@ namespace Global {
string banner;
size_t banner_width = 0;
+ string exit_error_msg;
+ atomic<bool> thread_exception (false);
+
fs::path self_path;
bool debuginit = false;
@@ -142,10 +145,7 @@ void argumentParser(int argc, char **argv){
void _resize(bool force=false){
if (Term::refresh(false) or force) {
Global::resized = true;
- if (Runner::active) {
- Runner::stop = true;
- atomic_wait(Runner::active);
- }
+ Runner::stop();
Term::refresh();
}
else return;
@@ -160,29 +160,23 @@ void _resize(bool force=false){
}
//* 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;
Global::quitting = true;
- if (Runner::active) {
- Runner::stop = true;
- atomic_wait(Runner::active);
- }
+ Runner::stop();
if (Term::initialized) {
Term::restore();
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
}
-
Config::write();
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);
}
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
void _sleep(){
- if (Runner::active) {
- Runner::stop = true;
- atomic_wait(Runner::active);
- }
+ Runner::stop();
if (Term::initialized) {
Term::restore();
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;
}
+//? Manages secondary thread for collection and drawing of boxes
namespace Runner {
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;
- 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
if (false) {
string key;
@@ -474,7 +535,7 @@ int main(int argc, char **argv){
exit(0);
}
-
+ //* Test graphs
if (false) {
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"};
- vector<int> vy;
-
- cout << v_contains(vv, "vad"s) << endl;
- 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();
-
+ //* ------------------------------------------------ MAIN LOOP ----------------------------------------------------
- string ostring;
- uint64_t tsl, timestamp2, rcount = 0;
+ uint64_t future_time = time_ms(), rcount = 0;
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;
- for (size_t i : iota(0, (int)Term::height - 19)){
- xc = 230 - i * 150 / (Term::height - 20);
- greyscale.push_back(Theme::dec_to_color(xc, xc, xc));
- }
- while (key != "q") {
- timestamp = time_micros();
- tsl = time_ms() + timer;
- 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();
+ try {
+ while (true) {
+ if (Global::thread_exception) clean_quit(1);
- ostring = Proc::draw(plist);
- Config::unlock();
+ if (Runner::has_output) {
+ cout << Term::sync_start << Runner::get_output() << Term::sync_end << flush;
- 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;
+ //! 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;
}
- 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));
+
+ if (time_ms() >= future_time) {
+ Runner::run();
+ future_time = time_ms() + Config::getI("update_ms");
}
- 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));
+
+ while (time_ms() < future_time and not Global::resized) {
+ if (Input::poll(future_time - time_ms()))
+ Input::process(Input::get());
+ else
+ break;
}
- 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;
+ if (Global::resized) {
+ // cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
+ Global::resized = false;
+ future_time = time_ms();
}
-
- 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;
+ }
+ catch (std::exception& e) {
+ Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
+ Logger::error(Global::exit_error_msg);
+ clean_quit(1);
}
diff --git a/src/btop_config.cpp b/src/btop_config.cpp
index 012b0c2..10f86e4 100644
--- a/src/btop_config.cpp
+++ b/src/btop_config.cpp
@@ -230,7 +230,7 @@ namespace Config {
vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
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())
write_new = true;
return locked.load();
@@ -240,17 +240,19 @@ namespace Config {
fs::path conf_dir;
fs::path conf_file;
+ vector<string> current_boxes;
+
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
- const bool& getB(string name){
+ const bool& getB(const string& name){
return bools.at(name);
}
- const int& getI(string name){
+ const int& getI(const string& name){
return ints.at(name);
}
- const string& getS(string name){
+ const string& getS(const string& name){
return strings.at(name);
}
@@ -278,13 +280,19 @@ namespace Config {
}
void lock(){
- atomic_wait_set(locked);
+ writelock.wait(true);
+ locked = true;
}
void unlock(){
if (not locked) return;
- atomic_wait(Runner::active);
- atomic_wait_set(writelock);
+ writelock = true;
+
+ 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){
strings.at(item.first) = item.second;
@@ -301,20 +309,17 @@ 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;
+ writelock.notify_all();
}
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;
}
+ current_boxes.swap(new_boxes);
return true;
}
@@ -342,10 +347,12 @@ namespace Config {
}
string name, value;
getline(cread, name, '=');
+ if (name.ends_with(' ')) name = trim(name);
if (not v_contains(valid_names, name)) {
cread.ignore(SSmax, '\n');
continue;
}
+ cread >> std::ws;
if (bools.contains(name)) {
cread >> value;
@@ -362,7 +369,6 @@ namespace Config {
ints.at(name) = stoi(value);
}
else if (strings.contains(name)) {
- cread >> std::ws;
if (cread.peek() == '"') {
cread.ignore(1);
getline(cread, value, '"');
@@ -393,10 +399,14 @@ namespace Config {
if (cwrite.good()) {
cwrite << "#? Config file for btop v. " << Global::Version;
for (auto [name, description] : descriptions) {
- cwrite << "\n\n" << (description.empty() ? "" : description + "\n") << name << "=";
- if (strings.contains(name)) 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 << "\n\n" << (description.empty() ? "" : description + "\n")
+ << name << " = ";
+ if (strings.contains(name))
+ 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();
}
diff --git a/src/btop_config.hpp b/src/btop_config.hpp
index 4769e30..609bb68 100644
--- a/src/btop_config.hpp
+++ b/src/btop_config.hpp
@@ -32,17 +32,19 @@ namespace Config {
extern const vector<string> valid_graph_symbols;
+ extern vector<string> current_boxes;
+
//* Check if string only contains space seperated valid names for boxes
bool check_boxes(string boxes);
//* Return bool for config key <name>
- const bool& getB(string name);
+ const bool& getB(const string& name);
//* Return integer for config key <name>
- const int& getI(string name);
+ const int& getI(const string& 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>
void set(string name, bool value);
diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp
index fbaca0e..49745ff 100644
--- a/src/btop_draw.cpp
+++ b/src/btop_draw.cpp
@@ -275,6 +275,16 @@ namespace Cpu {
bool shown = true, redraw = true;
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 {
@@ -285,6 +295,16 @@ namespace Mem {
bool shown = true, redraw = true;
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 {
@@ -296,6 +316,16 @@ namespace Net {
bool shown = true, redraw = true;
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 {
@@ -311,6 +341,7 @@ namespace Proc {
void selection(string cmd_key) {
auto start = Config::getI("proc_start");
auto selected = Config::getI("proc_selected");
+ int numpids = Proc::numpids;
if (cmd_key == "up" and selected > 0) {
if (start > 0 and selected == 1) start--;
else selected--;
@@ -340,7 +371,7 @@ namespace Proc {
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& filtering = Config::getB("proc_filtering");
auto& proc_tree = Config::getB("proc_tree");
@@ -352,9 +383,10 @@ namespace Proc {
uint64_t total_mem = 16328872 << 10;
int y = show_detailed ? Proc::y + 9 : Proc::y;
int height = show_detailed ? Proc::height - 9 : Proc::height;
+ int numpids = Proc::numpids;
string out;
//* If true, redraw elements not needed to be updated every cycle
- if (redraw) {
+ if (redraw or force_redraw) {
redraw = false;
out = box;
out += Mv::to(y, x) + Mv::r(12)
diff --git a/src/btop_input.cpp b/src/btop_input.cpp
index aa18b24..d992398 100644
--- a/src/btop_input.cpp
+++ b/src/btop_input.cpp
@@ -22,8 +22,10 @@ tab-size = 4
#include <btop_input.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;
namespace Input {
@@ -95,7 +97,7 @@ namespace Input {
string wait(){
while (cin.rdbuf()->in_avail() < 1) {
- if (interrupt) { interrupt = false; return string(); }
+ if (interrupt) { interrupt = false; return ""; }
sleep_ms(10);
}
return get();
@@ -105,4 +107,59 @@ namespace Input {
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());
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/btop_input.hpp b/src/btop_input.hpp
index adb4317..11ca095 100644
--- a/src/btop_input.hpp
+++ b/src/btop_input.hpp
@@ -47,4 +47,7 @@ namespace Input {
//* Clears last entered key
void clear();
+ //* Process actions for input <key>
+ void process(const std::string key);
+
} \ No newline at end of file
diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp
index 8fe4869..e35e5aa 100644
--- a/src/btop_linux.cpp
+++ b/src/btop_linux.cpp
@@ -93,10 +93,34 @@ namespace Shared {
namespace Cpu {
bool got_sensors = false;
string cpuName = "";
+
+ cpu_info current_cpu;
+
+ cpu_info& collect(const bool return_last) {
+ (void)return_last;
+ return current_cpu;
+ }
}
namespace Mem {
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 {
@@ -117,11 +141,9 @@ namespace Proc {
int counter = 0;
}
uint64_t old_cputimes = 0;
- int numpids = 0;
+ atomic<int> numpids = 0;
size_t reserve_pids = 500;
bool tree_state = false;
- atomic<bool> stop (false);
- atomic<bool> collecting (false);
vector<string> sort_vector = {
"pid",
"name",
@@ -150,6 +172,7 @@ namespace Proc {
//* 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){
+ if (Runner::stopping) return;
auto cur_pos = out_procs.size();
bool filtering = false;
@@ -290,9 +313,8 @@ namespace 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;
- atomic_wait_set(collecting);
auto& sorting = Config::getS("proc_sorting");
auto reverse = Config::getB("proc_reversed");
auto& filter = Config::getS("proc_filter");
@@ -345,12 +367,9 @@ namespace Proc {
//* Iterate over all pids in /proc
for (auto& d: fs::directory_iterator(Shared::proc_path)){
- if (pread.is_open()) pread.close();
- if (stop) {
- collecting = false;
- stop = false;
+ if (Runner::stopping)
return current_procs;
- }
+ if (pread.is_open()) pread.close();
string pid_str = d.path().filename();
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)) {
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter);
}
+
+ if (Runner::stopping) return current_procs;
procs.swap(tree_procs);
}
@@ -574,7 +595,6 @@ namespace Proc {
numpids = (int)procs.size();
current_procs.swap(procs);
reserve_pids = npids;
- collecting = false;
return current_procs;
}
}
diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp
index 4a7e86b..61627b0 100644
--- a/src/btop_shared.hpp
+++ b/src/btop_shared.hpp
@@ -24,11 +24,16 @@ tab-size = 4
#include <atomic>
#include <deque>
#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 {
extern const string Version;
+ extern string exit_error_msg;
+ extern atomic<bool> thread_exception;
extern int coreCount;
extern string banner;
}
@@ -36,7 +41,10 @@ namespace Global {
namespace Runner {
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 {
extern string box, cpuName;
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 {
extern string box;
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 {
extern string box;
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 {
- extern int numpids;
- extern atomic<bool> stop;
- extern atomic<bool> collecting;
+ extern atomic<int> numpids;
extern string box;
extern bool shown, redraw;
@@ -106,12 +157,12 @@ namespace Proc {
extern detail_container detailed;
- //* Collect and sort process information from /proc, saves and returns reference to Proc::current_procs;
- vector<proc_info>& collect(bool return_last=false);
+ //* Collect and sort process information from /proc
+ vector<proc_info>& collect(const 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);
+ string draw(const vector<proc_info>& plist, bool force_redraw=false);
}
diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp
index a58a396..cce168c 100644
--- a/src/btop_tools.cpp
+++ b/src/btop_tools.cpp
@@ -329,7 +329,8 @@ namespace Logger {
void log_write(const size_t level, const string& msg){
if (loglevel < level or logfile.empty()) return;
- atomic_wait_set(busy, true);
+ busy.wait(true);
+ busy = true;
std::error_code ec;
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
auto old_log = logfile;
@@ -345,5 +346,6 @@ namespace Logger {
}
else logfile.clear();
busy = false;
+ busy.notify_one();
}
}
diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp
index fdb4417..bd93681 100644
--- a/src/btop_tools.hpp
+++ b/src/btop_tools.hpp
@@ -267,25 +267,6 @@ namespace Tools {
//* Return current time in <strf> format
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