mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-16 17:35:03 +12:00
Small changes
This commit is contained in:
parent
ecd4ef9985
commit
f424feb24d
191
btop.cpp
191
btop.cpp
|
@ -28,6 +28,7 @@ tab-size = 4
|
|||
#include <ranges>
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
#include <robin_hood.h>
|
||||
|
||||
namespace Global {
|
||||
const std::vector<std::array<std::string, 2>> Banner_src = {
|
||||
|
@ -69,7 +70,7 @@ namespace Global {
|
|||
#error Platform not supported!
|
||||
#endif
|
||||
|
||||
using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate;
|
||||
using std::string, std::vector, std::array, robin_hood::unordered_flat_map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate;
|
||||
using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Tools;
|
||||
|
@ -79,10 +80,14 @@ namespace Global {
|
|||
string banner;
|
||||
const uint banner_width = 49;
|
||||
|
||||
fs::path conf_dir;
|
||||
fs::path conf_file;
|
||||
fs::path theme_folder;
|
||||
fs::path user_theme_folder;
|
||||
fs::path self_path;
|
||||
|
||||
bool debuginit = false;
|
||||
bool debug = false;
|
||||
|
||||
uint64_t start_time;
|
||||
|
||||
bool quitting = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,10 +99,13 @@ void argumentParser(int argc, char **argv){
|
|||
if (argument == "-v" || argument == "--version") {
|
||||
cout << "btop version: " << Global::Version << endl;
|
||||
exit(0);
|
||||
} else if (argument == "-h" || argument == "--help") {
|
||||
}
|
||||
else if (argument == "-h" || argument == "--help") {
|
||||
cout << "help here" << endl;
|
||||
exit(0);
|
||||
} else {
|
||||
}
|
||||
else if (argument == "--debug") Global::debug = true;
|
||||
else {
|
||||
cout << " Unknown argument: " << argument << "\n" <<
|
||||
" Use -h or --help for help." << endl;
|
||||
exit(1);
|
||||
|
@ -134,6 +142,18 @@ string createBanner(){
|
|||
return out;
|
||||
}
|
||||
|
||||
void clean_quit(int signal){
|
||||
if (Global::quitting) return;
|
||||
if (Term::initialized) {
|
||||
Term::restore();
|
||||
if (!Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
||||
}
|
||||
if (Global::debug) Logger::debug("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
||||
Global::quitting = true;
|
||||
if (signal != -1) exit(signal);
|
||||
}
|
||||
|
||||
void _exit_handler() { clean_quit(-1); }
|
||||
|
||||
//* Threading test function
|
||||
string my_worker(int x){
|
||||
|
@ -150,62 +170,83 @@ int main(int argc, char **argv){
|
|||
|
||||
//? Init
|
||||
|
||||
Global::start_time = time_s();
|
||||
|
||||
cout.setf(std::ios::boolalpha);
|
||||
if (argc > 1) argumentParser(argc, argv);
|
||||
|
||||
std::atexit(_exit_handler);
|
||||
|
||||
#if defined(LINUX)
|
||||
//? Linux init
|
||||
//? Linux paths init
|
||||
Global::proc_path = (fs::is_directory(fs::path("/proc")) && access("/proc", R_OK) != -1) ? "/proc" : "";
|
||||
if (Global::proc_path.empty()) {
|
||||
cout << "ERROR: Proc filesystem not found or no permission to read from it!" << endl;
|
||||
exit(1);
|
||||
}
|
||||
{
|
||||
std::error_code ec;
|
||||
Global::self_path = fs::read_symlink("/proc/self/exe", ec).remove_filename();
|
||||
}
|
||||
#endif
|
||||
|
||||
//? Setup paths for config, log and themes
|
||||
for (auto env : {"XDG_CONFIG_HOME", "HOME"}) {
|
||||
if (getenv(env) != NULL && access(getenv(env), W_OK) != -1) {
|
||||
Global::conf_dir = fs::path(getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
||||
Config::conf_dir = fs::path(getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Global::conf_dir.empty()) {
|
||||
if (!fs::is_directory(Global::conf_dir) && !fs::create_directories(Global::conf_dir)) {
|
||||
if (!Config::conf_dir.empty()) {
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(Config::conf_dir) && !fs::create_directories(Config::conf_dir, ec)) {
|
||||
cout << "WARNING: Could not create or access btop config directory. Logging and config saving disabled." << endl;
|
||||
cout << "Make sure your $HOME environment variable is correctly set to fix this." << endl;
|
||||
}
|
||||
else {
|
||||
Global::conf_file = Global::conf_dir / "btop.conf";
|
||||
Logger::logfile = Global::conf_dir / "btop.log";
|
||||
Global::user_theme_folder = Global::conf_dir / "themes";
|
||||
if (!fs::exists(Global::user_theme_folder) && !fs::create_directory(Global::user_theme_folder)) Global::user_theme_folder.clear();
|
||||
std::error_code ec;
|
||||
Config::conf_file = Config::conf_dir / "btop.conf";
|
||||
Logger::logfile = Config::conf_dir / "btop.log";
|
||||
Theme::user_theme_dir = Config::conf_dir / "themes";
|
||||
if (!fs::exists(Theme::user_theme_dir) && !fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear();
|
||||
}
|
||||
}
|
||||
if (!Global::self_path.empty()) {
|
||||
std::error_code ec;
|
||||
Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec);
|
||||
if (ec || access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear();
|
||||
}
|
||||
|
||||
if (Theme::theme_dir.empty()) {
|
||||
for (auto theme_path : {"/usr/local/share/btop/themes", "/usr/share/btop/themes"}) {
|
||||
if (access(theme_path, R_OK) != -1) {
|
||||
Global::theme_folder = theme_path;
|
||||
if (fs::exists(fs::path(theme_path)) && access(theme_path, R_OK) != -1) {
|
||||
Theme::theme_dir = fs::path(theme_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string err_msg;
|
||||
Global::debug = true;
|
||||
if (Global::debug) { Logger::loglevel = 4; Logger::debug("Starting in debug mode");}
|
||||
|
||||
if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) {
|
||||
err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.";
|
||||
string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.";
|
||||
Logger::warning(err_msg);
|
||||
cout << "WARNING: " << err_msg << endl;
|
||||
}
|
||||
|
||||
//? Initialize terminal and set options
|
||||
if (!Term::init()) {
|
||||
err_msg = "No tty detected!";
|
||||
string err_msg = "No tty detected!";
|
||||
Logger::error(err_msg + " Quitting.");
|
||||
cout << "ERROR: " << err_msg << endl;
|
||||
cout << "btop++ needs an interactive shell to run." << endl;
|
||||
exit(1);
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
//? Read config file if present
|
||||
Config::load("____");
|
||||
Config::setB("truecolor", false);
|
||||
|
||||
auto thts = time_ms();
|
||||
|
||||
|
@ -218,16 +259,13 @@ int main(int argc, char **argv){
|
|||
|
||||
//* ------------------------------------------------ TESTING ------------------------------------------------------
|
||||
|
||||
int debug = 1;
|
||||
int tests = 0;
|
||||
bool debuginit = false;
|
||||
|
||||
if (debug > 0) { Logger::loglevel = 4; Logger::debug("Running in debug mode!");}
|
||||
Global::debuginit = false;
|
||||
|
||||
// cout << Theme("main_bg") << Term::clear << flush;
|
||||
bool thread_test = false;
|
||||
|
||||
if (!debuginit) cout << Term::alt_screen << Term::hide_cursor << flush;
|
||||
if (!Global::debuginit) cout << Term::alt_screen << Term::hide_cursor << flush;
|
||||
|
||||
cout << Theme::c("main_fg") << Theme::c("main_bg") << Term::clear << endl;
|
||||
|
||||
|
@ -273,8 +311,7 @@ int main(int argc, char **argv){
|
|||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
if (true) {
|
||||
if (false) {
|
||||
Draw::Meter kmeter;
|
||||
kmeter(Term::width - 2, "cpu", false);
|
||||
cout << kmeter(25) << endl;
|
||||
|
@ -285,11 +322,25 @@ int main(int argc, char **argv){
|
|||
exit(0);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
cout << fs::absolute(fs::current_path() / "..") << endl;
|
||||
cout << Global::self_path << endl;
|
||||
cout << Theme::theme_dir << endl;
|
||||
cout << Config::conf_dir << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
string a = "⣿ ⣿\n⣿⣿⣿⣿ ⣿\n⣿⣿⣿⣿ ⣿\n⣿⣿⣿⣿ ⣿\n⣿⣿⣿";
|
||||
cout << a << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
if (thread_test){
|
||||
|
||||
map<int, future<string>> runners;
|
||||
map<int, string> outputs;
|
||||
unordered_flat_map<int, future<string>> runners;
|
||||
unordered_flat_map<int, string> outputs;
|
||||
|
||||
for (int i : iota(0, 10)){
|
||||
runners[i] = async(my_worker, i);
|
||||
|
@ -325,7 +376,7 @@ int main(int argc, char **argv){
|
|||
uint lc;
|
||||
string ostring;
|
||||
uint64_t tsl, timestamp2, rcount = 0;
|
||||
list<uint64_t> avgtimes;
|
||||
list<uint64_t> avgtimes = {0};
|
||||
uint timer = 1000;
|
||||
bool filtering = false;
|
||||
bool reversing = false;
|
||||
|
@ -369,8 +420,8 @@ int main(int argc, char **argv){
|
|||
|
||||
|
||||
|
||||
avgtimes.push_front(timestamp);
|
||||
if (avgtimes.size() > 100) avgtimes.pop_back();
|
||||
if (rcount > 0) avgtimes.push_front(timestamp);
|
||||
if (avgtimes.size() > 10) avgtimes.pop_back();
|
||||
cout << pbox << ostring << Fx::reset << "\n" << endl;
|
||||
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " <<
|
||||
rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
|
||||
|
@ -403,79 +454,5 @@ int main(int argc, char **argv){
|
|||
|
||||
//*-----<<<<<
|
||||
|
||||
//cout << pw->pw_name << "/" << gr->gr_name << endl;
|
||||
|
||||
|
||||
|
||||
|
||||
if (tests>4){
|
||||
string trim_test1 = "-*vad ";
|
||||
string trim_test2 = " vad*-";
|
||||
string trim_test3 = trim_test1 + trim_test2;
|
||||
|
||||
cout << "\"" << ltrim(trim_test1, "-*") << "\" \"" << rtrim(trim_test2, "*-") << "\" \"" << trim(trim_test3, "-") << "\"" << endl;
|
||||
|
||||
|
||||
string testie = "Does this work as intended? Or?";
|
||||
auto t_vec = ssplit(testie);
|
||||
for(auto& tp : t_vec){
|
||||
cout << "\"" << tp << "\" " << flush;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if (tests>5){
|
||||
|
||||
//}
|
||||
|
||||
// map<string, string> dict = {
|
||||
// {"Korv", "14"},
|
||||
// {"Vad", "13"}
|
||||
// };
|
||||
|
||||
// cout << dict["Korv"] << ", " << dict["Vad"] << endl;
|
||||
|
||||
// vector<map<string, int>> test = {
|
||||
// {{"first", 1}, {"second", 2}},
|
||||
// {{"first", 11}, {"second", 22}}
|
||||
// };
|
||||
|
||||
//cout << test[0]["first"] << " " << test[1]["second"] << endl;
|
||||
|
||||
// for (auto& m : test) {
|
||||
// cout << endl;
|
||||
// for (auto& item : m) {
|
||||
// cout << item.first << " " << item.second << endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
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;
|
||||
while (!qp && wt >= 0){
|
||||
int wtm = wt / 60;
|
||||
int wts = wt - wtm * 60;
|
||||
wt--;
|
||||
cout << Mv::to(Term::height - 1, 26) << "(" << wtm << ":" << wts << ") " << flush;
|
||||
//chr = Key(1000);
|
||||
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;
|
||||
if (key == "q") qp = true;
|
||||
key = "";
|
||||
wt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Term::restore();
|
||||
if (!debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,11 +21,13 @@ tab-size = 4
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
#include <filesystem>
|
||||
|
||||
#include <btop_tools.h>
|
||||
|
||||
using std::string, std::vector, std::unordered_map;
|
||||
using std::string, std::vector, robin_hood::unordered_flat_map;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Tools;
|
||||
|
||||
|
||||
|
@ -33,9 +35,12 @@ using namespace Tools;
|
|||
namespace Config {
|
||||
namespace {
|
||||
|
||||
fs::path conf_dir;
|
||||
fs::path conf_file;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
unordered_map<string, string> strings = {
|
||||
unordered_flat_map<string, string> strings = {
|
||||
{"color_theme", "Default"},
|
||||
{"shown_boxes", "cpu mem net proc"},
|
||||
{"proc_sorting", "cpu lazy"},
|
||||
|
@ -52,7 +57,7 @@ namespace Config {
|
|||
{"net_iface", ""},
|
||||
{"log_level", "WARNING"}
|
||||
};
|
||||
unordered_map<string, bool> bools = {
|
||||
unordered_flat_map<string, bool> bools = {
|
||||
{"theme_background", true},
|
||||
{"truecolor", true},
|
||||
{"proc_reversed", false},
|
||||
|
@ -84,7 +89,7 @@ namespace Config {
|
|||
{"show_battery", true},
|
||||
{"show_init", false}
|
||||
};
|
||||
unordered_map<string, int> ints = {
|
||||
unordered_flat_map<string, int> ints = {
|
||||
{"update_ms", 2000},
|
||||
{"proc_update_mult", 2},
|
||||
{"tree_depth", 3}
|
||||
|
|
|
@ -20,7 +20,7 @@ tab-size = 4
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <robin_hood.h>
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
@ -31,7 +31,42 @@ tab-size = 4
|
|||
#ifndef _btop_draw_included_
|
||||
#define _btop_draw_included_ 1
|
||||
|
||||
using std::string, std::vector, std::map, std::round, std::views::iota, std::string_literals::operator""s;
|
||||
using std::string, std::vector, robin_hood::unordered_flat_map, std::round, std::views::iota, std::string_literals::operator""s, std::clamp;
|
||||
|
||||
namespace Symbols {
|
||||
const string h_line = "─";
|
||||
const string v_line = "│";
|
||||
const string left_up = "┌";
|
||||
const string right_up = "┐";
|
||||
const string left_down = "└";
|
||||
const string right_down = "┘";
|
||||
const string title_left = "┤";
|
||||
const string title_right = "├";
|
||||
const string div_up = "┬";
|
||||
const string div_down = "┴";
|
||||
|
||||
const string meter = "■";
|
||||
|
||||
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
|
||||
|
||||
const unordered_flat_map<bool, string> graph_00 = { {true, Mv::r(1)}, {false, " "} };
|
||||
|
||||
unordered_flat_map<float, string> graph_up = {
|
||||
{0.0, " "}, {0.1, "⢀"}, {0.2, "⢠"}, {0.3, "⢰"}, {0.4, "⢸"},
|
||||
{1.0, "⡀"}, {1.1, "⣀"}, {1.2, "⣠"}, {1.3, "⣰"}, {1.4, "⣸"},
|
||||
{2.0, "⡄"}, {2.1, "⣄"}, {2.2, "⣤"}, {2.3, "⣴"}, {2.4, "⣼"},
|
||||
{3.0, "⡆"}, {3.1, "⣆"}, {3.2, "⣦"}, {3.3, "⣶"}, {3.4, "⣾"},
|
||||
{4.0, "⡇"}, {4.1, "⣇"}, {4.2, "⣧"}, {4.3, "⣷"}, {4.4, "⣿"}
|
||||
};
|
||||
|
||||
unordered_flat_map<float, string> graph_down = {
|
||||
{0.0, " "}, {0.1, "⠈"}, {0.2, "⠘"}, {0.3, "⠸"}, {0.4, "⢸"},
|
||||
{1.0, "⠁"}, {1.1, "⠉"}, {1.2, "⠙"}, {1.3, "⠹"}, {1.4, "⢹"},
|
||||
{2.0, "⠃"}, {2.1, "⠋"}, {2.2, "⠛"}, {2.3, "⠻"}, {2.4, "⢻"},
|
||||
{3.0, "⠇"}, {3.1, "⠏"}, {3.2, "⠟"}, {3.3, "⠿"}, {3.4, "⢿"},
|
||||
{4.0, "⡇"}, {4.1, "⡏"}, {4.2, "⡟"}, {4.3, "⡿"}, {4.4, "⣿"}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Draw {
|
||||
|
||||
|
@ -43,6 +78,7 @@ namespace Draw {
|
|||
uint num=0;
|
||||
};
|
||||
|
||||
//* Create a box using values from a BoxConf struct and return as a string
|
||||
string createBox(BoxConf c){
|
||||
string out;
|
||||
string lcolor = (c.line_color.empty()) ? Theme::c("div_line") : c.line_color;
|
||||
|
@ -81,24 +117,27 @@ namespace Draw {
|
|||
return out + Fx::reset + Mv::to(c.y + 1, c.x + 2);
|
||||
}
|
||||
|
||||
//* Class holding a percentage meter
|
||||
class Meter {
|
||||
string out, color_gradient;
|
||||
int width = 10;
|
||||
bool invert = false;
|
||||
vector<string> cache;
|
||||
public:
|
||||
//* Set meter options
|
||||
void operator()(int width, string color_gradient, bool invert = false) {
|
||||
if (width < 0) width = 1;
|
||||
this->width = width;
|
||||
this->color_gradient = color_gradient;
|
||||
this->invert = invert;
|
||||
out.clear();
|
||||
cache.clear();
|
||||
cache.insert(cache.begin(), 101, "");
|
||||
}
|
||||
|
||||
//* Return a string representation of the meter with given value
|
||||
string operator()(int value) {
|
||||
if (value > 100) value = 100;
|
||||
else if (value < 0) value = 0;
|
||||
if (width < 1) return out;
|
||||
value = clamp(value, 0, 100);
|
||||
if (!cache.at(value).empty()) return out = cache.at(value);
|
||||
out.clear();
|
||||
int y;
|
||||
|
|
|
@ -23,9 +23,9 @@ tab-size = 4
|
|||
#include <vector>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
|
||||
using std::string, std::vector, std::unordered_map, std::array, std::atomic;
|
||||
using std::string, std::vector, std::unordered_map, std::array, std::atomic, robin_hood::unordered_flat_map;
|
||||
|
||||
namespace Global {
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace Global {
|
|||
|
||||
|
||||
|
||||
const unordered_map<string, unordered_map<string, vector<string>>> Menus = {
|
||||
const unordered_flat_map<string, unordered_map<string, vector<string>>> Menus = {
|
||||
{ "options", {
|
||||
{ "normal", {
|
||||
"┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐",
|
||||
|
|
|
@ -20,12 +20,12 @@ tab-size = 4
|
|||
#define _btop_input_included_ 1
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <btop_tools.h>
|
||||
|
||||
using std::string, std::unordered_map, std::cin;
|
||||
using std::string, robin_hood::unordered_flat_map, std::cin;
|
||||
using namespace Tools;
|
||||
|
||||
/* The input functions relies on the following std::cin options being set:
|
||||
|
@ -38,7 +38,7 @@ using namespace Tools;
|
|||
namespace Input {
|
||||
namespace {
|
||||
//* Map for translating key codes to readable values
|
||||
const unordered_map<string, string> Key_escapes = {
|
||||
const unordered_flat_map<string, string> Key_escapes = {
|
||||
{"\033", "escape"},
|
||||
{"\n", "enter"},
|
||||
{" ", "space"},
|
||||
|
|
|
@ -31,16 +31,17 @@ tab-size = 4
|
|||
#include <filesystem>
|
||||
#include <ranges>
|
||||
#include <list>
|
||||
#include <robin_hood.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <btop_config.h>
|
||||
#include <btop_globs.h>
|
||||
#include <btop_tools.h>
|
||||
#include <robin_hood.h>
|
||||
|
||||
|
||||
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, robin_hood::unordered_flat_map;
|
||||
|
||||
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize;
|
||||
using std::cout, std::flush, std::endl;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Tools;
|
||||
|
|
|
@ -22,20 +22,24 @@ tab-size = 4
|
|||
#include <string>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <robin_hood.h>
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
|
||||
#include <btop_globs.h>
|
||||
#include <btop_tools.h>
|
||||
#include <btop_config.h>
|
||||
|
||||
using std::string, std::round, std::vector, std::map, std::stoi, std::views::iota, std::array, std::unordered_map;
|
||||
using std::string, std::round, std::vector, robin_hood::unordered_flat_map, std::stoi, std::views::iota, std::array, std::clamp, std::max, std::min;
|
||||
using namespace Tools;
|
||||
|
||||
namespace Theme {
|
||||
|
||||
const unordered_map<string, string> Default_theme = {
|
||||
fs::path theme_dir;
|
||||
fs::path user_theme_dir;
|
||||
|
||||
const unordered_flat_map<string, string> Default_theme = {
|
||||
{ "main_bg", "#00" },
|
||||
{ "main_fg", "#cc" },
|
||||
{ "title", "#ee" },
|
||||
|
@ -83,10 +87,10 @@ namespace Theme {
|
|||
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;
|
||||
if (round((double)r / 11) == round((double)g / 11) && round((double)g / 11) == round((double)b / 11)) {
|
||||
return 232 + round((double)r / 11);
|
||||
} else {
|
||||
return round((float)(r / 51)) * 36 + round((float)(g / 51)) * 6 + round((float)(b / 51)) + 16;
|
||||
return round((double)r / 51) * 36 + round((double)g / 51) * 6 + round((double)b / 51) + 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +102,10 @@ namespace Theme {
|
|||
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 "";
|
||||
for (auto& c : hexa) if (!isxdigit(c)) {
|
||||
Logger::error("Invalid hex value: " + hexa);
|
||||
return "";
|
||||
}
|
||||
depth = (depth == "fg") ? "38" : "48";
|
||||
string pre = Fx::e + depth + ";";
|
||||
pre += (t_to_256) ? "5;" : "2;";
|
||||
|
@ -125,7 +132,9 @@ namespace Theme {
|
|||
to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m";
|
||||
}
|
||||
}
|
||||
else Logger::error("Invalid size of hex value: " + hexa);
|
||||
}
|
||||
else Logger::error("Hex value missing." + hexa);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -137,9 +146,9 @@ namespace Theme {
|
|||
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;
|
||||
r = min(r, 255u);
|
||||
g = min(g, 255u);
|
||||
b = min(b, 255u);
|
||||
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";
|
||||
}
|
||||
|
@ -161,9 +170,9 @@ namespace Theme {
|
|||
|
||||
|
||||
namespace {
|
||||
unordered_map<string, string> colors;
|
||||
unordered_map<string, array<int, 3>> rgbs;
|
||||
unordered_map<string, array<string, 101>> gradients;
|
||||
unordered_flat_map<string, string> colors;
|
||||
unordered_flat_map<string, array<int, 3>> rgbs;
|
||||
unordered_flat_map<string, array<string, 101>> gradients;
|
||||
|
||||
//* Convert hex color to a array of decimals
|
||||
array<int, 3> hex_to_dec(string hexa){
|
||||
|
@ -187,39 +196,45 @@ namespace Theme {
|
|||
}
|
||||
|
||||
//* Generate colors and rgb decimal vectors for the theme
|
||||
void generateColors(unordered_map<string, string>& source){
|
||||
void generateColors(unordered_flat_map<string, string>& source){
|
||||
vector<string> t_rgb;
|
||||
string depth;
|
||||
bool t_to_256 = !Config::getB("truecolor");
|
||||
colors.clear(); rgbs.clear();
|
||||
for (auto& [name, color] : Default_theme) {
|
||||
depth = (name.ends_with("bg") && name != "meter_bg") ? "bg" : "fg";
|
||||
if (source.contains(name)) {
|
||||
if (source.at(name)[0] == '#') {
|
||||
colors[name] = hex_to_color(source.at(name), !Config::getB("truecolor"), depth);
|
||||
colors[name] = hex_to_color(source.at(name), t_to_256, depth);
|
||||
rgbs[name] = hex_to_dec(source.at(name));
|
||||
}
|
||||
else {
|
||||
t_rgb = ssplit(source.at(name), " ");
|
||||
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), !Config::getB("truecolor"), depth);
|
||||
if (t_rgb.size() != 3)
|
||||
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
||||
else {
|
||||
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
|
||||
rgbs[name] = array<int, 3>{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
||||
}
|
||||
}
|
||||
else colors[name] = "";
|
||||
}
|
||||
if (colors[name].empty()) {
|
||||
colors[name] = hex_to_color(color, !Config::getB("truecolor"), depth);
|
||||
Logger::info("Missing color value for \"" + name + "\". Using value from default.");
|
||||
colors[name] = hex_to_color(color, t_to_256, depth);
|
||||
rgbs[name] = array<int, 3>{-1, -1, -1};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//* Generate color gradients from one, two or three colors, 101 values indexed 0-100
|
||||
//* Generate color gradients from two or three colors, 101 values indexed 0-100
|
||||
void generateGradients(){
|
||||
gradients.clear();
|
||||
array<string, 101> c_gradient;
|
||||
string wname;
|
||||
bool t_to_256 = !Config::getB("truecolor");
|
||||
array<array<int, 3>, 3> rgb_arr;
|
||||
array<array<int, 3>, 101> dec_arr;
|
||||
int f, s, r, o, y;
|
||||
int arr1, arr2, rng, offset, y;
|
||||
for (auto& [name, source_arr] : rgbs) {
|
||||
if (!name.ends_with("_start")) continue;
|
||||
dec_arr[0][0] = -1;
|
||||
|
@ -230,21 +245,22 @@ namespace Theme {
|
|||
if (rgb_arr[2][0] >= 0) {
|
||||
|
||||
//? Split iteration in two passes of 50 + 51 instead of 101 if gradient has _start, _mid and _end values defined
|
||||
r = (rgb_arr[1][0] >= 0) ? 50 : 100;
|
||||
for (int i : iota(0, 3)){
|
||||
f = 0; s = (r == 50) ? 1 : 2; o = 0;
|
||||
for (int c : iota(0, 101)){
|
||||
dec_arr[c][i] = rgb_arr[f][i] + (c - o) * (rgb_arr[s][i] - rgb_arr[f][i]) / r;
|
||||
rng = (rgb_arr[1][0] >= 0) ? 50 : 100;
|
||||
for (int rgb : iota(0, 3)){
|
||||
arr1 = 0; arr2 = (rng == 50) ? 1 : 2; offset = 0;
|
||||
for (int i : iota(0, 101)) {
|
||||
dec_arr[i][rgb] = rgb_arr[arr1][rgb] + (i - offset) * (rgb_arr[arr2][rgb] - rgb_arr[arr1][rgb]) / rng;
|
||||
|
||||
//? Switch source arrays from _start/_mid to _mid/_end at 50 passes if _mid is defined
|
||||
if (c == r) { ++f; ++s; o = 50;}
|
||||
if (i == rng) { ++arr1; ++arr2; offset = 50;}
|
||||
}
|
||||
}
|
||||
}
|
||||
y = 0;
|
||||
if (dec_arr[0][0] != -1) {
|
||||
for (auto& vec : dec_arr) c_gradient[y++] = dec_to_color(vec[0], vec[1], vec[2], !Config::getB("truecolor"));
|
||||
} else {
|
||||
for (auto& arr : dec_arr) c_gradient[y++] = dec_to_color(arr[0], arr[1], arr[2], t_to_256);
|
||||
}
|
||||
else {
|
||||
//? If only _start was defined fill array with _start color
|
||||
c_gradient.fill(colors[name]);
|
||||
}
|
||||
|
@ -255,7 +271,7 @@ namespace Theme {
|
|||
|
||||
|
||||
//* Set current theme using <source> map
|
||||
void set(unordered_map<string, string> source){
|
||||
void set(unordered_flat_map<string, string> source){
|
||||
generateColors(source);
|
||||
generateGradients();
|
||||
Term::fg = colors.at("main_fg");
|
||||
|
@ -264,17 +280,17 @@ namespace Theme {
|
|||
}
|
||||
|
||||
//* Return escape code for color <name>
|
||||
auto c(string name){
|
||||
auto& c(string name){
|
||||
return colors.at(name);
|
||||
}
|
||||
|
||||
//* Return array of escape codes for color gradient <name>
|
||||
auto g(string name){
|
||||
auto& g(string name){
|
||||
return gradients.at(name);
|
||||
}
|
||||
|
||||
//* Return array of red, green and blue in decimal for color <name>
|
||||
auto dec(string name){
|
||||
auto& dec(string name){
|
||||
return rgbs.at(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ tab-size = 4
|
|||
#include <utility>
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <robin_hood.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
@ -39,28 +40,11 @@ tab-size = 4
|
|||
|
||||
#include <btop_globs.h>
|
||||
|
||||
using std::string, std::vector, std::regex, std::max, std::to_string, std::cin, std::atomic;
|
||||
using std::string, std::vector, std::regex, std::max, std::to_string, std::cin, std::atomic, robin_hood::unordered_flat_map;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
|
||||
|
||||
namespace Symbols {
|
||||
const string h_line = "─";
|
||||
const string v_line = "│";
|
||||
const string left_up = "┌";
|
||||
const string right_up = "┐";
|
||||
const string left_down = "└";
|
||||
const string right_down = "┘";
|
||||
const string title_left = "┤";
|
||||
const string title_right = "├";
|
||||
const string div_up = "┬";
|
||||
const string div_down = "┴";
|
||||
|
||||
const string meter = "■";
|
||||
|
||||
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
|
||||
}
|
||||
|
||||
//* Collection of escape codes for text style and formatting
|
||||
namespace Fx {
|
||||
//* Escape sequence start
|
||||
|
@ -251,6 +235,11 @@ namespace Tools {
|
|||
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
|
||||
}
|
||||
|
||||
//* Return current time since epoch in seconds
|
||||
inline uint64_t time_s(){
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
//* Return current time since epoch in milliseconds
|
||||
inline uint64_t time_ms(){
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
@ -459,6 +448,7 @@ namespace Tools {
|
|||
|
||||
}
|
||||
|
||||
//* Simple logging implementation
|
||||
namespace Logger {
|
||||
namespace {
|
||||
std::atomic<bool> busy (false);
|
||||
|
@ -466,7 +456,7 @@ namespace Logger {
|
|||
uint loglevel = 2;
|
||||
bool first = true;
|
||||
string tdf = "%Y/%m/%d (%T) | ";
|
||||
unordered_map<uint, string> log_levels = {
|
||||
unordered_flat_map<uint, string> log_levels = {
|
||||
{ 0, "DISABLED" },
|
||||
{ 1, "ERROR" },
|
||||
{ 2, "WARNING" },
|
||||
|
|
Loading…
Reference in a new issue