mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 07:11:24 +12:00
Namespaces < Classes
This commit is contained in:
parent
05eb21dfbb
commit
833d253276
8 changed files with 538 additions and 487 deletions
|
@ -1,2 +0,0 @@
|
||||||
file(GLOB sources *.h *.c *.cxx *.cpp *.hxx)
|
|
||||||
add_executable(btop ${sources})
|
|
2
Makefile
2
Makefile
|
@ -1,7 +1,7 @@
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
DOCDIR ?= $(PREFIX)/share/btop/doc
|
DOCDIR ?= $(PREFIX)/share/btop/doc
|
||||||
CXX = g++
|
CXX = g++
|
||||||
CXXFLAGS = -std=c++20 -pthread -Wall
|
override CXXFLAGS += -std=c++20 -pthread -Wall -Wextra
|
||||||
INCLUDES = -I./src
|
INCLUDES = -I./src
|
||||||
|
|
||||||
btop: btop.cpp
|
btop: btop.cpp
|
||||||
|
|
158
btop.cpp
158
btop.cpp
|
@ -66,6 +66,9 @@ namespace Global {
|
||||||
};
|
};
|
||||||
|
|
||||||
const string Version = "0.0.1";
|
const string Version = "0.0.1";
|
||||||
|
|
||||||
|
string banner;
|
||||||
|
uint banner_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,23 +93,22 @@ void argumentParser(int argc, char **argv){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class C_Banner {
|
//* Generate the btop++ banner
|
||||||
string banner_str;
|
auto create_banner(){
|
||||||
|
struct out_vals {
|
||||||
public:
|
uint w;
|
||||||
int width = 0;
|
string s;
|
||||||
|
};
|
||||||
C_Banner(){
|
|
||||||
size_t z = 0;
|
size_t z = 0;
|
||||||
|
uint width=0, new_len=0;
|
||||||
string b_color, bg, fg, out, oc, letter;
|
string b_color, bg, fg, out, oc, letter;
|
||||||
|
bool truecolor = Config::getB("truecolor");
|
||||||
int bg_i;
|
int bg_i;
|
||||||
int new_len;
|
|
||||||
for (auto line: Global::Banner_src) {
|
for (auto line: Global::Banner_src) {
|
||||||
new_len = ulen(line[1]);
|
if ((new_len = ulen(line[1])) > width) width = new_len;
|
||||||
if (new_len > width) width = new_len;
|
fg = Theme::hex_to_color(line[0], !truecolor);
|
||||||
fg = hex_to_color(line[0]);
|
|
||||||
bg_i = 120-z*12;
|
bg_i = 120-z*12;
|
||||||
bg = dec_to_color(bg_i, bg_i, bg_i);
|
bg = Theme::dec_to_color(bg_i, bg_i, bg_i, !truecolor);
|
||||||
for (uint i = 0; i < line[1].size(); i += 3) {
|
for (uint i = 0; i < line[1].size(); i += 3) {
|
||||||
if (line[1][i] == ' '){
|
if (line[1][i] == ' '){
|
||||||
letter = ' ';
|
letter = ' ';
|
||||||
|
@ -122,14 +124,11 @@ public:
|
||||||
z++;
|
z++;
|
||||||
if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1);
|
if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1);
|
||||||
}
|
}
|
||||||
banner_str = out + Mv::r(18 - Global::Version.size()) + Fx::i + dec_to_color(0,0,0, State::truecolor, "bg") + dec_to_color(150, 150, 150) + "v" + Global::Version;
|
out += Mv::r(18 - Global::Version.size()) + Fx::i + Theme::dec_to_color(0,0,0, !truecolor, "bg") +
|
||||||
}
|
Theme::dec_to_color(150, 150, 150, !truecolor) + "v" + Global::Version + Fx::reset;
|
||||||
|
return out_vals {width, out};
|
||||||
|
}
|
||||||
|
|
||||||
//* Returns the pre-generated btop++ banner
|
|
||||||
string operator() (){
|
|
||||||
return banner_str + Fx::reset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//* Threading test function
|
//* Threading test function
|
||||||
string my_worker(int x){
|
string my_worker(int x){
|
||||||
|
@ -159,41 +158,38 @@ int main(int argc, char **argv){
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Initialize terminal and set options
|
//? Initialize terminal and set options
|
||||||
C_Term Term;
|
if (!Term::init()) {
|
||||||
if (!Term.initialized) {
|
|
||||||
cout << "ERROR: No tty detected!" << endl;
|
cout << "ERROR: No tty detected!" << endl;
|
||||||
cout << "Sorry, btop++ needs an interactive shell to run." << endl;
|
cout << "Sorry, btop++ needs an interactive shell to run." << endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Read config file if present
|
//? Read config file if present
|
||||||
C_Config Config;
|
Config::load("____");
|
||||||
|
|
||||||
//? Generate the theme
|
//? Generate the theme
|
||||||
C_Theme Theme(Global::Default_theme);
|
Theme::set(Global::Default_theme);
|
||||||
|
|
||||||
//? Create the btop++ banner
|
//? Create the btop++ banner
|
||||||
C_Banner Banner;
|
auto [banner_width, banner] = create_banner();
|
||||||
|
Global::banner_width = move(banner_width);
|
||||||
|
Global::banner = move(banner);
|
||||||
|
|
||||||
//? Initialize the Input class
|
|
||||||
C_Input Input;
|
|
||||||
|
|
||||||
//* ------------------------------------------------ TESTING ------------------------------------------------------
|
//* ------------------------------------------------ TESTING ------------------------------------------------------
|
||||||
|
|
||||||
int debug = 2;
|
int debug = 0;
|
||||||
int tests = 0;
|
int tests = 0;
|
||||||
|
|
||||||
// cout << Theme("main_bg") << Term.clear << flush;
|
// cout << Theme("main_bg") << Term::clear << flush;
|
||||||
bool thread_test = false;
|
bool thread_test = false;
|
||||||
|
|
||||||
if (debug < 2) cout << Term.alt_screen << Term.clear << Term.hide_cursor << flush;
|
if (debug < 2) cout << Term::alt_screen << Term::hide_cursor << flush;
|
||||||
|
|
||||||
cout << Theme("main_fg") << Term.clear << endl;
|
|
||||||
|
|
||||||
cout << Mv::r(Term.width / 2 - Banner.width / 2) << Banner() << endl;
|
|
||||||
cout << string(Term.width - 1, '-') << endl;
|
|
||||||
|
|
||||||
|
cout << Theme::c("main_fg") << Theme::c("main_bg") << Term::clear << endl;
|
||||||
|
|
||||||
|
cout << Mv::r(Term::width / 2 - Global::banner_width / 2) << Global::banner << endl;
|
||||||
|
cout << string(Term::width - 1, '-') << endl;
|
||||||
|
|
||||||
|
|
||||||
//* Test MENUS
|
//* Test MENUS
|
||||||
|
@ -215,7 +211,7 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
if (tests>0) for(auto& item : Global::Default_theme) {
|
if (tests>0) for(auto& item : Global::Default_theme) {
|
||||||
cout << Theme(item.first) << item.first << ":" << Theme("main_fg") << Theme(item.first).erase(0, 2) << Fx::reset << " ";
|
cout << Theme::c(item.first) << item.first << ":" << Theme::c("main_fg") << Theme::c(item.first).erase(0, 2) << Fx::reset << " ";
|
||||||
if (++i == 4) {
|
if (++i == 4) {
|
||||||
i = 0;
|
i = 0;
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
@ -258,11 +254,14 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
|
|
||||||
auto timestamp = time_ms();
|
auto timestamp = time_ms();
|
||||||
Processes Proc;
|
Proc::init();
|
||||||
|
|
||||||
cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl;
|
cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl;
|
||||||
|
cout << "Press any key to start!" << Mv::l(100) << flush;
|
||||||
|
|
||||||
sleep_ms(1000);
|
// sleep_ms(1000);
|
||||||
|
// Input::wait();
|
||||||
|
// Input::clear();
|
||||||
|
|
||||||
|
|
||||||
// insert Processes call here
|
// insert Processes call here
|
||||||
|
@ -270,26 +269,63 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
uint lc;
|
uint lc;
|
||||||
string ostring;
|
string ostring;
|
||||||
cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term.width - 69) << " Threads: " <<
|
uint64_t tsl, timestamp2;
|
||||||
|
uint timer = 2000;
|
||||||
|
bool filtering = false;
|
||||||
|
vector<string> sorting;
|
||||||
|
bool reversing = false;
|
||||||
|
int sortint = Proc::sort_map["cpu lazy"];
|
||||||
|
string filter;
|
||||||
|
string filter_cur;
|
||||||
|
string key;
|
||||||
|
cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term::width - 69) << " Threads: " <<
|
||||||
ljustify("User:", 10) << " " << rjustify("MemB", 5) << " " << rjustify("Cpu%", 14) << "\n" << Mv::save << flush;
|
ljustify("User:", 10) << " " << rjustify("MemB", 5) << " " << rjustify("Cpu%", 14) << "\n" << Mv::save << flush;
|
||||||
|
|
||||||
while (Input() != "q") {
|
while (key != "q") {
|
||||||
timestamp = time_ms();
|
timestamp = time_ms();
|
||||||
auto plist = Proc.collect("cpu lazy", false);
|
tsl = timestamp + timer;
|
||||||
timestamp = time_ms() - timestamp;
|
auto plist = Proc::collect(Proc::sort_vector[sortint], reversing, filter);
|
||||||
|
timestamp2 = time_ms();
|
||||||
|
timestamp = timestamp2 - timestamp;
|
||||||
ostring.clear();
|
ostring.clear();
|
||||||
lc = 0;
|
lc = 0;
|
||||||
|
filter_cur = (filtering) ? Fx::bl + "█" + Fx::reset : "";
|
||||||
|
cout << Mv::restore << Mv::u(2) << Mv::r(20) << rjustify("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') +
|
||||||
|
"Sorting: " + Proc::sort_vector[sortint], Term::width - 22, true, filtering) << Mv::restore << flush;
|
||||||
|
|
||||||
for (auto& [lpid, lname, lcmd, lthread, luser, lmem, lcpu, lcpu_s] : plist){
|
for (auto& [lpid, lname, lcmd, lthread, luser, lmem, lcpu, lcpu_s] : plist){
|
||||||
(void) lcpu_s;
|
(void) lcpu_s;
|
||||||
ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term.width - 66, true) + " " +
|
ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term::width - 66, true) + " " +
|
||||||
rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true, 1), 5) + string(11, ' ');
|
rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true), 5) + string(11, ' ');
|
||||||
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
|
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
|
||||||
ostring += "\n";
|
ostring += "\n";
|
||||||
if (lc++ > Term.height - 20) break;
|
if (lc++ > Term::height - 20) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << Mv::restore << ostring << Term::clear_end << endl;
|
||||||
|
cout << "Processes call took: " << timestamp << "ms. Drawing took: " << time_ms() - timestamp2 << "ms." << endl;
|
||||||
|
|
||||||
|
while (time_ms() < tsl) {
|
||||||
|
if (Input::poll(tsl - time_ms())) key = Input::get();
|
||||||
|
else { key.clear() ; continue; }
|
||||||
|
// if (key != "") continue;
|
||||||
|
if (filtering) {
|
||||||
|
if (key == "enter") filtering = false;
|
||||||
|
else if (key == "backspace") {if (!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; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (key == "q") break;
|
||||||
|
else if (key == "left") { if (--sortint < 0) sortint = (int)Proc::sort_vector.size() - 1; }
|
||||||
|
else if (key == "right") { if (++sortint > (int)Proc::sort_vector.size() - 1) sortint = 0; }
|
||||||
|
else if (key == "f") filtering = true;
|
||||||
|
else if (key == "r") reversing = !reversing;
|
||||||
|
else if (key == "delete") filter.clear();
|
||||||
|
else continue;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
cout << Mv::restore << ostring << endl;
|
|
||||||
cout << "Processes call took: " << timestamp << "ms." << endl;
|
|
||||||
Input(2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cout << "Found " << plist.size() << " pids\n" << endl;
|
// cout << "Found " << plist.size() << " pids\n" << endl;
|
||||||
|
@ -301,15 +337,15 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
|
|
||||||
if (tests>3){
|
if (tests>3){
|
||||||
auto nbcolor = hex_to_color(Global::Default_theme.at("net_box"));
|
auto nbcolor = Theme::hex_to_color(Global::Default_theme.at("net_box"));
|
||||||
auto nbcolor_rgb = c_to_rgb(nbcolor);
|
auto nbcolor_rgb = Theme::rgb(nbcolor);
|
||||||
auto nbcolor_man = ssplit(nbcolor, ";");
|
auto nbcolor_man = ssplit(nbcolor, ";");
|
||||||
cout << nbcolor << "Some color" << endl;
|
cout << nbcolor << "Some color" << endl;
|
||||||
cout << "nbcolor_rgb size=" << nbcolor_rgb.size() << endl;
|
cout << "nbcolor_rgb size=" << nbcolor_rgb.size() << endl;
|
||||||
cout << "R:" << nbcolor_rgb.at("r") << " G:" << nbcolor_rgb.at("g") << " B:" << nbcolor_rgb.at("b") << endl;
|
cout << "R:" << nbcolor_rgb.at("r") << " G:" << nbcolor_rgb.at("g") << " B:" << nbcolor_rgb.at("b") << endl;
|
||||||
cout << "MANUAL R:" << nbcolor_man.at(2) << " G:" << nbcolor_man.at(3) << " B:" << nbcolor_man.at(4) << endl;
|
cout << "MANUAL R:" << nbcolor_man.at(2) << " G:" << nbcolor_man.at(3) << " B:" << nbcolor_man.at(4) << endl;
|
||||||
|
|
||||||
auto ccc = dec_to_color(100, 255, 100);
|
auto ccc = Theme::dec_to_color(100, 255, 100);
|
||||||
cout << "\n" << ccc << "Testing..." << endl;
|
cout << "\n" << ccc << "Testing..." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,9 +393,9 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (debug == 0){
|
if (debug == 3){
|
||||||
cout << Theme("main_fg");
|
cout << Theme::c("main_fg");
|
||||||
cout << Mv::to(Term.height - 1, 0) << "Press q to exit! Timeout" << flush;
|
cout << Mv::to(Term::height - 1, 0) << "Press q to exit! Timeout" << flush;
|
||||||
string full, key;
|
string full, key;
|
||||||
int wt = 90;
|
int wt = 90;
|
||||||
bool qp = false;
|
bool qp = false;
|
||||||
|
@ -367,13 +403,13 @@ int main(int argc, char **argv){
|
||||||
int wtm = wt / 60;
|
int wtm = wt / 60;
|
||||||
int wts = wt - wtm * 60;
|
int wts = wt - wtm * 60;
|
||||||
wt--;
|
wt--;
|
||||||
cout << Mv::to(Term.height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush;
|
cout << Mv::to(Term::height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush;
|
||||||
//chr = Key(1000);
|
//chr = Key(1000);
|
||||||
if (Input(1000)) {
|
if (Input::poll(1000)) {
|
||||||
key = Input();
|
key = Input::get();
|
||||||
cout << Mv::to(Term.height - 2, 1) << "Last key: LEN=" << key.size() << " ULEN=" << ulen(key) << " KEY=\"" << key << "\" CODE=" << (int)key.at(0) << " " << flush;
|
cout << Mv::to(Term::height - 2, 1) << "Last key: LEN=" << key.size() << " ULEN=" << ulen(key) << " KEY=\"" << key << "\" CODE=" << (int)key.at(0) << " " << flush;
|
||||||
full += key;
|
full += key;
|
||||||
cout << Mv::to(Term.height - 5, 1) << full << flush;
|
cout << Mv::to(Term::height - 5, 1) << full << flush;
|
||||||
if (key == "q") qp = true;
|
if (key == "q") qp = true;
|
||||||
key = "";
|
key = "";
|
||||||
wt++;
|
wt++;
|
||||||
|
@ -381,8 +417,8 @@ int main(int argc, char **argv){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug == 1) Input(-1);
|
if (debug == 1) Input::wait();
|
||||||
Term.restore();
|
Term::restore();
|
||||||
if (debug < 2) cout << Term.normal_screen << Term.show_cursor << flush;
|
if (debug < 2) cout << Term::normal_screen << Term::show_cursor << flush;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,21 +28,12 @@ tab-size = 4
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
//? Classes, functions and variables for reading and writing the btop config file
|
//* Functions and variables for reading and writing the btop config file
|
||||||
|
namespace Config {
|
||||||
|
namespace {
|
||||||
|
|
||||||
#define Bool bool()
|
bool changed = false;
|
||||||
#define Int int()
|
|
||||||
#define String string()
|
|
||||||
|
|
||||||
|
|
||||||
//* Used for classes and functions needing pre-initialised values
|
|
||||||
namespace State {
|
|
||||||
bool truecolor = true;
|
|
||||||
string fg, bg;
|
|
||||||
uint width, height;
|
|
||||||
}
|
|
||||||
|
|
||||||
class C_Config {
|
|
||||||
map<string, string> strings = {
|
map<string, string> strings = {
|
||||||
{"color_theme", "Default"},
|
{"color_theme", "Default"},
|
||||||
{"shown_boxes", "cpu mem net proc"},
|
{"shown_boxes", "cpu mem net proc"},
|
||||||
|
@ -97,29 +88,45 @@ class C_Config {
|
||||||
{"proc_update_mult", 2},
|
{"proc_update_mult", 2},
|
||||||
{"tree_depth", 3}
|
{"tree_depth", 3}
|
||||||
};
|
};
|
||||||
public:
|
};
|
||||||
C_Config(){
|
|
||||||
bools["truecolor"] = "true";
|
|
||||||
strings["color_theme"] = "Default";
|
|
||||||
ints["tree_depth"] = 3;
|
|
||||||
|
|
||||||
State::truecolor = bools["truecolor"];
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Return config value <name> as a bool
|
//* Return config value <name> as a bool
|
||||||
bool operator()(bool b_type, string name){
|
bool& getB(string name){
|
||||||
return bools.at(name);
|
return bools.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return config value <name> as a int
|
//* Return config value <name> as a int
|
||||||
int operator()(int i_type, string name){
|
int& getI(string name){
|
||||||
return ints.at(name);
|
return ints.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return config value <name> as a string
|
//* Return config value <name> as a string
|
||||||
string operator()(string s_type, string name){
|
string& getS(string name){
|
||||||
return strings.at(name);
|
return strings.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//* Set config value <name> to bool <value>
|
||||||
|
void setB(string name, bool value){
|
||||||
|
bools.at(name) = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Set config value <name> to int <value>
|
||||||
|
void setI(string name, int value){
|
||||||
|
ints.at(name) = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Set config value <name> to string <value>
|
||||||
|
void setS(string name, string value){
|
||||||
|
strings.at(name) = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(string source){
|
||||||
|
(void)source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -29,11 +29,9 @@ tab-size = 4
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
//* Class for handling keyboard and mouse input
|
//* Functions and variables for handling keyboard and mouse input
|
||||||
class C_Input {
|
namespace Input {
|
||||||
|
namespace {
|
||||||
string last = "";
|
|
||||||
|
|
||||||
//* Map for translating key codes to readable values
|
//* Map for translating key codes to readable values
|
||||||
const map<string, string> Key_escapes = {
|
const map<string, string> Key_escapes = {
|
||||||
{"\033", "escape"},
|
{"\033", "escape"},
|
||||||
|
@ -70,18 +68,29 @@ class C_Input {
|
||||||
{"[23~", "f11"},
|
{"[23~", "f11"},
|
||||||
{"[24~", "f12"}
|
{"[24~", "f12"}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
bool wait(int timeout=0){
|
//* Last entered key
|
||||||
if (timeout == 0) return cin.rdbuf()->in_avail() > 0;
|
string last = "";
|
||||||
|
|
||||||
|
//* Poll keyboard & mouse input for <timeout> ms and return input availabilty as a bool
|
||||||
|
bool poll(int timeout=0){
|
||||||
|
if (timeout < 1) return cin.rdbuf()->in_avail() > 0;
|
||||||
auto timer = 0;
|
auto timer = 0;
|
||||||
while (timeout == -1 || timer * 10 <= timeout) {
|
while (timer * 10 <= timeout) {
|
||||||
if (cin.rdbuf()->in_avail() > 0) return true;
|
if (cin.rdbuf()->in_avail() > 0) return true;
|
||||||
sleep_ms(10);
|
sleep_ms(10);
|
||||||
if (timeout >= 0) ++timer;
|
++timer;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//* Wait until input is available
|
||||||
|
void wait(){
|
||||||
|
while (cin.rdbuf()->in_avail() < 1) sleep_ms(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Get a key or mouse action from input
|
||||||
string get(){
|
string get(){
|
||||||
string key;
|
string key;
|
||||||
while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get();
|
while (cin.rdbuf()->in_avail() > 0 && key.size() < 100) key += cin.get();
|
||||||
|
@ -89,28 +98,15 @@ class C_Input {
|
||||||
if (key.substr(0,2) == Fx::e) key.erase(0, 1);
|
if (key.substr(0,2) == Fx::e) key.erase(0, 1);
|
||||||
if (Key_escapes.contains(key)) key = Key_escapes.at(key);
|
if (Key_escapes.contains(key)) key = Key_escapes.at(key);
|
||||||
else if (ulen(key) > 1) key = "";
|
else if (ulen(key) > 1) key = "";
|
||||||
|
last = key;
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
void clear(){
|
||||||
|
last.clear();
|
||||||
//* Wait <timeout> ms for input on stdin and return true if available
|
|
||||||
//* -1 for infinite wait
|
|
||||||
bool operator()(int timeout){
|
|
||||||
if (wait(timeout)) {
|
|
||||||
last = get();
|
|
||||||
return !last.empty();
|
|
||||||
} else {
|
|
||||||
last = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return last entered key
|
|
||||||
string operator()(){
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -44,20 +44,11 @@ namespace Global {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Processes {
|
namespace Proc {
|
||||||
|
namespace {
|
||||||
uint64_t tstamp;
|
uint64_t tstamp;
|
||||||
long int clk_tck;
|
long int clk_tck;
|
||||||
map<int, tuple<string, string, string>> cache;
|
map<int, tuple<string, string, string>> cache;
|
||||||
map<string, uint> sorts = {
|
|
||||||
{"pid", 0},
|
|
||||||
{"name", 1},
|
|
||||||
{"command", 2},
|
|
||||||
{"threads", 3},
|
|
||||||
{"user", 4},
|
|
||||||
{"memory", 5},
|
|
||||||
{"cpu direct", 6},
|
|
||||||
{"cpu lazy", 7}
|
|
||||||
};
|
|
||||||
map<string, string> uid_user;
|
map<string, string> uid_user;
|
||||||
fs::path passwd_path;
|
fs::path passwd_path;
|
||||||
fs::file_time_type passwd_time;
|
fs::file_time_type passwd_time;
|
||||||
|
@ -65,9 +56,23 @@ class Processes {
|
||||||
map<int, uint64_t> cpu_second;
|
map<int, uint64_t> cpu_second;
|
||||||
uint counter = 0;
|
uint counter = 0;
|
||||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||||
public:
|
}
|
||||||
|
|
||||||
atomic<bool> stop;
|
atomic<bool> stop;
|
||||||
atomic<bool> running;
|
atomic<bool> running;
|
||||||
|
vector<string> sort_vector = {
|
||||||
|
"pid",
|
||||||
|
"name",
|
||||||
|
"command",
|
||||||
|
"threads",
|
||||||
|
"user",
|
||||||
|
"memory",
|
||||||
|
"cpu direct",
|
||||||
|
"cpu lazy",
|
||||||
|
};
|
||||||
|
map<string, uint> sort_map;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//* Collects process information from /proc and returns a vector of tuples
|
//* Collects process information from /proc and returns a vector of tuples
|
||||||
auto collect(string sorting="pid", bool reverse=false, string filter=""){
|
auto collect(string sorting="pid", bool reverse=false, string filter=""){
|
||||||
|
@ -81,7 +86,7 @@ public:
|
||||||
auto since_last = time_ms() - tstamp;
|
auto since_last = time_ms() - tstamp;
|
||||||
if (since_last < 1) since_last = 1;
|
if (since_last < 1) since_last = 1;
|
||||||
auto uptime = system_uptime();
|
auto uptime = system_uptime();
|
||||||
auto sortint = (sorts.contains(sorting)) ? sorts[sorting] : 5;
|
auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7;
|
||||||
vector<string> pstat;
|
vector<string> pstat;
|
||||||
|
|
||||||
//? Return type! Values in tuple: pid, program, command, threads, username, mem KiB, cpu%, cpu cumulative
|
//? Return type! Values in tuple: pid, program, command, threads, username, mem KiB, cpu%, cpu cumulative
|
||||||
|
@ -121,7 +126,7 @@ public:
|
||||||
if (d.is_directory() && isdigit(pid_str[0])) {
|
if (d.is_directory() && isdigit(pid_str[0])) {
|
||||||
pid = stoi(pid_str);
|
pid = stoi(pid_str);
|
||||||
|
|
||||||
//* Get cpu usage, threads and rss mem from [pid]/stat
|
//* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat
|
||||||
if (fs::exists((string)d.path() + "/stat")) {
|
if (fs::exists((string)d.path() + "/stat")) {
|
||||||
pread.clear(); pstat.clear();
|
pread.clear(); pstat.clear();
|
||||||
ifstream pread((string)d.path() + "/stat");
|
ifstream pread((string)d.path() + "/stat");
|
||||||
|
@ -140,9 +145,6 @@ public:
|
||||||
//? Cache process start time
|
//? Cache process start time
|
||||||
if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]);
|
if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]);
|
||||||
|
|
||||||
//? Get RSS memory in KiB (will be overriden by /status if available)
|
|
||||||
rss_mem = (stoull(pstat[23]) * page_size) >> 10;
|
|
||||||
|
|
||||||
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
|
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
|
||||||
cpu = static_cast<double>(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck;
|
cpu = static_cast<double>(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck;
|
||||||
|
|
||||||
|
@ -151,23 +153,16 @@ public:
|
||||||
cpu_times[pid] = cpu_t;
|
cpu_times[pid] = cpu_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Get RSS memory in KiB
|
//* Get RSS memory in bytes from /proc/[pid]/statm
|
||||||
if (fs::exists((string)d.path() + "/status")) {
|
if (fs::exists((string)d.path() + "/statm")) {
|
||||||
pread.clear(); status.clear(); tmpstr.clear();
|
pread.clear(); tmpstr.clear();
|
||||||
ifstream pread((string)d.path() + "/status");
|
ifstream pread((string)d.path() + "/statm");
|
||||||
if (pread.good()) {
|
if (pread.good()) {
|
||||||
while (getline(pread, status, ':')){
|
pread.ignore(numeric_limits<streamsize>::max(), ' ');
|
||||||
if (status == "VmRSS") {
|
pread >> rss_mem;
|
||||||
pread.ignore();
|
rss_mem *= page_size;
|
||||||
pread >> ws;
|
|
||||||
getline(pread, tmpstr, 'k');
|
|
||||||
tmpstr.pop_back();
|
|
||||||
break;
|
|
||||||
} else pread.ignore(numeric_limits<streamsize>::max(), '\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pread.close();
|
pread.close();
|
||||||
if (!tmpstr.empty()) rss_mem = stoull(tmpstr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Cache program name, command and username
|
//* Cache program name, command and username
|
||||||
|
@ -263,12 +258,14 @@ public:
|
||||||
return procs;
|
return procs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Processes() {
|
void init(){
|
||||||
clk_tck = sysconf(_SC_CLK_TCK);
|
clk_tck = sysconf(_SC_CLK_TCK);
|
||||||
tstamp = time_ms();
|
tstamp = time_ms();
|
||||||
stop.store(false);
|
stop.store(false);
|
||||||
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path;
|
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path;
|
||||||
collect();
|
uint i = 0;
|
||||||
|
for (auto& item : sort_vector) sort_map[item] = i++;
|
||||||
|
// collect();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,22 +30,24 @@ tab-size = 4
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
|
namespace Theme {
|
||||||
|
|
||||||
//* Convert 24-bit colors to 256 colors using 6x6x6 color cube
|
namespace {
|
||||||
int truecolor_to_256(uint r, uint g, uint b){
|
//* Convert 24-bit colors to 256 colors using 6x6x6 color cube
|
||||||
|
int truecolor_to_256(uint r, uint g, uint b){
|
||||||
if (r / 11 == g / 11 && g / 11 == b / 11) {
|
if (r / 11 == g / 11 && g / 11 == b / 11) {
|
||||||
return 232 + r / 11;
|
return 232 + r / 11;
|
||||||
} else {
|
} else {
|
||||||
return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16;
|
return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
||||||
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
|
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
|
||||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||||
string hex_to_color(string hexa, bool t_to_256=false, string depth="fg"){
|
string hex_to_color(string hexa, bool t_to_256=false, string depth="fg"){
|
||||||
if (hexa.size() > 1){
|
if (hexa.size() > 1){
|
||||||
hexa.erase(0, 1);
|
hexa.erase(0, 1);
|
||||||
for (auto& c : hexa) if (!isxdigit(c)) return "";
|
for (auto& c : hexa) if (!isxdigit(c)) return "";
|
||||||
|
@ -77,13 +79,13 @@ string hex_to_color(string hexa, bool t_to_256=false, string depth="fg"){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
||||||
//* Args r: [0-255], g: [0-255], b: [0-255]
|
//* Args r: [0-255], g: [0-255], b: [0-255]
|
||||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||||
string dec_to_color(uint r, uint g, uint b, bool t_to_256=false, string depth="fg"){
|
string dec_to_color(uint r, uint g, uint b, bool t_to_256=false, string depth="fg"){
|
||||||
depth = (depth == "fg") ? "38" : "48";
|
depth = (depth == "fg") ? "38" : "48";
|
||||||
string pre = Fx::e + depth + ";";
|
string pre = Fx::e + depth + ";";
|
||||||
pre += (t_to_256) ? "5;" : "2;";
|
pre += (t_to_256) ? "5;" : "2;";
|
||||||
|
@ -92,10 +94,10 @@ string dec_to_color(uint r, uint g, uint b, bool t_to_256=false, string depth="f
|
||||||
b = (b > 255) ? 255 : b;
|
b = (b > 255) ? 255 : b;
|
||||||
if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m";
|
if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m";
|
||||||
else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m";
|
else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m";
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string
|
//* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string
|
||||||
map<string, int> c_to_rgb(string c_string){
|
map<string, int> rgb(string c_string){
|
||||||
map<string, int> rgb = {{"r", 0}, {"g", 0}, {"b", 0}};
|
map<string, int> rgb = {{"r", 0}, {"g", 0}, {"b", 0}};
|
||||||
if (c_string.size() >= 14){
|
if (c_string.size() >= 14){
|
||||||
c_string.erase(0, 7);
|
c_string.erase(0, 7);
|
||||||
|
@ -107,14 +109,13 @@ map<string, int> c_to_rgb(string c_string){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rgb;
|
return rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
//? --------------------------------------------------- CLASSES -----------------------------------------------------
|
namespace {
|
||||||
|
map<string, string> color;
|
||||||
class C_Theme {
|
map<string, vector<string>> gradient;
|
||||||
map<string, string> c;
|
|
||||||
map<string, vector<string>> g;
|
|
||||||
|
|
||||||
|
//* Generate the theme
|
||||||
map<string,string> generate(map<string, string>& source){
|
map<string,string> generate(map<string, string>& source){
|
||||||
map<string, string> out;
|
map<string, string> out;
|
||||||
vector<string> t_rgb;
|
vector<string> t_rgb;
|
||||||
|
@ -122,46 +123,36 @@ class C_Theme {
|
||||||
for (auto& item : Global::Default_theme) {
|
for (auto& item : Global::Default_theme) {
|
||||||
depth = (item.first.ends_with("bg")) ? "bg" : "fg";
|
depth = (item.first.ends_with("bg")) ? "bg" : "fg";
|
||||||
if (source.contains(item.first)) {
|
if (source.contains(item.first)) {
|
||||||
if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !State::truecolor, depth);
|
if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !Config::getB("truecolor"), depth);
|
||||||
else {
|
else {
|
||||||
t_rgb = ssplit(source.at(item.first), " ");
|
t_rgb = ssplit(source.at(item.first), " ");
|
||||||
out[item.first] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !State::truecolor, depth);
|
out[item.first] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !Config::getB("truecolor"), depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else out[item.first] = "";
|
else out[item.first] = "";
|
||||||
if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !State::truecolor, depth);
|
if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !Config::getB("truecolor"), depth);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
//* Change to theme <source>
|
|
||||||
void change(map<string, string> source){
|
|
||||||
c = generate(source);
|
|
||||||
State::fg = c.at("main_fg");
|
|
||||||
State::bg = c.at("main_bg");
|
|
||||||
Fx::reset = Fx::reset_base + State::fg + State::bg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Generate theme from <source> map, default to DEFAULT_THEME on missing or malformatted values
|
|
||||||
C_Theme(map<string, string> source){
|
//* Set current theme using <source> map
|
||||||
change(source);
|
void set(map<string, string> source){
|
||||||
|
color = generate(source);
|
||||||
|
Term::fg = color.at("main_fg");
|
||||||
|
Term::bg = color.at("main_bg");
|
||||||
|
Fx::reset = Fx::reset_base + Term::fg + Term::bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return escape code for color <name>
|
//* Return escape code for color <name>
|
||||||
auto operator()(string name){
|
auto c(string name){
|
||||||
return c.at(name);
|
return color.at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return vector of escape codes for color gradient <name>
|
//* Return vector of escape codes for color gradient <name>
|
||||||
auto gradient(string name){
|
auto g(string name){
|
||||||
return g.at(name);
|
return gradient.at(name);
|
||||||
}
|
|
||||||
|
|
||||||
//* Return map of decimal int's (r, g, b) for color <name>
|
|
||||||
auto rgb(string name){
|
|
||||||
return c_to_rgb(c.at(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
262
src/btop_tools.h
262
src/btop_tools.h
|
@ -27,6 +27,7 @@ tab-size = 4
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
@ -42,7 +43,7 @@ using namespace std;
|
||||||
//* Collection of escape codes for text style and formatting
|
//* Collection of escape codes for text style and formatting
|
||||||
namespace Fx {
|
namespace Fx {
|
||||||
//* Escape sequence start
|
//* Escape sequence start
|
||||||
const string e = "\033[";
|
const string e = "\x1b[";
|
||||||
|
|
||||||
//* Bold on
|
//* Bold on
|
||||||
const string b = e + "1m";
|
const string b = e + "1m";
|
||||||
|
@ -67,6 +68,17 @@ namespace Fx {
|
||||||
|
|
||||||
//* Reset text effects and restore default foregrund and background color < Changed by C_Theme
|
//* Reset text effects and restore default foregrund and background color < Changed by C_Theme
|
||||||
string reset = reset_base;
|
string reset = reset_base;
|
||||||
|
|
||||||
|
//* Regex for matching color, style and curse move escape sequences
|
||||||
|
const regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}");
|
||||||
|
|
||||||
|
//* Regex for matching only color and style escape sequences
|
||||||
|
const regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}");
|
||||||
|
|
||||||
|
//* Return a string with all colors and text styling removed
|
||||||
|
string uncolor(string& s){
|
||||||
|
return regex_replace(s, color_regex, "");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Collection of escape codes and functions for cursor manipulation
|
//* Collection of escape codes and functions for cursor manipulation
|
||||||
|
@ -93,12 +105,121 @@ namespace Mv {
|
||||||
const string restore = Fx::e + "u";
|
const string restore = Fx::e + "u";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//* Collection of escape codes and functions for terminal manipulation
|
||||||
|
namespace Term {
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
bool resized = false;
|
||||||
|
uint width = 0;
|
||||||
|
uint height = 0;
|
||||||
|
string fg, bg;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct termios initial_settings;
|
||||||
|
|
||||||
|
//* Toggle terminal input echo
|
||||||
|
bool echo(bool on=true){
|
||||||
|
struct termios settings;
|
||||||
|
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
||||||
|
if (on) settings.c_lflag |= ECHO;
|
||||||
|
else settings.c_lflag &= ~(ECHO);
|
||||||
|
return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Refresh variables holding current terminal width and height and return true if resized
|
||||||
|
bool refresh(){
|
||||||
|
struct winsize w;
|
||||||
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||||
|
resized = (width != w.ws_col || height != w.ws_row) ? true : false;
|
||||||
|
width = w.ws_col;
|
||||||
|
height = w.ws_row;
|
||||||
|
return resized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//* Hide terminal cursor
|
||||||
|
const string hide_cursor = Fx::e + "?25l";
|
||||||
|
|
||||||
|
//* Show terminal cursor
|
||||||
|
const string show_cursor = Fx::e + "?25h";
|
||||||
|
|
||||||
|
//* Switch to alternate screen
|
||||||
|
const string alt_screen = Fx::e + "?1049h";
|
||||||
|
|
||||||
|
//* Switch to normal screen
|
||||||
|
const string normal_screen = Fx::e + "?1049l";
|
||||||
|
|
||||||
|
//* Clear screen and set cursor to position 0,0
|
||||||
|
const string clear = Fx::e + "2J" + Fx::e + "0;0f";
|
||||||
|
|
||||||
|
//* Clear from cursor to end of screen
|
||||||
|
const string clear_end = Fx::e + "0J";
|
||||||
|
|
||||||
|
//* Clear from cursor to beginning of screen
|
||||||
|
const string clear_begin = Fx::e + "1J";
|
||||||
|
|
||||||
|
//* Enable reporting of mouse position on click and release
|
||||||
|
const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h";
|
||||||
|
|
||||||
|
//* Disable mouse reporting
|
||||||
|
const string mouse_off = Fx::e + "?1002l";
|
||||||
|
|
||||||
|
//* Enable reporting of mouse position at any movement
|
||||||
|
const string mouse_direct_on = Fx::e + "?1003h";
|
||||||
|
|
||||||
|
//* Disable direct mouse reporting
|
||||||
|
const string mouse_direct_off = Fx::e + "?1003l";
|
||||||
|
|
||||||
|
//* Toggle need for return key when reading input
|
||||||
|
bool linebuffered(bool on=true){
|
||||||
|
struct termios settings;
|
||||||
|
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
||||||
|
if (on) settings.c_lflag |= ICANON;
|
||||||
|
else settings.c_lflag &= ~(ICANON);
|
||||||
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
||||||
|
if (on) setlinebuf(stdin);
|
||||||
|
else setbuf(stdin, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//* Check for a valid tty, save terminal options and set new options
|
||||||
|
bool init(){
|
||||||
|
if (!initialized){
|
||||||
|
initialized = (bool)isatty(STDIN_FILENO);
|
||||||
|
if (initialized) {
|
||||||
|
initialized = (0 == tcgetattr(STDIN_FILENO, &initial_settings));
|
||||||
|
cin.sync_with_stdio(false);
|
||||||
|
cin.tie(NULL);
|
||||||
|
echo(false);
|
||||||
|
linebuffered(false);
|
||||||
|
refresh();
|
||||||
|
resized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Restore terminal options
|
||||||
|
void restore(){
|
||||||
|
if (initialized) {
|
||||||
|
echo(true);
|
||||||
|
linebuffered(true);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings);
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
|
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
|
||||||
|
|
||||||
//* Return number of UTF8 characters in a string
|
//* Return number of UTF8 characters in a string with option to disregard escape sequences
|
||||||
inline size_t ulen(string s){
|
inline size_t ulen(string s, bool escape=false){
|
||||||
|
if (escape) s = regex_replace(s, Fx::escape_regex, "");
|
||||||
return std::count_if(s.begin(), s.end(),
|
return std::count_if(s.begin(), s.end(),
|
||||||
[](char& c) { return (c & 0xC0) != 0x80; } );
|
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Return current time since epoch in milliseconds
|
//* Return current time since epoch in milliseconds
|
||||||
|
@ -157,38 +278,40 @@ void sleep_ms(uint ms) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||||
string ljustify(string str, size_t x, bool utf=false, bool lim=true){
|
string ljustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){
|
||||||
if (!utf) {
|
if (utf || escape) {
|
||||||
if (lim && str.size() > x) str.resize(x);
|
if (!escape && lim && ulen(str) > x) {
|
||||||
return str + string(max((int)(x - str.size()), 0), ' ');
|
|
||||||
} else {
|
|
||||||
if (lim && ulen(str) > x) {
|
|
||||||
auto i = str.size();
|
auto i = str.size();
|
||||||
while (ulen(str) > x) str.resize(--i);
|
while (ulen(str) > x) str.resize(--i);
|
||||||
}
|
}
|
||||||
return str + string(max((int)(x - ulen(str)), 0), ' ');
|
return str + string(max((int)(x - ulen(str, escape)), 0), ' ');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lim && str.size() > x) str.resize(x);
|
||||||
|
return str + string(max((int)(x - str.size()), 0), ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||||
string rjustify(string str, size_t x, bool utf=false, bool lim=true){
|
string rjustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){
|
||||||
if (!utf) {
|
if (utf || escape) {
|
||||||
if (lim && str.size() > x) str.resize(x);
|
if (!escape && lim && ulen(str) > x) {
|
||||||
return string(max((int)(x - str.size()), 0), ' ') + str;
|
|
||||||
} else {
|
|
||||||
if (lim && ulen(str) > x) {
|
|
||||||
auto i = str.size();
|
auto i = str.size();
|
||||||
while (ulen(str) > x) str.resize(--i);
|
while (ulen(str) > x) str.resize(--i);
|
||||||
}
|
}
|
||||||
return string(max((int)(x - ulen(str)), 0), ' ') + str;
|
return string(max((int)(x - ulen(str, escape)), 0), ' ') + str;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lim && str.size() > x) str.resize(x);
|
||||||
|
return string(max((int)(x - str.size()), 0), ' ') + str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Trim trailing characters if utf8 string length is greatear than <x>
|
//* Trim trailing characters if utf8 string length is greatear than <x>
|
||||||
string uresize(string str, size_t x){
|
string uresize(string str, size_t x){
|
||||||
auto i = str.size();
|
// auto i = str.size();
|
||||||
if (i < 1 || x < 1) return str;
|
if (str.empty()) return str;
|
||||||
while (ulen(str) > x) str.resize(--i);
|
while (ulen(str) > x) str.pop_back();
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,104 +392,7 @@ string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool
|
||||||
|
|
||||||
//? --------------------------------------------------- CLASSES -----------------------------------------------------
|
//? --------------------------------------------------- CLASSES -----------------------------------------------------
|
||||||
|
|
||||||
//* Collection of escape codes and functions for terminal manipulation
|
|
||||||
class C_Term {
|
|
||||||
struct termios initial_settings;
|
|
||||||
public:
|
|
||||||
bool initialized = false;
|
|
||||||
bool resized = false;
|
|
||||||
uint width = 0;
|
|
||||||
uint height = 0;
|
|
||||||
|
|
||||||
//* Hide terminal cursor
|
|
||||||
const string hide_cursor = Fx::e + "?25l";
|
|
||||||
|
|
||||||
//* Show terminal cursor
|
|
||||||
const string show_cursor = Fx::e + "?25h";
|
|
||||||
|
|
||||||
//* Switch to alternate screen
|
|
||||||
const string alt_screen = Fx::e + "?1049h";
|
|
||||||
|
|
||||||
//* Switch to normal screen
|
|
||||||
const string normal_screen = Fx::e + "?1049l";
|
|
||||||
|
|
||||||
//* Clear screen and set cursor to position 0,0
|
|
||||||
const string clear = Fx::e + "2J" + Fx::e + "0;0f";
|
|
||||||
|
|
||||||
//* Enable reporting of mouse position on click and release
|
|
||||||
const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h";
|
|
||||||
|
|
||||||
//* Disable mouse reporting
|
|
||||||
const string mouse_off = Fx::e + "?1002l";
|
|
||||||
|
|
||||||
//* Enable reporting of mouse position at any movement
|
|
||||||
const string mouse_direct_on = Fx::e + "?1003h";
|
|
||||||
|
|
||||||
//* Disable direct mouse reporting
|
|
||||||
const string mouse_direct_off = Fx::e + "?1003l";
|
|
||||||
|
|
||||||
//* Toggle need for return key when reading input
|
|
||||||
bool linebuffered(bool on=true){
|
|
||||||
struct termios settings;
|
|
||||||
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
|
||||||
if (on) settings.c_lflag |= ICANON;
|
|
||||||
else settings.c_lflag &= ~(ICANON);
|
|
||||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
|
||||||
if (on) setlinebuf(stdin);
|
|
||||||
else setbuf(stdin, NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Toggle terminal input echo
|
|
||||||
bool echo(bool on=true){
|
|
||||||
struct termios settings;
|
|
||||||
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
|
||||||
if (on) settings.c_lflag |= ECHO;
|
|
||||||
else settings.c_lflag &= ~(ECHO);
|
|
||||||
return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Refresh variables holding current terminal width and height and return true if resized
|
|
||||||
bool refresh(){
|
|
||||||
struct winsize w;
|
|
||||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
|
||||||
resized = (width != w.ws_col || height != w.ws_row) ? true : false;
|
|
||||||
State::width = width = w.ws_col;
|
|
||||||
State::height = height = w.ws_row;
|
|
||||||
return resized;
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Check for a valid tty, save terminal options and set new options
|
|
||||||
bool init(){
|
|
||||||
if (!initialized){
|
|
||||||
initialized = (bool)isatty(STDIN_FILENO);
|
|
||||||
if (initialized) {
|
|
||||||
initialized = (0 == tcgetattr(STDIN_FILENO, &initial_settings));
|
|
||||||
cin.sync_with_stdio(false);
|
|
||||||
cin.tie(NULL);
|
|
||||||
echo(false);
|
|
||||||
linebuffered(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Restore terminal options
|
|
||||||
void restore(){
|
|
||||||
if (initialized) {
|
|
||||||
echo(true);
|
|
||||||
linebuffered(true);
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings);
|
|
||||||
initialized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
C_Term() {
|
|
||||||
init();
|
|
||||||
refresh();
|
|
||||||
resized = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//? --------------------------------------------------- STRUCTS -------------------------------------------------------
|
//? --------------------------------------------------- STRUCTS -------------------------------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue