Added menu system

This commit is contained in:
aristocratos 2021-09-12 15:58:23 +02:00
parent db96a20e16
commit 8583a8a2ef
15 changed files with 1308 additions and 362 deletions

View file

@ -16,13 +16,13 @@ ifeq ($(ARCH),unknown)
ARCH := $(shell uname -m || echo unknown)
endif
ifeq ($(ARCH),x86_64)
ADDFLAGS = -fcf-protection
ADDFLAGS := -fcf-protection
endif
#? Make sure PLATFORM Darwin is OSX and not Darwin
ifeq ($(PLATFORM),Darwin)
ifeq ($(shell sw_vers >/dev/null 2>&1; echo $$?),0)
PLATFORM = OSX
PLATFORM := OSX
endif
endif
@ -32,17 +32,30 @@ override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
#? Try to make sure we are using GCC/G++ version 11 or later if not instructed to use g++-10
ifneq ($(CXX),g++-10)
V_MAJOR = $(shell echo $(CXX_VERSION) | cut -f1 -d".")
V_MAJOR := $(shell echo $(CXX_VERSION) | cut -f1 -d".")
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
override CXX = g++-11
override CXX := g++-11
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
endif
endif
endif
#? Pull in platform specific source files and get thread count
ifeq ($(PLATFORM),Linux)
PLATFORM_DIR := linux
THREADS := $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
else ifeq ($(PLATFORM),FreeBSD)
PLATFORM_DIR := freebsd
THREADS := $(shell getconf NPROCESSORS_ONLN 2>/dev/null || echo 1)
else ifeq ($(PLATFORM),OSX)
PLATFORM_DIR := osx
THREADS := $(shell sysctl -n hw.ncpu || echo 1)
else
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
endif
#? Use all CPU cores (will only be set if using Make 4.3+)
THREADS := $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
MAKEFLAGS := --jobs=$(THREADS)
ifeq ($(THREADS),1)
override THREADS := auto
@ -70,17 +83,6 @@ SU_GROUP := root
SOURCES := $(shell find $(SRCDIR) -maxdepth 1 -type f -name *.$(SRCEXT))
#? Pull in platform specific source files
ifeq ($(PLATFORM),Linux)
PLATFORM_DIR = linux
else ifeq ($(PLATFORM),FreeBSD)
PLATFORM_DIR = freebsd
else ifeq ($(PLATFORM),OSX)
PLATFORM_DIR = osx
else
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
endif
SOURCES += $(shell find $(SRCDIR)/$(PLATFORM_DIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))

View file

@ -55,11 +55,9 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const string Version = "0.5.0";
const string Version = "0.9.0";
int coreCount;
string banner;
size_t banner_width = 0;
string overlay;
string clock;
@ -106,7 +104,7 @@ void argumentParser(const int& argc, char **argv) {
<< endl;
exit(0);
}
if (is_in(argument, "-v", "--version")) {
else if (is_in(argument, "-v", "--version")) {
cout << "btop version: " << Global::Version << endl;
exit(0);
}
@ -236,105 +234,6 @@ void _signal_handler(const int sig) {
}
}
//* Generate the btop++ banner
void banner_gen() {
Global::banner.clear();
Global::banner_width = 0;
string b_color, bg, fg, oc, letter;
auto& lowcolor = Config::getB("lowcolor");
auto& tty_mode = Config::getB("tty_mode");
for (size_t z = 0; const auto& line : Global::Banner_src) {
if (const auto w = ulen(line[1]); w > Global::banner_width) Global::banner_width = w;
if (tty_mode) {
fg = (z > 2) ? "\x1b[31m" : "\x1b[91m";
bg = (z > 2) ? "\x1b[90m" : "\x1b[37m";
}
else {
fg = Theme::hex_to_color(line[0], lowcolor);
int bg_i = 120 - z * 12;
bg = Theme::dec_to_color(bg_i, bg_i, bg_i, lowcolor);
}
for (size_t i = 0; i < line[1].size(); i += 3) {
if (line[1][i] == ' ') {
letter = ' ';
i -= 2;
}
else
letter = line[1].substr(i, 3);
b_color = (letter == "") ? fg : bg;
if (b_color != oc) Global::banner += b_color;
Global::banner += letter;
oc = b_color;
}
if (++z < Global::Banner_src.size()) Global::banner += Mv::l(ulen(line[1])) + Mv::d(1);
}
Global::banner += Mv::r(18 - Global::Version.size())
+ (tty_mode ? "\x1b[0;40;37m" : Theme::dec_to_color(0,0,0, lowcolor, "bg")
+ Theme::dec_to_color(150, 150, 150, lowcolor))
+ Fx::i + "v" + Global::Version + Fx::ui;
}
bool update_clock() {
const auto& clock_format = Config::getS("clock_format");
if (not Cpu::shown or clock_format.empty()) return false;
static const unordered_flat_map<string, string> clock_custom_format = {
{"/user", Tools::username()},
{"/host", Tools::hostname()},
{"/uptime", ""}
};
static time_t c_time = 0;
static size_t clock_len = 0;
static string old_clock;
string new_clock;
if (auto n_time = time(NULL); n_time == c_time)
return false;
else {
c_time = n_time;
new_clock = Tools::strf_time(clock_format);
if (new_clock == old_clock) return false;
old_clock = new_clock;
}
auto& out = Global::clock;
const auto& cpu_bottom = Config::getB("cpu_bottom");
const auto& x = Cpu::x;
const auto y = (cpu_bottom ? Cpu::y + Cpu::height - 1 : Cpu::y);
const auto& width = Cpu::width;
const auto& title_left = (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
const auto& title_right = (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
for (const auto& [c_format, replacement] : clock_custom_format) {
if (s_contains(new_clock, c_format)) {
if (c_format == "/uptime") {
string upstr = sec_to_dhms(system_uptime());
if (upstr.size() > 8) upstr.resize(upstr.size() - 3);
new_clock = s_replace(new_clock, c_format, upstr);
}
else {
new_clock = s_replace(new_clock, c_format, replacement);
}
}
}
new_clock = uresize(new_clock, std::max(0, width - 56));
out.clear();
if (new_clock.size() != clock_len) {
if (not Global::resized and clock_len > 0) out = Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + Symbols::h_line * clock_len;
clock_len = new_clock.size();
}
out += Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + title_left
+ Theme::c("title") + Fx::b + new_clock + Theme::c("cpu_box") + Fx::ub + title_right;
return true;
}
//* Manages secondary thread for collection and drawing of boxes
namespace Runner {
atomic<bool> active (false);
@ -366,6 +265,7 @@ namespace Runner {
};
string output;
bool pause_output = false;
sigset_t mask;
pthread_t runner_id;
pthread_mutex_t mtx;
@ -407,6 +307,7 @@ namespace Runner {
bitset<8> box_mask;
bool no_update;
bool force_redraw;
bool background_update;
string overlay;
string clock;
};
@ -464,6 +365,7 @@ namespace Runner {
//! DEBUG stats
if (Global::debug) {
if (debug_bg.empty()) Runner::debug_bg = Draw::createBox(2, 2, 32, 8, "", true, "debug");
debug_times.clear();
debug_times["total"] = {0, 0};
}
@ -499,7 +401,7 @@ namespace Runner {
if (Global::debug) debug_timer("proc", draw_begin);
//? Draw box
output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
if (not pause_output) output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("proc", draw_done);
}
@ -527,7 +429,7 @@ namespace Runner {
if (Global::debug) debug_timer("net", draw_begin);
//? Draw box
output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
if (not pause_output) output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("net", draw_done);
}
@ -555,7 +457,7 @@ namespace Runner {
if (Global::debug) debug_timer("mem", draw_begin);
//? Draw box
output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
if (not pause_output) output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("mem", draw_done);
}
@ -583,7 +485,7 @@ namespace Runner {
if (Global::debug) debug_timer("cpu", draw_begin);
//? Draw box
output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
if (not pause_output) output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("cpu", draw_done);
}
@ -606,8 +508,11 @@ namespace Runner {
continue;
}
if (not pause_output) output += conf.clock;
if (not conf.overlay.empty() and not conf.background_update) pause_output = true;
//! DEBUG stats -->
if (Global::debug) {
if (Global::debug and not Menu::active) {
output += debug_bg + Theme::c("title") + Fx::b + ljust(" Box", 9) + ljust("Collect μs", 12, true) + ljust("Draw μs", 9, true) + Theme::c("main_fg") + Fx::ub;
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
if (not debug_times.contains(name)) debug_times[name] = {0,0};
@ -619,8 +524,8 @@ namespace Runner {
//? If overlay isn't empty, print output without color and then print overlay on top
cout << Term::sync_start << (conf.overlay.empty()
? output + conf.clock
: Fx::ub + Theme::c("inactive_fg") + Fx::uncolor(output + conf.clock) + conf.overlay)
? output
: (output.empty() ? "" : Fx::ub + Theme::c("inactive_fg") + Fx::uncolor(output)) + conf.overlay)
<< Term::sync_end << flush;
}
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
@ -654,7 +559,9 @@ namespace Runner {
box_mask |= box_bits.at(box);
}
current_conf = {box_mask, no_update, force_redraw, Global::overlay, Global::clock};
current_conf = {box_mask, no_update, force_redraw, (not Config::getB("tty_mode") and Config::getB("background_update")), Global::overlay, Global::clock};
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
thread_trigger();
@ -826,13 +733,6 @@ int main(int argc, char **argv) {
exit(1);
}
if (Global::debug) {
Runner::debug_bg = Draw::createBox(2, 2, 32, 8, "", true, "debug");
}
//? Create the btop++ banner
banner_gen();
//? Calculate sizes of all boxes
Draw::calcSizes();
@ -867,7 +767,7 @@ int main(int argc, char **argv) {
//? Trigger secondary thread to redraw if terminal has been resized
if (Global::resized) {
Draw::calcSizes();
update_clock();
Draw::update_clock();
Global::resized = false;
if (Menu::active) Menu::process();
else Runner::run("all", true, true);
@ -875,7 +775,7 @@ int main(int argc, char **argv) {
}
//? Update clock if needed
if (update_clock() and not Menu::active) {
if (Draw::update_clock() and not Menu::active) {
Runner::run("clock");
}
@ -912,7 +812,7 @@ int main(int argc, char **argv) {
}
}
catch (std::exception& e) {
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
exit(1);
}

View file

@ -39,7 +39,8 @@ namespace Config {
bool write_new;
const vector<array<string, 2>> descriptions = {
{"color_theme", "#* Full path to a bashtop/bpytop/btop++ formatted \".theme\" file, \"Default\" and \"TTY\" for builtin themes."},
{"color_theme", "#* Name of a btop++/bpytop/bashtop formatted \".theme\" file, \"Default\" and \"TTY\" for builtin themes.\n"
"#* Themes should be placed in \"../share/btop/themes\" relative to binary or \"$HOME/.config/btop/themes\""},
{"theme_background", "#* If the theme set background should be shown, set to False if you want terminal background transparency."},
@ -139,9 +140,9 @@ namespace Config {
{"use_fstab", "#* Read disks list from /etc/fstab. This also disables only_physical."},
{"show_io_stat", "#* Toggles if io stats should be shown in regular disk usage view."},
{"show_io_stat", "#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view."},
{"io_mode", "#* Toggles io mode for disks, showing only big graphs for disk read/write speeds."},
{"io_mode", "#* Toggles io mode for disks, showing big graphs for disk read/write speeds."},
{"io_graph_combined", "#* Set to True to show combined read/write io graphs in io mode."},
@ -160,7 +161,7 @@ namespace Config {
{"show_battery", "#* Show battery stats in top right if battery is present."},
{"log_level", "#* Set loglevel for \"~/.config/bpytop/error.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
{"log_level", "#* Set loglevel for \"~/.config/btop/error.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."}
};
@ -260,6 +261,98 @@ namespace Config {
locked = true;
}
string validError;
bool intValid(const string& name, const string& value) {
int i_value;
try {
i_value = stoi(value);
}
catch (const std::invalid_argument&) {
validError = "Invalid numerical value!";
return false;
}
catch (const std::out_of_range&) {
validError = "Value out of range!";
return false;
}
if (name == "update_ms" and i_value < 100)
validError = "Config value update_ms set too low (<100).";
else if (name == "update_ms" and i_value > 86400000)
validError = "Config value update_ms set too high (>86400000).";
else
return true;
return false;
}
bool stringValid(const string& name, const string& value) {
if (name == "log_level" and not v_contains(Logger::log_levels, value))
validError = "Invalid log_level: " + value;
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
validError = "Invalid graph symbol identifier: " + value;
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
validError = "Invalid graph symbol identifier for" + name + ": " + value;
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
validError = "Invalid box name(s) in shown_boxes!";
else if (name == "cpu_core_map") {
const auto maps = ssplit(value);
bool all_good = true;
for (const auto& map : maps) {
const auto map_split = ssplit(map, ':');
if (map_split.size() != 2)
all_good = false;
else if (not isint(map_split.at(0)) or not isint(map_split.at(1)))
all_good = false;
if (not all_good) {
validError = "Invalid formatting of cpu_core_map!";
return false;
}
}
return true;
}
else if (name == "io_graph_speeds") {
const auto maps = ssplit(value);
bool all_good = true;
for (const auto& map : maps) {
const auto map_split = ssplit(map, ':');
if (map_split.size() != 2)
all_good = false;
else if (map_split.at(0).empty() or not isint(map_split.at(1)))
all_good = false;
if (not all_good) {
validError = "Invalid formatting of io_graph_speeds!";
return false;
}
}
return true;
}
else
return true;
return false;
}
string getAsString(const string& name) {
if (bools.contains(name))
return (bools.at(name) ? "True" : "False");
else if (ints.contains(name))
return to_string(ints.at(name));
else if (strings.contains(name))
return strings.at(name);
return "";
}
void flip(const string& name) {
if (_locked(name)) {
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
@ -369,9 +462,8 @@ namespace Config {
cread >> value;
if (not isint(value))
load_warnings.push_back("Got an invalid integer value for config name: " + name);
else if (name == "update_ms" and stoi(value) < 100) {
load_warnings.push_back("Config value update_ms set too low (<100), setting (100).");
ints.at(name) = 100;
else if (not intValid(name, value)) {
load_warnings.push_back(validError);
}
else
ints.at(name) = stoi(value);
@ -383,14 +475,8 @@ namespace Config {
}
else cread >> value;
if (name == "log_level" and not v_contains(Logger::log_levels, value))
load_warnings.push_back("Invalid log_level: " + value);
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
load_warnings.push_back("Invalid graph symbol identifier: " + value);
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
load_warnings.push_back("Invalid graph symbol identifier for" + name + ": " + value);
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
load_warnings.push_back("Invalid box name(s) in shown_boxes: " + value);
if (not stringValid(name, value))
load_warnings.push_back(validError);
else
strings.at(name) = value;
}

View file

@ -39,7 +39,9 @@ namespace Config {
extern unordered_flat_map<string, int> intsTmp;
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
const vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
const vector<string> temp_scales = { "celsius", "fahrenheit", "kelvin", "rankine" };
extern vector<string> current_boxes;
@ -60,6 +62,13 @@ namespace Config {
//* Return string for config key <name>
inline const string& getS(const string& name) { return strings.at(name); }
string getAsString(const string& name);
extern string validError;
bool intValid(const string& name, const string& value);
bool stringValid(const string& name, const string& value);
//* Set config key <name> to bool <value>
inline void set(const string& name, const bool& value) {
if (_locked(name)) boolsTmp.insert_or_assign(name, value);

View file

@ -90,8 +90,49 @@ namespace Symbols {
namespace Draw {
string banner_gen(int y, int x, bool centered, bool redraw) {
static string banner;
static size_t width = 0;
if (redraw) banner.clear();
if (banner.empty()) {
string b_color, bg, fg, oc, letter;
auto& lowcolor = Config::getB("lowcolor");
auto& tty_mode = Config::getB("tty_mode");
for (size_t z = 0; const auto& line : Global::Banner_src) {
if (const auto w = ulen(line[1]); w > width) width = w;
if (tty_mode) {
fg = (z > 2) ? "\x1b[31m" : "\x1b[91m";
bg = (z > 2) ? "\x1b[90m" : "\x1b[37m";
}
else {
fg = Theme::hex_to_color(line[0], lowcolor);
int bg_i = 120 - z * 12;
bg = Theme::dec_to_color(bg_i, bg_i, bg_i, lowcolor);
}
for (size_t i = 0; i < line[1].size(); i += 3) {
if (line[1][i] == ' ') {
letter = ' ';
i -= 2;
}
else
letter = line[1].substr(i, 3);
b_color = (letter == "") ? fg : bg;
if (b_color != oc) banner += b_color;
banner += letter;
oc = b_color;
}
if (++z < Global::Banner_src.size()) banner += Mv::l(ulen(line[1])) + Mv::d(1);
}
banner += Mv::r(18 - Global::Version.size())
+ Theme::c("main_fg") + Fx::b + Fx::i + "v" + Global::Version + Fx::reset;
}
if (redraw) return "";
return (centered ? Mv::to(y, Term::width / 2 - width / 2) : Mv::to(y, x)) + banner;
}
TextEdit::TextEdit() {}
TextEdit::TextEdit(string text) : text(text) {
TextEdit::TextEdit(string text, bool numeric) : numeric(numeric), text(text) {
pos = this->text.size();
upos = ulen(this->text);
}
@ -127,11 +168,12 @@ namespace Draw {
const string first = uresize(text, upos + 1);
text = uresize(first, ulen(first) - 1) + text.substr(first.size());
}
else if (key == "space") {
else if (key == "space" and not numeric) {
text.insert(pos++, 1, ' ');
upos++;
}
else if (ulen(key) == 1) {
else if (ulen(key) == 1 and text.size() < text.max_size() - 20) {
if (numeric and not isint(key)) return false;
if (key.size() == 1) {
text.insert(pos++, 1, key.at(0));
upos++;
@ -171,6 +213,10 @@ namespace Draw {
return text.substr(0, pos) + Fx::bl + "" + Fx::ubl + text.substr(pos);
}
void TextEdit::clear() {
this->text.clear();
}
string createBox(const int x, const int y, const int width, const int height, string line_color, const bool fill, const string title, const string title2, const int num) {
string out;
if (line_color.empty()) line_color = Theme::c("div_line");
@ -215,6 +261,69 @@ namespace Draw {
return out + Fx::reset + Mv::to(y + 1, x + 1);
}
bool update_clock() {
const auto& clock_format = Config::getS("clock_format");
if (not Cpu::shown or clock_format.empty()) {
if (clock_format.empty() and not Global::clock.empty()) Global::clock.clear();
return false;
}
static const unordered_flat_map<string, string> clock_custom_format = {
{"/user", Tools::username()},
{"/host", Tools::hostname()},
{"/uptime", ""}
};
static time_t c_time = 0;
static size_t clock_len = 0;
static string old_clock;
string new_clock;
if (auto n_time = time(NULL); n_time == c_time)
return false;
else {
c_time = n_time;
new_clock = Tools::strf_time(clock_format);
if (new_clock == old_clock) return false;
old_clock = new_clock;
}
auto& out = Global::clock;
const auto& cpu_bottom = Config::getB("cpu_bottom");
const auto& x = Cpu::x;
const auto y = (cpu_bottom ? Cpu::y + Cpu::height - 1 : Cpu::y);
const auto& width = Cpu::width;
const auto& title_left = (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
const auto& title_right = (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
for (const auto& [c_format, replacement] : clock_custom_format) {
if (s_contains(new_clock, c_format)) {
if (c_format == "/uptime") {
string upstr = sec_to_dhms(system_uptime());
if (upstr.size() > 8) upstr.resize(upstr.size() - 3);
new_clock = s_replace(new_clock, c_format, upstr);
}
else {
new_clock = s_replace(new_clock, c_format, replacement);
}
}
}
new_clock = uresize(new_clock, std::max(0, width - 56));
out.clear();
if (new_clock.size() != clock_len) {
if (not Global::resized and clock_len > 0) out = Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + Symbols::h_line * clock_len;
clock_len = new_clock.size();
}
out += Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + title_left
+ Theme::c("title") + Fx::b + new_clock + Theme::c("cpu_box") + Fx::ub + title_right;
return true;
}
//* Meter class ------------------------------------------------------------------------------------------------------------>
Meter::Meter() {}
@ -294,7 +403,7 @@ namespace Draw {
for (const int& i : iota(1, height + 1)) {
if (i > 1) out += Mv::d(1) + Mv::l(width);
if (not color_gradient.empty())
out += (invert) ? Theme::g(color_gradient).at((i - 1) * 100 / (height - 1)) : Theme::g(color_gradient).at(100 - (i * 100 / height));
out += (invert) ? Theme::g(color_gradient).at(i * 100 / height) : Theme::g(color_gradient).at(100 - ((i - 1) * 100 / height));
out += (invert) ? graphs.at(current).at(height - i) : graphs.at(current).at(i-1);
}
}
@ -374,7 +483,7 @@ namespace Cpu {
auto& graph_lo_field = Config::getS("cpu_graph_lower");
auto& tty_mode = Config::getB("tty_mode");
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_cpu"));
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(1);
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
auto& temp_scale = Config::getS("temp_scale");
string out;
out.reserve(width * height);
@ -540,7 +649,7 @@ namespace Mem {
auto& use_graphs = Config::getB("mem_graphs");
auto& tty_mode = Config::getB("tty_mode");
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_mem"));
// auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(1);
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
string out;
out.reserve(height * width);
@ -592,7 +701,7 @@ namespace Mem {
for (const auto& [name, disk] : mem.disks) {
if (disk.io_read.empty()) continue;
io_graphs[name + "_activity"] = Draw::Graph{disks_width - 6, 1, "available", disk.io_activity, graph_symbol, false, true};
io_graphs[name + "_activity"] = Draw::Graph{disks_width - 6, 1, "", disk.io_activity, graph_symbol};
if (io_mode) {
//? Create one combined graph for IO read/write if enabled
@ -672,7 +781,7 @@ namespace Mem {
//? Disks
if (show_disks) {
const auto& disks = mem.disks;
cx = x + mem_width - 1; cy = 0;
cx = mem_width; cy = 0;
const bool big_disk = disks_width >= 25;
divider = Mv::l(1) + Theme::c("div_line") + Symbols::div_left + Symbols::h_line * disks_width + Theme::c("mem_box") + Fx::ub + Symbols::div_right + Mv::l(disks_width);
if (io_mode) {
@ -688,7 +797,8 @@ namespace Mem {
const string used_percent = to_string(disk.used_percent);
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2)) + Theme::c("main_fg") + used_percent + '%';
}
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same);
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6) + Theme::g("available").at(max(50ll, disk.io_activity.back()))
+ Mv::l(disks_width - 6) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same) + Theme::c("main_fg");
if (++cy > height - 3) break;
if (io_graph_combined) {
auto comb_val = disk.io_read.back() + disk.io_write.back();
@ -728,7 +838,8 @@ namespace Mem {
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)human_io.size() / 2)) + Theme::c("main_fg") + human_io;
if (++cy > height - 3) break;
if (show_io_stat and io_graphs.contains(mount + "_activity")) {
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same);
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6) + Theme::g("available").at(max(50ll, disk.io_activity.back()))
+ Mv::l(disks_width - 6) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same) + Theme::c("main_fg");
if (not big_disk) out += Mv::to(y+1+cy, x+cx) + Theme::c("main_fg") + human_io;
if (++cy > height - 3) break;
}
@ -938,7 +1049,7 @@ namespace Proc {
auto& proc_colors = Config::getB("proc_colors");
auto& tty_mode = Config::getB("tty_mode");
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_proc"));
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(1);
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
auto& mem_bytes = Config::getB("proc_mem_bytes");
start = Config::getI("proc_start");
selected = Config::getI("proc_selected");
@ -1330,10 +1441,13 @@ namespace Draw {
Proc::box.clear();
Global::clock.clear();
Global::overlay.clear();
Runner::pause_output = false;
Runner::debug_bg.clear();
Proc::p_counters.clear();
Proc::p_graphs.clear();
if (Menu::active) Menu::redraw = true;
Input::mouse_mappings.clear();
Menu::mouse_mappings.clear();
Cpu::x = Mem::x = Net::x = Proc::x = 1;
Cpu::y = Mem::y = Net::y = Proc::y = 1;

View file

@ -57,21 +57,28 @@ namespace Symbols {
namespace Draw {
//* Generate if needed and return the btop++ banner
string banner_gen(int y=0, int x=0, bool centered=false, bool redraw=false);
//* An editable text field
class TextEdit {
size_t pos = 0;
size_t upos = 0;
bool numeric;
public:
string text;
TextEdit();
TextEdit(string text);
TextEdit(string text, bool numeric=false);
bool command(const string& key);
string operator()(const size_t limit=0);
void clear();
};
//* Create a box and return as a string
string createBox(const int x, const int y, const int width, const int height, string line_color="", const bool fill=false, const string title="", const string title2="", const int num=0);
bool update_clock();
//* Class holding a percentage meter
class Meter {
int width;
@ -121,9 +128,10 @@ namespace Draw {
//* Calculate sizes of boxes, draw outlines and save to enabled boxes namespaces
void calcSizes();
}
namespace Proc {
extern Draw::TextEdit filter;
extern unordered_flat_map<size_t, Draw::Graph> p_graphs;
extern unordered_flat_map<size_t, int> p_counters;
}

View file

@ -189,6 +189,21 @@ namespace Input {
if (str_to_lower(key) == "q") {
exit(0);
}
else if (is_in(key, "escape", "m")) {
Menu::menuMask.set(Menu::Main);
Menu::process();
return;
}
else if (is_in(key, "F1", "h")) {
Menu::menuMask.set(Menu::Help);
Menu::process();
return;
}
else if (is_in(key, "F2", "o")) {
Menu::menuMask.set(Menu::Options);
Menu::process();
return;
}
else if (is_in(key, "1", "2", "3", "4")) {
atomic_wait(Runner::active);
static const array<string, 4> boxes = {"cpu", "mem", "net", "proc"};
@ -330,6 +345,7 @@ namespace Input {
return;
}
else if (key == "s" and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
if (Term::width < 80 or Term::height < 20) return;
atomic_wait(Runner::active);
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
Menu::menuMask.set(Menu::SignalChoose);

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,7 @@ namespace Menu {
string box_contents, button_left, button_right;
int height = 0, width = 0, boxtype = 0, selected = 0, x = 0, y = 0;
public:
enum BoxTypes { OK, YES_NO, NO_YES };
enum msgReturn {
Invalid,
Ok_Yes,
@ -52,7 +53,7 @@ namespace Menu {
Select
};
msgBox();
msgBox(int width, int boxtype, vector<string>& content, string title);
msgBox(int width, int boxtype, vector<string> content, string title);
//? Draw and return box as a string
string operator()();
@ -65,7 +66,7 @@ namespace Menu {
};
extern bitset<8> menuMask;
//* Enum for functions in vector menuFuncs
enum Menus {
SignalChoose,

View file

@ -33,6 +33,7 @@ void term_resize(bool force=false);
void banner_gen();
namespace Global {
extern const vector<array<string, 2>> Banner_src;
extern const string Version;
extern atomic<bool> quitting;
extern string exit_error_msg;
@ -49,6 +50,8 @@ namespace Runner {
extern atomic<bool> reading;
extern atomic<bool> stopping;
extern pthread_t runner_id;
extern bool pause_output;
extern string debug_bg;
void run(const string& box="", const bool no_update=false, const bool force_redraw=false);
void stop();
@ -74,6 +77,8 @@ namespace Cpu {
extern int x, y, width, height, min_width, min_height;
extern bool shown, redraw, got_sensors, cpu_temp_only;
extern string cpuName, cpuHz;
extern vector<string> available_fields;
extern vector<string> available_sensors;
struct cpu_info {
unordered_flat_map<string, deque<long long>> cpu_percent = {

View file

@ -345,6 +345,8 @@ namespace Theme {
rgbs.clear();
gradients.clear();
colors = TTY_theme;
if (not Config::getB("theme_background"))
colors["main_bg"] = "\x1b[49m";
for (const auto& c : colors) {
if (not c.first.ends_with("_start")) continue;
@ -401,10 +403,10 @@ namespace Theme {
themes.push_back("Default");
themes.push_back("TTY");
for (const auto& path : { theme_dir, user_theme_dir } ) {
for (const auto& path : { user_theme_dir, theme_dir } ) {
if (path.empty()) continue;
for (auto& file : fs::directory_iterator(path)) {
if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1) {
if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1 and not v_contains(themes, file.path().c_str())) {
themes.push_back(file.path().c_str());
}
}
@ -413,11 +415,18 @@ namespace Theme {
}
void setTheme() {
string theme = Config::getS("color_theme");
const auto& theme = Config::getS("color_theme");
fs::path theme_path;
for (const fs::path p : themes) {
if (p == theme or p.stem() == theme or p.filename() == theme) {
theme_path = p;
break;
}
}
if (theme == "TTY" or Config::getB("tty_mode"))
generateTTYColors();
else {
generateColors((theme == "Default" ? Default_theme : loadFile(theme)));
generateColors((theme == "Default" or theme_path.empty() ? Default_theme : loadFile(theme_path)));
generateGradients();
}
Term::fg = colors.at("main_fg");

View file

@ -46,6 +46,7 @@ namespace Term {
atomic<int> width = 0;
atomic<int> height = 0;
string current_tty;
char* custombuf;
namespace {
struct termios initial_settings;
@ -107,11 +108,21 @@ namespace Term {
if (initialized) {
tcgetattr(STDIN_FILENO, &initial_settings);
current_tty = (string)ttyname(STDIN_FILENO);
//? Disable stream sync
cin.sync_with_stdio(false);
cout.sync_with_stdio(false);
//? Disable stream ties
cin.tie(NULL);
cout.tie(NULL);
echo(false);
linebuffered(false);
refresh();
//? Set 1MB buffer for cout
std::cout.rdbuf()->pubsetbuf(custombuf, 1048576);
cout << alt_screen << hide_cursor << mouse_on << flush;
Global::resized = false;
}
@ -169,11 +180,10 @@ namespace Tools {
}
string s_replace(const string& str, const string& from, const string& to) {
size_t start_pos = str.find(from);
if(start_pos == std::string::npos)
return str;
string out = str;
out.replace(start_pos, from.length(), to);
for (size_t start_pos = out.find(from); start_pos != std::string::npos; start_pos = out.find(from)) {
out.replace(start_pos, from.length(), to);
}
return out;
}

View file

@ -214,9 +214,9 @@ namespace Tools {
return is_in(str, "true", "True");
}
//* Check if a string is a valid integer value
//* Check if a string is a valid integer value (only postive)
inline bool isint(const string& str) {
return all_of(str.begin() + (str[0] == '-' ? 1 : 0), str.end(), ::isdigit);
return all_of(str.begin(), str.end(), ::isdigit);
}
//* Left-trim <t_str> from <str> and return new string

View file

@ -43,6 +43,7 @@ namespace Cpu {
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields;
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
bool got_sensors = false, cpu_temp_only = false;
@ -133,6 +134,9 @@ namespace Shared {
}
Cpu::cpuName = Cpu::get_cpuName();
Cpu::got_sensors = Cpu::get_sensors();
for (const auto& [sensor, ignored] : Cpu::found_sensors) {
Cpu::available_sensors.push_back(sensor);
}
Cpu::core_mapping = Cpu::get_core_mapping();
//? Init for namespace Mem

View file

@ -1,100 +0,0 @@
#Bashtop theme with default colors and black background
#by aristocratos
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
# example for white: "#FFFFFF", "#ff" or "255 255 255".
# All graphs and meters can be gradients
# For single color graphs leave "mid" and "end" variable empty.
# Use "start" and "end" variables for two color gradient
# Use "start", "mid" and "end" for three color gradient
# Main background, empty for terminal default, need to be empty if you want transparent background
theme[main_bg]="#00"
# Main text color
theme[main_fg]="#cc"
# Title color for boxes
theme[title]="#ee"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#90"
# Background color of selected item in processes box
theme[selected_bg]="#7e2626"
# Foreground color of selected item in processes box
theme[selected_fg]="#ee"
# Color of inactive/disabled text
theme[inactive_fg]="#40"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#60"
# Background color of the percentage meters
theme[meter_bg]="#40"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#0de756"
# Cpu box outline color
theme[cpu_box]="#3d7b46"
# Memory/disks box outline color
theme[mem_box]="#8a882e"
# Net up/down box outline color
theme[net_box]="#423ba5"
# Processes box outline color
theme[proc_box]="#923535"
# Box divider line and small boxes line color
theme[div_line]="#30"
# Temperature graph colors
theme[temp_start]="#4897d4"
theme[temp_mid]="#5474e8"
theme[temp_end]="#ff40b6"
# CPU graph colors
theme[cpu_start]="#50f095"
theme[cpu_mid]="#f2e266"
theme[cpu_end]="#fa1e1e"
# Mem/Disk free meter
theme[free_start]="#223014"
theme[free_mid]="#b5e685"
theme[free_end]="#dcff85"
# Mem/Disk cached meter
theme[cached_start]="#0b1a29"
theme[cached_mid]="#74e6fc"
theme[cached_end]="#26c5ff"
# Mem/Disk available meter
theme[available_start]="#292107"
theme[available_mid]="#ffd77a"
theme[available_end]="#ffb814"
# Mem/Disk used meter
theme[used_start]="#3b1f1c"
theme[used_mid]="#d9626d"
theme[used_end]="#ff4769"
# Download graph colors
theme[download_start]="#231a63"
theme[download_mid]="#4f43a3"
theme[download_end]="#b0a9de"
# Upload graph colors
theme[upload_start]="#510554"
theme[upload_mid]="#7d4180"
theme[upload_end]="#dcafde"
# Process box color gradient for threads, mem and cpu usage
theme[process_start]="#80d0a3"
theme[process_mid]="#dcd179"
theme[process_end]="#d45454"