Namespaces < Classes

This commit is contained in:
aristocratos 2021-05-14 18:54:37 +02:00
parent 05eb21dfbb
commit 833d253276
8 changed files with 538 additions and 487 deletions

View file

@ -1,2 +0,0 @@
file(GLOB sources *.h *.c *.cxx *.cpp *.hxx)
add_executable(btop ${sources})

View file

@ -1,7 +1,7 @@
PREFIX ?= /usr/local
DOCDIR ?= $(PREFIX)/share/btop/doc
CXX = g++
CXXFLAGS = -std=c++20 -pthread -Wall
override CXXFLAGS += -std=c++20 -pthread -Wall -Wextra
INCLUDES = -I./src
btop: btop.cpp

192
btop.cpp
View file

@ -66,6 +66,9 @@ namespace Global {
};
const string Version = "0.0.1";
string banner;
uint banner_width;
}
@ -90,46 +93,42 @@ void argumentParser(int argc, char **argv){
}
}
class C_Banner {
string banner_str;
public:
int width = 0;
C_Banner(){
size_t z = 0;
string b_color, bg, fg, out, oc, letter;
int bg_i;
int new_len;
for (auto line: Global::Banner_src) {
new_len = ulen(line[1]);
if (new_len > width) width = new_len;
fg = hex_to_color(line[0]);
bg_i = 120-z*12;
bg = dec_to_color(bg_i, bg_i, bg_i);
for (uint 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) out += b_color;
out += letter;
oc = b_color;
//* Generate the btop++ banner
auto create_banner(){
struct out_vals {
uint w;
string s;
};
size_t z = 0;
uint width=0, new_len=0;
string b_color, bg, fg, out, oc, letter;
bool truecolor = Config::getB("truecolor");
int bg_i;
for (auto line: Global::Banner_src) {
if ((new_len = ulen(line[1])) > width) width = new_len;
fg = Theme::hex_to_color(line[0], !truecolor);
bg_i = 120-z*12;
bg = Theme::dec_to_color(bg_i, bg_i, bg_i, !truecolor);
for (uint i = 0; i < line[1].size(); i += 3) {
if (line[1][i] == ' '){
letter = ' ';
i -= 2;
} else{
letter = line[1].substr(i, 3);
}
z++;
if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1);
b_color = (letter == "") ? fg : bg;
if (b_color != oc) out += b_color;
out += letter;
oc = b_color;
}
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;
z++;
if (z < Global::Banner_src.size()) out += Mv::l(new_len) + Mv::d(1);
}
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
string my_worker(int x){
@ -159,41 +158,38 @@ int main(int argc, char **argv){
}
//? Initialize terminal and set options
C_Term Term;
if (!Term.initialized) {
if (!Term::init()) {
cout << "ERROR: No tty detected!" << endl;
cout << "Sorry, btop++ needs an interactive shell to run." << endl;
exit(1);
}
//? Read config file if present
C_Config Config;
Config::load("____");
//? Generate the theme
C_Theme Theme(Global::Default_theme);
Theme::set(Global::Default_theme);
//? 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 ------------------------------------------------------
int debug = 2;
int debug = 0;
int tests = 0;
// cout << Theme("main_bg") << Term.clear << flush;
// cout << Theme("main_bg") << Term::clear << flush;
bool thread_test = false;
if (debug < 2) cout << Term.alt_screen << Term.clear << 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;
if (debug < 2) cout << Term::alt_screen << Term::hide_cursor << flush;
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
@ -215,7 +211,7 @@ int main(int argc, char **argv){
int i = 0;
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) {
i = 0;
cout << endl;
@ -258,11 +254,14 @@ int main(int argc, char **argv){
auto timestamp = time_ms();
Processes Proc;
Proc::init();
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
@ -270,26 +269,63 @@ int main(int argc, char **argv){
uint lc;
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;
while (Input() != "q") {
while (key != "q") {
timestamp = time_ms();
auto plist = Proc.collect("cpu lazy", false);
timestamp = time_ms() - timestamp;
tsl = timestamp + timer;
auto plist = Proc::collect(Proc::sort_vector[sortint], reversing, filter);
timestamp2 = time_ms();
timestamp = timestamp2 - timestamp;
ostring.clear();
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){
(void) lcpu_s;
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, ' ');
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), 5) + string(11, ' ');
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
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;
@ -301,15 +337,15 @@ int main(int argc, char **argv){
if (tests>3){
auto nbcolor = hex_to_color(Global::Default_theme.at("net_box"));
auto nbcolor_rgb = c_to_rgb(nbcolor);
auto nbcolor = Theme::hex_to_color(Global::Default_theme.at("net_box"));
auto nbcolor_rgb = Theme::rgb(nbcolor);
auto nbcolor_man = ssplit(nbcolor, ";");
cout << nbcolor << "Some color" << 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 << "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;
}
@ -357,9 +393,9 @@ int main(int argc, char **argv){
if (debug == 0){
cout << Theme("main_fg");
cout << Mv::to(Term.height - 1, 0) << "Press q to exit! Timeout" << flush;
if (debug == 3){
cout << Theme::c("main_fg");
cout << Mv::to(Term::height - 1, 0) << "Press q to exit! Timeout" << flush;
string full, key;
int wt = 90;
bool qp = false;
@ -367,13 +403,13 @@ int main(int argc, char **argv){
int wtm = wt / 60;
int wts = wt - wtm * 60;
wt--;
cout << Mv::to(Term.height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush;
cout << Mv::to(Term::height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush;
//chr = Key(1000);
if (Input(1000)) {
key = Input();
cout << Mv::to(Term.height - 2, 1) << "Last key: LEN=" << key.size() << " ULEN=" << ulen(key) << " KEY=\"" << key << "\" CODE=" << (int)key.at(0) << " " << flush;
if (Input::poll(1000)) {
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;
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;
key = "";
wt++;
@ -381,8 +417,8 @@ int main(int argc, char **argv){
}
}
if (debug == 1) Input(-1);
Term.restore();
if (debug < 2) cout << Term.normal_screen << Term.show_cursor << flush;
if (debug == 1) Input::wait();
Term::restore();
if (debug < 2) cout << Term::normal_screen << Term::show_cursor << flush;
return 0;
}

View file

@ -28,98 +28,105 @@ tab-size = 4
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()
#define Int int()
#define String string()
bool changed = false;
//* 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 = {
{"color_theme", "Default"},
{"shown_boxes", "cpu mem net proc"},
{"proc_sorting", "cpu lazy"},
{"cpu_graph_upper", "total"},
{"cpu_graph_lower", "total"},
{"cpu_sensor", "Auto"},
{"temp_scale", "celsius"},
{"draw_clock", "%X"},
{"custom_cpu_name", ""},
{"disks_filter", ""},
{"io_graph_speeds", ""},
{"net_download", "10M"},
{"net_upload", "10M"},
{"net_iface", ""},
{"log_level", "WARNING"}
map<string, string> strings = {
{"color_theme", "Default"},
{"shown_boxes", "cpu mem net proc"},
{"proc_sorting", "cpu lazy"},
{"cpu_graph_upper", "total"},
{"cpu_graph_lower", "total"},
{"cpu_sensor", "Auto"},
{"temp_scale", "celsius"},
{"draw_clock", "%X"},
{"custom_cpu_name", ""},
{"disks_filter", ""},
{"io_graph_speeds", ""},
{"net_download", "10M"},
{"net_upload", "10M"},
{"net_iface", ""},
{"log_level", "WARNING"}
};
map<string, bool> bools = {
{"theme_background", true},
{"truecolor", true},
{"proc_reversed", false},
{"proc_tree", false},
{"proc_colors", true},
{"proc_gradient", true},
{"proc_per_core", false},
{"proc_mem_bytes", true},
{"cpu_invert_lower", true},
{"cpu_single_graph", false},
{"show_uptime", true},
{"check_temp", true},
{"show_coretemp", true},
{"show_cpu_freq", true},
{"background_update", true},
{"update_check", true},
{"mem_graphs", true},
{"show_swap", true},
{"swap_disk", true},
{"show_disks", true},
{"only_physical", true},
{"use_fstab", false},
{"show_io_stat", true},
{"io_mode", false},
{"io_graph_combined", false},
{"net_color_fixed", false},
{"net_auto", true},
{"net_sync", false},
{"show_battery", true},
{"show_init", false}
};
map<string, int> ints = {
{"update_ms", 2000},
{"proc_update_mult", 2},
{"tree_depth", 3}
};
};
map<string, bool> bools = {
{"theme_background", true},
{"truecolor", true},
{"proc_reversed", false},
{"proc_tree", false},
{"proc_colors", true},
{"proc_gradient", true},
{"proc_per_core", false},
{"proc_mem_bytes", true},
{"cpu_invert_lower", true},
{"cpu_single_graph", false},
{"show_uptime", true},
{"check_temp", true},
{"show_coretemp", true},
{"show_cpu_freq", true},
{"background_update", true},
{"update_check", true},
{"mem_graphs", true},
{"show_swap", true},
{"swap_disk", true},
{"show_disks", true},
{"only_physical", true},
{"use_fstab", false},
{"show_io_stat", true},
{"io_mode", false},
{"io_graph_combined", false},
{"net_color_fixed", false},
{"net_auto", true},
{"net_sync", false},
{"show_battery", true},
{"show_init", false}
};
map<string, int> ints = {
{"update_ms", 2000},
{"proc_update_mult", 2},
{"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
bool operator()(bool b_type, string name){
bool& getB(string name){
return bools.at(name);
}
//* Return config value <name> as a int
int operator()(int i_type, string name){
int& getI(string name){
return ints.at(name);
}
//* Return config value <name> as a string
string operator()(string s_type, string name){
string& getS(string 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

View file

@ -29,59 +29,68 @@ tab-size = 4
using namespace std;
//* Class for handling keyboard and mouse input
class C_Input {
string last = "";
//* Map for translating key codes to readable values
const map<string, string> Key_escapes = {
{"\033", "escape"},
{"\n", "enter"},
{" ", "space"},
{"\x7f", "backspace"},
{"\x08", "backspace"},
{"[A", "up"},
{"OA", "up"},
{"[B", "down"},
{"OB", "down"},
{"[D", "left"},
{"OD", "left"},
{"[C", "right"},
{"OC", "right"},
{"[2~", "insert"},
{"[3~", "delete"},
{"[H", "home"},
{"[F", "end"},
{"[5~", "page_up"},
{"[6~", "page_down"},
{"\t", "tab"},
{"[Z", "shift_tab"},
{"OP", "f1"},
{"OQ", "f2"},
{"OR", "f3"},
{"OS", "f4"},
{"[15~", "f5"},
{"[17~", "f6"},
{"[18~", "f7"},
{"[19~", "f8"},
{"[20~", "f9"},
{"[21~", "f10"},
{"[23~", "f11"},
{"[24~", "f12"}
//* Functions and variables for handling keyboard and mouse input
namespace Input {
namespace {
//* Map for translating key codes to readable values
const map<string, string> Key_escapes = {
{"\033", "escape"},
{"\n", "enter"},
{" ", "space"},
{"\x7f", "backspace"},
{"\x08", "backspace"},
{"[A", "up"},
{"OA", "up"},
{"[B", "down"},
{"OB", "down"},
{"[D", "left"},
{"OD", "left"},
{"[C", "right"},
{"OC", "right"},
{"[2~", "insert"},
{"[3~", "delete"},
{"[H", "home"},
{"[F", "end"},
{"[5~", "page_up"},
{"[6~", "page_down"},
{"\t", "tab"},
{"[Z", "shift_tab"},
{"OP", "f1"},
{"OQ", "f2"},
{"OR", "f3"},
{"OS", "f4"},
{"[15~", "f5"},
{"[17~", "f6"},
{"[18~", "f7"},
{"[19~", "f8"},
{"[20~", "f9"},
{"[21~", "f10"},
{"[23~", "f11"},
{"[24~", "f12"}
};
};
bool wait(int timeout=0){
if (timeout == 0) return cin.rdbuf()->in_avail() > 0;
//* Last entered key
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;
while (timeout == -1 || timer * 10 <= timeout) {
while (timer * 10 <= timeout) {
if (cin.rdbuf()->in_avail() > 0) return true;
sleep_ms(10);
if (timeout >= 0) ++timer;
++timer;
}
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 key;
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_escapes.contains(key)) key = Key_escapes.at(key);
else if (ulen(key) > 1) key = "";
last = key;
}
return key;
}
public:
//* 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;
}
void clear(){
last.clear();
}
//* Return last entered key
string operator()(){
return last;
}
};
#endif

View file

@ -44,30 +44,35 @@ namespace Global {
}
class Processes {
uint64_t tstamp;
long int clk_tck;
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;
fs::path passwd_path;
fs::file_time_type passwd_time;
map<int, uint64_t> cpu_times;
map<int, uint64_t> cpu_second;
uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
public:
namespace Proc {
namespace {
uint64_t tstamp;
long int clk_tck;
map<int, tuple<string, string, string>> cache;
map<string, string> uid_user;
fs::path passwd_path;
fs::file_time_type passwd_time;
map<int, uint64_t> cpu_times;
map<int, uint64_t> cpu_second;
uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
}
atomic<bool> stop;
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
auto collect(string sorting="pid", bool reverse=false, string filter=""){
@ -81,7 +86,7 @@ public:
auto since_last = time_ms() - tstamp;
if (since_last < 1) since_last = 1;
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;
//? 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])) {
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")) {
pread.clear(); pstat.clear();
ifstream pread((string)d.path() + "/stat");
@ -140,9 +145,6 @@ public:
//? Cache process start time
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
cpu = static_cast<double>(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck;
@ -151,23 +153,16 @@ public:
cpu_times[pid] = cpu_t;
}
//* Get RSS memory in KiB
if (fs::exists((string)d.path() + "/status")) {
pread.clear(); status.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/status");
//* Get RSS memory in bytes from /proc/[pid]/statm
if (fs::exists((string)d.path() + "/statm")) {
pread.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/statm");
if (pread.good()) {
while (getline(pread, status, ':')){
if (status == "VmRSS") {
pread.ignore();
pread >> ws;
getline(pread, tmpstr, 'k');
tmpstr.pop_back();
break;
} else pread.ignore(numeric_limits<streamsize>::max(), '\n');
}
pread.ignore(numeric_limits<streamsize>::max(), ' ');
pread >> rss_mem;
rss_mem *= page_size;
}
pread.close();
if (!tmpstr.empty()) rss_mem = stoull(tmpstr);
}
//* Cache program name, command and username
@ -263,12 +258,14 @@ public:
return procs;
}
Processes() {
void init(){
clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms();
stop.store(false);
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();
}
};

View file

@ -30,138 +30,129 @@ tab-size = 4
using namespace std;
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Theme {
//* 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) {
return 232 + r / 11;
} else {
return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16;
namespace {
//* 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) {
return 232 + r / 11;
} else {
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
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* 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"){
if (hexa.size() > 1){
hexa.erase(0, 1);
for (auto& c : hexa) if (!isxdigit(c)) return "";
//* Generate escape sequence for 24-bit or 256 color and return as a string
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* 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"){
if (hexa.size() > 1){
hexa.erase(0, 1);
for (auto& c : hexa) if (!isxdigit(c)) return "";
depth = (depth == "fg") ? "38" : "48";
string pre = Fx::e + depth + ";";
pre += (t_to_256) ? "5;" : "2;";
if (hexa.size() == 2){
uint h_int = stoi(hexa, 0, 16);
if (t_to_256){
return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m";
} else {
string h_str = to_string(h_int);
return pre + h_str + ";" + h_str + ";" + h_str + "m";
}
}
else if (hexa.size() == 6){
if (t_to_256){
return pre + to_string(truecolor_to_256(
stoi(hexa.substr(0, 2), 0, 16),
stoi(hexa.substr(2, 2), 0, 16),
stoi(hexa.substr(4, 2), 0, 16))) + "m";
} else {
return pre +
to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" +
to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" +
to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m";
}
}
}
return "";
}
//* Generate escape sequence for 24-bit or 256 color and return as a string
//* Args r: [0-255], g: [0-255], b: [0-255]
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* 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"){
depth = (depth == "fg") ? "38" : "48";
string pre = Fx::e + depth + ";";
pre += (t_to_256) ? "5;" : "2;";
r = (r > 255) ? 255 : r;
g = (g > 255) ? 255 : g;
b = (b > 255) ? 255 : b;
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";
}
if (hexa.size() == 2){
uint h_int = stoi(hexa, 0, 16);
if (t_to_256){
return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m";
} else {
string h_str = to_string(h_int);
return pre + h_str + ";" + h_str + ";" + h_str + "m";
}
}
else if (hexa.size() == 6){
if (t_to_256){
return pre + to_string(truecolor_to_256(
stoi(hexa.substr(0, 2), 0, 16),
stoi(hexa.substr(2, 2), 0, 16),
stoi(hexa.substr(4, 2), 0, 16))) + "m";
} else {
return pre +
to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" +
to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" +
to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m";
//* Return a map of "r", "g", "b", 0-255 values for a 24-bit color escape string
map<string, int> rgb(string c_string){
map<string, int> rgb = {{"r", 0}, {"g", 0}, {"b", 0}};
if (c_string.size() >= 14){
c_string.erase(0, 7);
auto c_split = ssplit(c_string, ";");
if (c_split.size() == 3){
rgb["r"] = stoi(c_split[0]);
rgb["g"] = stoi(c_split[1]);
rgb["b"] = stoi(c_split[2].erase(c_split[2].size()));
}
}
return rgb;
}
return "";
}
//* Generate escape sequence for 24-bit or 256 color and return as a string
//* Args r: [0-255], g: [0-255], b: [0-255]
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* 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"){
depth = (depth == "fg") ? "38" : "48";
string pre = Fx::e + depth + ";";
pre += (t_to_256) ? "5;" : "2;";
r = (r > 255) ? 255 : r;
g = (g > 255) ? 255 : g;
b = (b > 255) ? 255 : b;
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";
}
namespace {
map<string, string> color;
map<string, vector<string>> gradient;
//* 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 = {{"r", 0}, {"g", 0}, {"b", 0}};
if (c_string.size() >= 14){
c_string.erase(0, 7);
auto c_split = ssplit(c_string, ";");
if (c_split.size() == 3){
rgb["r"] = stoi(c_split[0]);
rgb["g"] = stoi(c_split[1]);
rgb["b"] = stoi(c_split[2].erase(c_split[2].size()));
}
}
return rgb;
}
//? --------------------------------------------------- CLASSES -----------------------------------------------------
class C_Theme {
map<string, string> c;
map<string, vector<string>> g;
map<string,string> generate(map<string, string>& source){
map<string, string> out;
vector<string> t_rgb;
string depth;
for (auto& item : Global::Default_theme) {
depth = (item.first.ends_with("bg")) ? "bg" : "fg";
if (source.contains(item.first)) {
if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !State::truecolor, depth);
else {
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);
//* Generate the theme
map<string,string> generate(map<string, string>& source){
map<string, string> out;
vector<string> t_rgb;
string depth;
for (auto& item : Global::Default_theme) {
depth = (item.first.ends_with("bg")) ? "bg" : "fg";
if (source.contains(item.first)) {
if (source.at(item.first)[0] == '#') out[item.first] = hex_to_color(source.at(item.first), !Config::getB("truecolor"), depth);
else {
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]), !Config::getB("truecolor"), depth);
}
}
else out[item.first] = "";
if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !Config::getB("truecolor"), depth);
}
else out[item.first] = "";
if (out[item.first].empty()) out[item.first] = hex_to_color(item.second, !State::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){
change(source);
//* Set current theme using <source> map
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>
auto operator()(string name){
return c.at(name);
auto c(string name){
return color.at(name);
}
//* Return vector of escape codes for color gradient <name>
auto gradient(string name){
return g.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));
auto g(string name){
return gradient.at(name);
}
};

View file

@ -27,6 +27,7 @@ tab-size = 4
#include <thread>
#include <algorithm>
#include <fstream>
#include <regex>
#include <unistd.h>
#include <termios.h>
@ -42,7 +43,7 @@ using namespace std;
//* Collection of escape codes for text style and formatting
namespace Fx {
//* Escape sequence start
const string e = "\033[";
const string e = "\x1b[";
//* Bold on
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
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
@ -93,12 +105,121 @@ namespace Mv {
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 -----------------------------------------------------
//* Return number of UTF8 characters in a string
inline size_t ulen(string s){
//* Return number of UTF8 characters in a string with option to disregard escape sequences
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(),
[](char& c) { return (c & 0xC0) != 0x80; } );
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
}
//* 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
string ljustify(string str, size_t x, bool utf=false, bool lim=true){
if (!utf) {
if (lim && str.size() > x) str.resize(x);
return str + string(max((int)(x - str.size()), 0), ' ');
} else {
if (lim && ulen(str) > x) {
string ljustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){
if (utf || escape) {
if (!escape && lim && ulen(str) > x) {
auto i = str.size();
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
string rjustify(string str, size_t x, bool utf=false, bool lim=true){
if (!utf) {
if (lim && str.size() > x) str.resize(x);
return string(max((int)(x - str.size()), 0), ' ') + str;
} else {
if (lim && ulen(str) > x) {
string rjustify(string str, size_t x, bool utf=false, bool escape=false, bool lim=true){
if (utf || escape) {
if (!escape && lim && ulen(str) > x) {
auto i = str.size();
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>
string uresize(string str, size_t x){
auto i = str.size();
if (i < 1 || x < 1) return str;
while (ulen(str) > x) str.resize(--i);
// auto i = str.size();
if (str.empty()) return str;
while (ulen(str) > x) str.pop_back();
return str;
}
@ -269,104 +392,7 @@ string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool
//? --------------------------------------------------- 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 -------------------------------------------------------