mirror of https://github.com/aristocratos/btop.git
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
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
281
src/btop.cpp
281
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;
|
||||
//* ------------------------------------------------ MAIN LOOP ----------------------------------------------------
|
||||
|
||||
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();
|
||||
|
||||
|
||||
|
||||
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));
|
||||
try {
|
||||
while (true) {
|
||||
if (Global::thread_exception) clean_quit(1);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
catch (std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
|
||||
Logger::error(Global::exit_error_msg);
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -47,4 +47,7 @@ namespace Input {
|
|||
//* Clears last entered key
|
||||
void clear();
|
||||
|
||||
//* Process actions for input <key>
|
||||
void process(const std::string key);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue