mirror of https://github.com/aristocratos/btop.git
Added functionality for text editing and slight modifications to default theme colors
This commit is contained in:
parent
f8005aeb21
commit
9ee9f3232d
24
src/btop.cpp
24
src/btop.cpp
|
@ -464,7 +464,13 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
//? Platform dependent init and error check
|
||||
Shared::init();
|
||||
try {
|
||||
Shared::init();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what();
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
//? Update list of available themes and generate the selected theme
|
||||
Theme::updateThemes();
|
||||
|
@ -561,19 +567,21 @@ int main(int argc, char **argv) {
|
|||
deque<long long> mydata;
|
||||
for (long long i = 0; i <= 100; i++) mydata.push_back(i);
|
||||
for (long long i = 100; i >= 0; i--) mydata.push_back(i);
|
||||
mydata.push_back(50);
|
||||
// mydata.push_back(50);
|
||||
auto mydata2 = mydata;
|
||||
mydata2.push_back(10);
|
||||
for (long long i = 0; i <= 100; i++) mydata2.push_back(i);
|
||||
for (long long i = 100; i >= 0; i--) mydata2.push_back(i);
|
||||
|
||||
|
||||
Draw::Graph kgraph {};
|
||||
Draw::Graph kgraph2 {};
|
||||
Draw::Graph kgraph3 {};
|
||||
|
||||
cout << Draw::createBox(5, 10, Term::width - 10, 12, Theme::c("proc_box"), false, "braille", "", 1) << Mv::save;
|
||||
cout << Draw::createBox(5, 23, Term::width - 10, 12, Theme::c("proc_box"), false, "block", "", 2);
|
||||
cout << Draw::createBox(5, 36, Term::width - 10, 12, Theme::c("proc_box"), false, "tty", "", 3) << flush;
|
||||
auto kts = time_micros();
|
||||
kgraph(Term::width - 13, 10, "cpu", mydata, "braille", false, false);
|
||||
kgraph2(Term::width - 13, 10, "cpu", mydata, "block", false, false);
|
||||
kgraph3(Term::width - 13, 10, "cpu", mydata, "tty", false, false);
|
||||
Draw::Graph kgraph {Term::width - 13, 10, "cpu", mydata, "block", false, false};
|
||||
Draw::Graph kgraph2 {Term::width - 13, 10, "upload", {0, 1}, "block", false, false};
|
||||
Draw::Graph kgraph3 {Term::width - 13, 10, "download", {}, "block", false, false};
|
||||
|
||||
|
||||
cout << Mv::restore << kgraph(mydata, true)
|
||||
|
|
|
@ -240,22 +240,12 @@ namespace Config {
|
|||
|
||||
vector<string> current_boxes;
|
||||
|
||||
void set(string name, bool value) {
|
||||
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
|
||||
else bools.at(name) = value;
|
||||
void lock() {
|
||||
writelock.wait(true);
|
||||
locked = true;
|
||||
}
|
||||
|
||||
void set(string name, int value) {
|
||||
if (_locked(name)) intsTmp.insert_or_assign(name, value);
|
||||
ints.at(name) = value;
|
||||
}
|
||||
|
||||
void set(string name, string value) {
|
||||
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
|
||||
else strings.at(name) = value;
|
||||
}
|
||||
|
||||
void flip(string name) {
|
||||
void flip(const string& name) {
|
||||
if (_locked(name)) {
|
||||
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
|
||||
else boolsTmp.insert_or_assign(name, (not bools.at(name)));
|
||||
|
@ -263,11 +253,6 @@ namespace Config {
|
|||
else bools.at(name) = not bools.at(name);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
writelock.wait(true);
|
||||
locked = true;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (not locked) return;
|
||||
writelock.wait(true);
|
||||
|
@ -305,7 +290,7 @@ namespace Config {
|
|||
writelock.notify_all();
|
||||
}
|
||||
|
||||
bool check_boxes(string boxes) {
|
||||
bool check_boxes(const string& boxes) {
|
||||
auto new_boxes = ssplit(boxes);
|
||||
for (auto& box : new_boxes) {
|
||||
if (not v_contains(valid_boxes, box)) return false;
|
||||
|
@ -314,7 +299,7 @@ namespace Config {
|
|||
return true;
|
||||
}
|
||||
|
||||
void load(fs::path conf_file, vector<string>& load_errors) {
|
||||
void load(const fs::path& conf_file, vector<string>& load_errors) {
|
||||
if (conf_file.empty())
|
||||
return;
|
||||
else if (not fs::exists(conf_file)) {
|
||||
|
|
|
@ -32,15 +32,20 @@ namespace Config {
|
|||
extern std::filesystem::path conf_file;
|
||||
|
||||
extern unordered_flat_map<string, string> strings;
|
||||
extern unordered_flat_map<string, string> stringsTmp;
|
||||
extern unordered_flat_map<string, bool> bools;
|
||||
extern unordered_flat_map<string, bool> boolsTmp;
|
||||
extern unordered_flat_map<string, int> ints;
|
||||
extern unordered_flat_map<string, int> intsTmp;
|
||||
|
||||
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
||||
|
||||
extern vector<string> current_boxes;
|
||||
|
||||
//* Check if string only contains space seperated valid names for boxes
|
||||
bool check_boxes(string boxes);
|
||||
bool check_boxes(const string& boxes);
|
||||
|
||||
bool _locked(const string& name);
|
||||
|
||||
//* Return bool for config key <name>
|
||||
inline const bool& getB(const string& name) { return bools.at(name); }
|
||||
|
@ -52,16 +57,25 @@ namespace Config {
|
|||
inline const string& getS(const string& name) { return strings.at(name); }
|
||||
|
||||
//* Set config key <name> to bool <value>
|
||||
void set(string name, bool value);
|
||||
inline void set(const string& name, const bool& value) {
|
||||
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
|
||||
else bools.at(name) = value;
|
||||
}
|
||||
|
||||
//* Set config key <name> to int <value>
|
||||
void set(string name, int value);
|
||||
inline void set(const string& name, const int& value) {
|
||||
if (_locked(name)) intsTmp.insert_or_assign(name, value);
|
||||
ints.at(name) = value;
|
||||
}
|
||||
|
||||
//* Set config key <name> to string <value>
|
||||
void set(string name, string value);
|
||||
inline void set(const string& name, const string& value) {
|
||||
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
|
||||
else strings.at(name) = value;
|
||||
}
|
||||
|
||||
//* Flip config key bool <name>
|
||||
void flip(string name);
|
||||
void flip(const string& name);
|
||||
|
||||
//* Lock config and cache changes until unlocked
|
||||
void lock();
|
||||
|
@ -70,8 +84,15 @@ namespace Config {
|
|||
void unlock();
|
||||
|
||||
//* Load the config file from disk
|
||||
void load(std::filesystem::path conf_file, vector<string>& load_errors);
|
||||
void load(const std::filesystem::path& conf_file, vector<string>& load_errors);
|
||||
|
||||
//* Write the config file to disk
|
||||
void write();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,12 @@ namespace Symbols {
|
|||
const string div_up = "┬";
|
||||
const string div_down = "┴";
|
||||
|
||||
const string up = "↑";
|
||||
const string down = "↓";
|
||||
const string left = "←";
|
||||
const string right = "→";
|
||||
const string enter = "↲";
|
||||
|
||||
const string meter = "■";
|
||||
|
||||
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
|
||||
|
@ -71,7 +77,7 @@ namespace Symbols {
|
|||
"▖", "▄", "▄", "▟", "▟",
|
||||
"▖", "▄", "▄", "▟", "▟",
|
||||
"▌", "▙", "▙", "█", "█",
|
||||
"▌", "▙", "▙", "█", "█",
|
||||
"▌", "▙", "▙", "█", "█"
|
||||
}},
|
||||
{"block_down", {
|
||||
" ", "▝", "▝", "▐", "▐",
|
||||
|
@ -100,50 +106,127 @@ namespace Symbols {
|
|||
|
||||
namespace Draw {
|
||||
|
||||
string createBox(int x, int y, int width, int height, string line_color, bool fill, string title, string title2, int num) {
|
||||
string out;
|
||||
string lcolor = (line_color.empty()) ? Theme::c("div_line") : line_color;
|
||||
string numbering = (num == 0) ? "" : Theme::c("hi_fg") + (Config::getB("tty_mode") ? std::to_string(num) : Symbols::superscript[num]);
|
||||
TextEdit::TextEdit() {}
|
||||
TextEdit::TextEdit(string text) : text(text) {
|
||||
pos = this->text.size();
|
||||
upos = ulen(this->text);
|
||||
}
|
||||
|
||||
out = Fx::reset + lcolor;
|
||||
bool TextEdit::command(const string& key) {
|
||||
if (key == "left" and upos > 0) {
|
||||
upos--;
|
||||
pos = uresize(text, upos).size();
|
||||
}
|
||||
else if (key == "right" and pos < text.size()) {
|
||||
upos++;
|
||||
pos = uresize(text, upos).size();
|
||||
}
|
||||
else if (key == "home" and pos > 0) {
|
||||
pos = upos = 0;
|
||||
}
|
||||
else if (key == "end" and pos < text.size()) {
|
||||
pos = text.size();
|
||||
upos = ulen(text);
|
||||
}
|
||||
else if (key == "backspace" and pos > 0) {
|
||||
if (pos == text.size()) {
|
||||
text = uresize(text, --upos);
|
||||
pos = text.size();
|
||||
}
|
||||
else {
|
||||
const string first = uresize(text, --upos);
|
||||
pos = first.size();
|
||||
text = first + text.substr(pos);
|
||||
}
|
||||
}
|
||||
else if (key == "delete" and pos < text.size()) {
|
||||
const string first = uresize(text, upos + 1);
|
||||
text = uresize(first, ulen(first) - 1) + text.substr(first.size());
|
||||
}
|
||||
else if (key == "space") {
|
||||
text.insert(pos++, 1, ' ');
|
||||
upos++;
|
||||
}
|
||||
else if (ulen(key) == 1) {
|
||||
if (key.size() == 1) {
|
||||
text.insert(pos++, 1, key[0]);
|
||||
upos++;
|
||||
}
|
||||
else {
|
||||
const string first = uresize(text, upos) + key;
|
||||
text = first + text.substr(pos);
|
||||
upos++;
|
||||
pos = first.size();
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string TextEdit::operator()(const size_t limit) {
|
||||
if (limit > 0 and ulen(text) + 1 > limit) {
|
||||
try {
|
||||
const size_t half = (size_t)round((double)limit / 2);
|
||||
string first;
|
||||
|
||||
if (upos + half > ulen(text))
|
||||
first = luresize(text.substr(0, pos), limit - (ulen(text) - upos));
|
||||
else if (upos - half < 1)
|
||||
first = text.substr(0, pos);
|
||||
else
|
||||
first = luresize(text.substr(0, pos), half);
|
||||
|
||||
return first + Fx::bl + "█" + Fx::ubl + uresize(text.substr(pos), limit - ulen(first));
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("In TextEdit::operator() : " + (string)e.what());
|
||||
}
|
||||
}
|
||||
return text.substr(0, pos) + Fx::bl + "█" + Fx::ubl + text.substr(pos);
|
||||
}
|
||||
|
||||
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");
|
||||
const string numbering = (num == 0) ? "" : Theme::c("hi_fg") + (Config::getB("tty_mode") ? std::to_string(num) : Symbols::superscript[num]);
|
||||
|
||||
out = Fx::reset + line_color;
|
||||
|
||||
//? Draw horizontal lines
|
||||
for (int hpos : {y, y + height - 1}) {
|
||||
for (const int& hpos : {y, y + height - 1}) {
|
||||
out += Mv::to(hpos, x) + Symbols::h_line * (width - 1);
|
||||
}
|
||||
|
||||
//? Draw vertical lines and fill if enabled
|
||||
for (int hpos : iota(y + 1, y + height - 1)) {
|
||||
out += Mv::to(hpos, x) + Symbols::v_line +
|
||||
((fill) ? string(width - 2, ' ') : Mv::r(width - 2)) +
|
||||
Symbols::v_line;
|
||||
for (const int& hpos : iota(y + 1, y + height - 1)) {
|
||||
out += Mv::to(hpos, x) + Symbols::v_line
|
||||
+ ((fill) ? string(width - 2, ' ') : Mv::r(width - 2))
|
||||
+ Symbols::v_line;
|
||||
}
|
||||
|
||||
//? Draw corners
|
||||
out += Mv::to(y, x) + Symbols::left_up +
|
||||
Mv::to(y, x + width - 1) + Symbols::right_up +
|
||||
Mv::to(y + height - 1, x) + Symbols::left_down +
|
||||
Mv::to(y + height - 1, x + width - 1) + Symbols::right_down;
|
||||
out += Mv::to(y, x) + Symbols::left_up
|
||||
+ Mv::to(y, x + width - 1) + Symbols::right_up
|
||||
+ Mv::to(y + height - 1, x) + Symbols::left_down
|
||||
+ Mv::to(y + height - 1, x + width - 1) + Symbols::right_down;
|
||||
|
||||
//? Draw titles if defined
|
||||
if (not title.empty()) {
|
||||
out += Mv::to(y, x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + title +
|
||||
Fx::ub + lcolor + Symbols::title_right;
|
||||
out += Mv::to(y, x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + title
|
||||
+ Fx::ub + line_color + Symbols::title_right;
|
||||
}
|
||||
if (not title2.empty()) {
|
||||
out += Mv::to(y + height - 1, x + 2) + Symbols::title_left + Theme::c("title") + title2 +
|
||||
Fx::ub + lcolor + Symbols::title_right;
|
||||
out += Mv::to(y + height - 1, x + 2) + Symbols::title_left + Theme::c("title") + title2
|
||||
+ Fx::ub + line_color + Symbols::title_right;
|
||||
}
|
||||
|
||||
return out + Fx::reset + Mv::to(y + 1, x + 1);
|
||||
}
|
||||
|
||||
//* Meter class ------------------------------------------------------------------------------------------------------------>
|
||||
void Meter::operator()(int width, string color_gradient, bool invert) {
|
||||
this->width = width;
|
||||
this->color_gradient = color_gradient;
|
||||
this->invert = invert;
|
||||
cache.clear();
|
||||
Meter::Meter(const int width, const string& color_gradient, const bool invert) : width(width), color_gradient(color_gradient), invert(invert) {
|
||||
cache.insert(cache.begin(), 101, "");
|
||||
}
|
||||
|
||||
|
@ -152,7 +235,7 @@ namespace Draw {
|
|||
value = clamp(value, 0, 100);
|
||||
if (not cache.at(value).empty()) return cache.at(value);
|
||||
string& out = cache.at(value);
|
||||
for (int i : iota(1, width + 1)) {
|
||||
for (const int& i : iota(1, width + 1)) {
|
||||
int y = round((double)i * 100.0 / width);
|
||||
if (value >= y)
|
||||
out += Theme::g(color_gradient)[invert ? 100 - y : y] + Symbols::meter;
|
||||
|
@ -169,7 +252,7 @@ namespace Draw {
|
|||
void Graph::_create(const deque<long long>& data, int data_offset) {
|
||||
const bool mult = (data.size() - data_offset > 1);
|
||||
if (mult and (data.size() - data_offset) % 2 != 0) data_offset--;
|
||||
auto& graph_symbol = Symbols::graph_symbols.at(symbol + '_' + (invert ? "down" : "up"));
|
||||
const auto& graph_symbol = Symbols::graph_symbols.at(symbol + '_' + (invert ? "down" : "up"));
|
||||
array<int, 2> result;
|
||||
const float mod = (height == 1) ? 0.3 : 0.1;
|
||||
long long data_value = 0;
|
||||
|
@ -187,12 +270,12 @@ namespace Draw {
|
|||
if (max_value > 0) data_value = clamp((data_value + offset) * 100 / max_value, 0ll, 100ll);
|
||||
|
||||
//? Vertical iteration over height of graph
|
||||
for (int horizon : iota(0, height)) {
|
||||
int cur_high = (height > 1) ? round(100.0 * (height - horizon) / height) : 100;
|
||||
int cur_low = (height > 1) ? round(100.0 * (height - (horizon + 1)) / height) : 0;
|
||||
int clamp_min = (no_zero and horizon == height - 1 and i != -1) ? 1 : 0;
|
||||
for (const int& horizon : iota(0, height)) {
|
||||
const int cur_high = (height > 1) ? round(100.0 * (height - horizon) / height) : 100;
|
||||
const int cur_low = (height > 1) ? round(100.0 * (height - (horizon + 1)) / height) : 0;
|
||||
const int clamp_min = (no_zero and horizon == height - 1 and i != -1) ? 1 : 0;
|
||||
//? Calculate previous + current value to fit two values in 1 braille character
|
||||
for (int ai = 0; auto value : {last, data_value}) {
|
||||
for (int ai = 0; const auto& value : {last, data_value}) {
|
||||
if (value >= cur_high)
|
||||
result[ai++] = 4;
|
||||
else if (value <= cur_low)
|
||||
|
@ -214,7 +297,7 @@ namespace Draw {
|
|||
out += graphs[current][0];
|
||||
}
|
||||
else {
|
||||
for (int i : iota(0, height)) {
|
||||
for (const int& i : iota(0, height)) {
|
||||
if (i > 0) out += Mv::d(1) + Mv::l(width);
|
||||
if (not color_gradient.empty())
|
||||
out += (invert) ? Theme::g(color_gradient)[i * 100 / (height - 1)] : Theme::g(color_gradient)[100 - (i * 100 / (height - 1))];
|
||||
|
@ -224,12 +307,10 @@ namespace Draw {
|
|||
if (not color_gradient.empty()) out += Fx::reset;
|
||||
}
|
||||
|
||||
void Graph::operator()(int width, int height, string color_gradient, const deque<long long>& data, string symbol, bool invert, bool no_zero, long long max_value, long long offset) {
|
||||
graphs[true].clear(); graphs[false].clear();
|
||||
this->width = width; this->height = height;
|
||||
this->invert = invert; this->offset = offset;
|
||||
this->no_zero = no_zero;
|
||||
this->color_gradient = color_gradient;
|
||||
Graph::Graph() {};
|
||||
|
||||
Graph::Graph(int width, int height, const string& color_gradient, const deque<long long>& data, const string& symbol, bool invert, bool no_zero, long long max_value, long long offset)
|
||||
: width(width), height(height), color_gradient(color_gradient), invert(invert), no_zero(no_zero), offset(offset) {
|
||||
if (Config::getB("tty_mode") or symbol == "tty") {
|
||||
tty_mode = true;
|
||||
this->symbol = "tty";
|
||||
|
@ -243,21 +324,22 @@ namespace Draw {
|
|||
if (value_width > width) data_offset = data.size() - width * 2;
|
||||
|
||||
//? Populate the two switching graph vectors and fill empty space if data size < width
|
||||
for (int i : iota(0, height * 2)) {
|
||||
for (const int& i : iota(0, height * 2)) {
|
||||
if (tty_mode and i % 2 != current) continue;
|
||||
graphs[(i % 2 != 0)].push_back((value_width < width) ? ((height == 1) ? Mv::r(1) : " "s) * (width - value_width) : "");
|
||||
}
|
||||
if (data.size() == 0) return;
|
||||
this->_create(data, data_offset);
|
||||
this->_create((data.size() == 1 ? deque{0, data[0]} : data), data_offset);
|
||||
}
|
||||
|
||||
string& Graph::operator()(const deque<long long>& data, bool data_same) {
|
||||
string& Graph::operator()(const deque<long long>& data, const bool data_same) {
|
||||
if (data_same) return out;
|
||||
|
||||
//? Make room for new characters on graph
|
||||
// bool select_graph = tty_mode ? current : not current;
|
||||
for (int i : iota(0, height)) {
|
||||
if (graphs[current][i][1] == '[') graphs[current][i].erase(0, 4);
|
||||
else graphs[current][i].erase(0, 3);
|
||||
bool select_graph = (tty_mode) ? current : not current;
|
||||
for (const int& i : iota(0, height)) {
|
||||
if (graphs[select_graph][i][1] == '[') graphs[select_graph][i].erase(0, 4);
|
||||
else graphs[select_graph][i].erase(0, 3);
|
||||
}
|
||||
this->_create(data, (int)data.size() - 1);
|
||||
return out;
|
||||
|
@ -345,6 +427,7 @@ namespace Proc {
|
|||
unordered_flat_map<size_t, Draw::Graph> p_graphs;
|
||||
unordered_flat_map<size_t, int> p_counters;
|
||||
int counter = 0;
|
||||
Draw::TextEdit filter;
|
||||
|
||||
string box;
|
||||
|
||||
|
@ -396,11 +479,9 @@ namespace Proc {
|
|||
}
|
||||
|
||||
string draw(const vector<proc_info>& plist, const bool force_redraw, const bool data_same) {
|
||||
auto& filter = Config::getS("proc_filter");
|
||||
auto& filtering = Config::getB("proc_filtering");
|
||||
auto& proc_tree = Config::getB("proc_tree");
|
||||
const bool show_detailed = (Config::getB("show_detailed") and Proc::detailed.last_pid == (size_t)Config::getI("detailed_pid"));
|
||||
const bool proc_gradient = (Config::getB("proc_gradient") and not Config::getB("tty_mode"));
|
||||
const bool proc_gradient = (Config::getB("proc_gradient") and not Config::getB("tty_mode") and not Config::getB("lowcolor"));
|
||||
auto& proc_colors = Config::getB("proc_colors");
|
||||
const auto& graph_symbol = (Config::getB("tty_mode") ? "tty" : Config::getS("graph_symbol_proc"));
|
||||
const auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? "braille_up" : graph_symbol + "_up"))[1];
|
||||
|
@ -417,11 +498,48 @@ namespace Proc {
|
|||
out = box;
|
||||
const string title_left = Theme::c("proc_box") + Symbols::title_left;
|
||||
const string title_right = Theme::c("proc_box") + Symbols::title_right;
|
||||
//? Buttons etc. in box titlebar
|
||||
out += Mv::to(y, x) + Mv::r(12)
|
||||
+ trans("Filter: " + filter + (filtering ? Fx::bl + "█"s + Fx::reset : " "))
|
||||
+ trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: "
|
||||
+ string(Config::getS("proc_sorting")), width - 23 - ulen(filter)));
|
||||
const auto& filtering = Config::getB("proc_filtering"); // ? filter(20) : Config::getS("proc_filter"))
|
||||
const auto filter_text = (filtering) ? filter(max(6, width - 58)) : uresize(Config::getS("proc_filter"), max(6, width - 58));
|
||||
|
||||
//? Filter
|
||||
out += Mv::to(y, x+9) + title_left + (not filter_text.empty() ? Fx::b : "") + Theme::c("hi_fg") + 'f'
|
||||
+ Theme::c("title") + (not filter_text.empty() ? ' ' + filter_text : "ilter")
|
||||
+ (not filtering and not filter_text.empty() ? Theme::c("hi_fg") + " del" : "")
|
||||
+ (filtering ? Theme::c("hi_fg") + ' ' + Symbols::enter : "") + Fx::ub + title_right;
|
||||
if (not filtering) {
|
||||
int f_len = (filter_text.empty() ? 6 : ulen(filter_text) + 2);
|
||||
Input::mouse_mappings["f"] = {y, x + 10, 1, f_len};
|
||||
if (filter_text.empty() and Input::mouse_mappings.contains("delete"))
|
||||
Input::mouse_mappings.erase("delete");
|
||||
else if (not filter_text.empty())
|
||||
Input::mouse_mappings["delete"] = {y, x + 11 + f_len, 1, 3};
|
||||
}
|
||||
|
||||
//? per-core, reverse, tree and sorting
|
||||
const auto& sorting = Config::getS("proc_sorting");
|
||||
const int sort_len = sorting.size();
|
||||
const int sort_pos = x + width - sort_len - 8;
|
||||
|
||||
if (width > 55 + sort_len) {
|
||||
out += Mv::to(y, sort_pos - 25) + title_left + (Config::getB("proc_per_core") ? Fx::b : "") + Theme::c("title")
|
||||
+ "per-" + Theme::c("hi_fg") + 'c' + Theme::c("title") + "ore" + Fx::ub + title_right;
|
||||
}
|
||||
if (width > 45 + sort_len) {
|
||||
out += Mv::to(y, sort_pos - 15) + title_left + (Config::getB("proc_reversed") ? Fx::b : "") + Theme::c("hi_fg")
|
||||
+ 'r' + Theme::c("title") + "everse" + Fx::ub + title_right;
|
||||
}
|
||||
if (width > 35 + sort_len) {
|
||||
out += Mv::to(y, sort_pos - 6) + title_left + (Config::getB("proc_tree") ? Fx::b : "") + Theme::c("title") + "tre"
|
||||
+ Theme::c("hi_fg") + 'e' + Fx::ub + title_right;
|
||||
}
|
||||
out += Mv::to(y, sort_pos) + title_left + Fx::b + Theme::c("hi_fg") + "< " + Theme::c("title") + sorting + Theme::c("hi_fg")
|
||||
+ " >" + Fx::ub + title_right;
|
||||
|
||||
|
||||
// out += Mv::to(y, x) + Mv::r(12)
|
||||
// + trans("Filter: " + filter_text)
|
||||
// + trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: "
|
||||
// + string(Config::getS("proc_sorting")), width - 23 - ulen(filter_text)));
|
||||
|
||||
//? Labels for fields in list
|
||||
if (not proc_tree)
|
||||
|
@ -436,8 +554,6 @@ namespace Proc {
|
|||
out += Mv::to(y+1, x+1) + Theme::c("title") + Fx::b + ljust("Tree:", width - 40)
|
||||
+ "Threads: " + ljust("User:", 10) + " " + rjust("MemB", 5)
|
||||
+ " " + rjust("Cpu%", 10) + Fx::ub;
|
||||
|
||||
Input::mouse_mappings["down"] = {2, 2, 10, 10};
|
||||
}
|
||||
|
||||
//* Check bounds of current selection and view
|
||||
|
@ -460,7 +576,7 @@ namespace Proc {
|
|||
//? Update graphs for processes with above 0.0% cpu usage, delete if below 0.1% 10x times
|
||||
if (not data_same and (p.cpu_p > 0 or p_counters.contains(p.pid))) {
|
||||
if (not p_graphs.contains(p.pid)) {
|
||||
p_graphs[p.pid](5, 1, "", {0}, graph_symbol);
|
||||
p_graphs[p.pid] = {5, 1, "", {}, graph_symbol};
|
||||
p_counters[p.pid] = 0;
|
||||
}
|
||||
else if (p.cpu_p < 0.1) {
|
||||
|
|
|
@ -27,18 +27,29 @@ using std::string, std::vector, robin_hood::unordered_flat_map, std::deque;
|
|||
|
||||
namespace Draw {
|
||||
|
||||
//* An editable text field
|
||||
class TextEdit {
|
||||
size_t pos = 0;
|
||||
size_t upos = 0;
|
||||
public:
|
||||
string text;
|
||||
TextEdit();
|
||||
TextEdit(string text);
|
||||
bool command(const string& key);
|
||||
string operator()(const size_t limit=0);
|
||||
};
|
||||
|
||||
//* Create a box and return as a string
|
||||
string createBox(int x, int y, int width, int height, string line_color="", bool fill=false, string title="", string title2="", int num=0);
|
||||
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);
|
||||
|
||||
//* Class holding a percentage meter
|
||||
class Meter {
|
||||
string color_gradient;
|
||||
int width = 0;
|
||||
bool invert = false;
|
||||
const int width;
|
||||
const string color_gradient;
|
||||
const bool invert;
|
||||
vector<string> cache;
|
||||
public:
|
||||
//* Set meter options
|
||||
void operator()(int width, string color_gradient, bool invert = false);
|
||||
Meter(const int width, const string& color_gradient, const bool invert = false);
|
||||
|
||||
//* Return a string representation of the meter with given value
|
||||
string operator()(int value);
|
||||
|
@ -46,21 +57,32 @@ namespace Draw {
|
|||
|
||||
//* Class holding a percentage graph
|
||||
class Graph {
|
||||
string out, color_gradient, symbol = "default";
|
||||
int width = 0, height = 0;
|
||||
long long last = 0, max_value = 0, offset = 0;
|
||||
bool current = true, no_zero = false, invert = false, tty_mode = false;
|
||||
int width, height;
|
||||
string color_gradient;
|
||||
string out, symbol = "default";
|
||||
bool invert, no_zero;
|
||||
long long offset;
|
||||
long long last = 0, max_value = 0;
|
||||
bool current = true, tty_mode = false;
|
||||
unordered_flat_map<bool, vector<string>> graphs = { {true, {}}, {false, {}}};
|
||||
|
||||
//* Create two representations of the graph to switch between to represent two values for each braille character
|
||||
void _create(const deque<long long>& data, int data_offset);
|
||||
|
||||
public:
|
||||
//* Set graph options and initialize with data
|
||||
void operator()(int width, int height, string color_gradient, const deque<long long>& data, string symbol="default", bool invert=false, bool no_zero=false, long long max_value=0, long long offset=0);
|
||||
Graph();
|
||||
Graph( int width,
|
||||
int height,
|
||||
const string& color_gradient,
|
||||
const deque<long long>& data,
|
||||
const string& symbol="default",
|
||||
bool invert=false,
|
||||
bool no_zero=false,
|
||||
long long max_value=0,
|
||||
long long offset=0);
|
||||
|
||||
//* Add last value from back of <data> and return string representation of graph
|
||||
string& operator()(const deque<long long>& data, bool data_same=false);
|
||||
string& operator()(const deque<long long>& data, const bool data_same=false);
|
||||
|
||||
//* Return string representation of graph
|
||||
string& operator()();
|
||||
|
@ -69,4 +91,8 @@ namespace Draw {
|
|||
//* Calculate sizes of boxes, draw outlines and save to enabled boxes namespaces
|
||||
void calcSizes();
|
||||
|
||||
}
|
||||
|
||||
namespace Proc {
|
||||
extern Draw::TextEdit filter;
|
||||
}
|
|
@ -23,6 +23,7 @@ tab-size = 4
|
|||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_menu.hpp>
|
||||
#include <btop_draw.hpp>
|
||||
|
||||
using std::cin, std::string_literals::operator""s;
|
||||
using namespace Tools;
|
||||
|
@ -72,6 +73,7 @@ namespace Input {
|
|||
unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
||||
|
||||
string last = "";
|
||||
string old_filter;
|
||||
|
||||
bool poll(int timeout) {
|
||||
if (timeout < 1) return cin.rdbuf()->in_avail() > 0;
|
||||
|
@ -116,6 +118,12 @@ namespace Input {
|
|||
else
|
||||
key.clear();
|
||||
|
||||
if (Config::getB("proc_filtering")) {
|
||||
if (mouse_event == "mouse_click") last = mouse_event;
|
||||
else last.clear();
|
||||
return last;
|
||||
}
|
||||
|
||||
//? Get column and line position of mouse and check for any actions mapped to current position
|
||||
if (not key.empty()) {
|
||||
try {
|
||||
|
@ -175,23 +183,24 @@ namespace Input {
|
|||
if (Proc::shown) {
|
||||
bool keep_going = false;
|
||||
if (filtering) {
|
||||
string filter = Config::getS("proc_filter");
|
||||
if (key == "enter")
|
||||
if (key == "enter") {
|
||||
Config::set("proc_filter", Proc::filter.text);
|
||||
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);
|
||||
|
||||
old_filter.clear();
|
||||
}
|
||||
else if (key == "escape" or key == "mouse_click") {
|
||||
Config::set("proc_filter", old_filter);
|
||||
Config::set("proc_filtering", false);
|
||||
old_filter.clear();
|
||||
}
|
||||
else if (Proc::filter.command(key)) {
|
||||
if (Config::getS("proc_filter") != Proc::filter.text)
|
||||
Config::set("proc_filter", Proc::filter.text);
|
||||
else
|
||||
recollect = false;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
Config::set("proc_filter", filter);
|
||||
}
|
||||
else if (key == "left") {
|
||||
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
|
||||
|
@ -207,6 +216,8 @@ namespace Input {
|
|||
}
|
||||
else if (key == "f") {
|
||||
Config::flip("proc_filtering");
|
||||
Proc::filter = { Config::getS("proc_filter") };
|
||||
old_filter = Proc::filter.text;
|
||||
recollect = false;
|
||||
}
|
||||
else if (key == "t")
|
||||
|
|
|
@ -34,9 +34,10 @@ using robin_hood::unordered_flat_map, std::array, std::string, std::atomic;
|
|||
namespace Input {
|
||||
|
||||
struct Mouse_loc {
|
||||
int col, line, width, height;
|
||||
int line, col, height, width;
|
||||
};
|
||||
|
||||
//? line, col, height, width
|
||||
extern unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
||||
|
||||
extern atomic<bool> interrupt;
|
||||
|
|
|
@ -56,13 +56,12 @@ namespace Shared {
|
|||
|
||||
void init() {
|
||||
proc_path = (fs::is_directory(fs::path("/proc")) and access("/proc", R_OK) != -1) ? "/proc" : "";
|
||||
if (proc_path.empty()) {
|
||||
Global::exit_error_msg = "Proc filesystem not found or no permission to read from it!";
|
||||
clean_quit(1);
|
||||
}
|
||||
if (proc_path.empty())
|
||||
throw std::runtime_error("Proc filesystem not found or no permission to read from it!");
|
||||
|
||||
passwd_path = (access("/etc/passwd", R_OK) != -1) ? fs::path("/etc/passwd") : passwd_path;
|
||||
if (passwd_path.empty()) Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
|
||||
passwd_path = (fs::is_regular_file(fs::path("/etc/passwd")) and access("/etc/passwd", R_OK) != -1) ? "/etc/passwd" : "";
|
||||
if (passwd_path.empty())
|
||||
Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
if (page_size <= 0) {
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace Shared {
|
|||
void init();
|
||||
}
|
||||
|
||||
|
||||
namespace Cpu {
|
||||
extern string box, cpuName;
|
||||
extern int x, y, width, height;
|
||||
|
|
|
@ -48,24 +48,24 @@ namespace Theme {
|
|||
{ "main_bg", "#00" },
|
||||
{ "main_fg", "#cc" },
|
||||
{ "title", "#ee" },
|
||||
{ "hi_fg", "#969696" },
|
||||
{ "selected_bg", "#7e2626" },
|
||||
{ "hi_fg", "#b54040" },
|
||||
{ "selected_bg", "#6a2f2f" },
|
||||
{ "selected_fg", "#ee" },
|
||||
{ "inactive_fg", "#40" },
|
||||
{ "graph_text", "#60" },
|
||||
{ "meter_bg", "#40" },
|
||||
{ "proc_misc", "#0de756" },
|
||||
{ "cpu_box", "#3d7b46" },
|
||||
{ "mem_box", "#8a882e" },
|
||||
{ "net_box", "#423ba5" },
|
||||
{ "proc_box", "#923535" },
|
||||
{ "cpu_box", "#556d59" },
|
||||
{ "mem_box", "#6c6c4b" },
|
||||
{ "net_box", "#5c588d" },
|
||||
{ "proc_box", "#805252" },
|
||||
{ "div_line", "#30" },
|
||||
{ "temp_start", "#4897d4" },
|
||||
{ "temp_mid", "#5474e8" },
|
||||
{ "temp_end", "#ff40b6" },
|
||||
{ "cpu_start", "#50f095" },
|
||||
{ "cpu_mid", "#f2e266" },
|
||||
{ "cpu_end", "#fa1e1e" },
|
||||
{ "cpu_start", "#77ca9b" },
|
||||
{ "cpu_mid", "#cbc06c" },
|
||||
{ "cpu_end", "#dc4c4c" },
|
||||
{ "free_start", "#223014" },
|
||||
{ "free_mid", "#b5e685" },
|
||||
{ "free_end", "#dcff85" },
|
||||
|
@ -93,7 +93,7 @@ namespace Theme {
|
|||
{ "main_bg", "\x1b[0;40m" },
|
||||
{ "main_fg", "\x1b[37m" },
|
||||
{ "title", "\x1b[97m" },
|
||||
{ "hi_fg", "\x1b[31m" },
|
||||
{ "hi_fg", "\x1b[91m" },
|
||||
{ "selected_bg", "\x1b[41m" },
|
||||
{ "selected_fg", "\x1b[97m" },
|
||||
{ "inactive_fg", "\x1b[90m" },
|
||||
|
@ -138,7 +138,7 @@ namespace Theme {
|
|||
//* Convert 24-bit colors to 256 colors
|
||||
int truecolor_to_256(const int& r, const int& g, const int& b) {
|
||||
//? Use upper 232-255 greyscale values if the downscaled red, green and blue are the same value
|
||||
if (int red = round((double)r / 11); red == round((double)g / 11) and round((double)g / 11) == round((double)b / 11)) {
|
||||
if (const int red = round((double)r / 11); red == round((double)g / 11) and red == round((double)b / 11)) {
|
||||
return 232 + red;
|
||||
}
|
||||
//? Else use 6x6x6 color cube to calculate approximate colors
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace Term {
|
|||
namespace Tools {
|
||||
|
||||
string uresize(string str, const size_t len) {
|
||||
if (len < 1) return "";
|
||||
if (len < 1 or str.empty()) return "";
|
||||
for (size_t x = 0, i = 0; i < str.size(); i++) {
|
||||
if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) x++;
|
||||
if (x == len + 1) {
|
||||
|
@ -126,6 +126,22 @@ namespace Tools {
|
|||
return str;
|
||||
}
|
||||
|
||||
string luresize(string str, const size_t len) {
|
||||
if (len < 1 or str.empty()) return "";
|
||||
for (size_t x = 0, last_pos = 0, i = str.size() - 1; i > 0 ; i--) {
|
||||
if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) {
|
||||
x++;
|
||||
last_pos = i;
|
||||
}
|
||||
if (x == len) {
|
||||
str = str.substr(last_pos);
|
||||
str.shrink_to_fit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
string ltrim(const string& str, const string& t_str) {
|
||||
string_view str_v = str;
|
||||
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
|
||||
|
|
|
@ -133,6 +133,9 @@ namespace Tools {
|
|||
//* Resize a string consisting of UTF8 characters (only reduces size)
|
||||
string uresize(const string str, const size_t len);
|
||||
|
||||
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
||||
string luresize(const string str, const size_t len);
|
||||
|
||||
//* Return <str> with only uppercase characters
|
||||
inline string str_to_upper(string str) {
|
||||
std::ranges::for_each(str, [](auto& c) { c = ::toupper(c); } );
|
||||
|
|
Loading…
Reference in New Issue