Added theme loadfile function

This commit is contained in:
aristocratos 2021-06-25 23:58:19 +02:00
parent b4d223cf40
commit 36f0264485
8 changed files with 226 additions and 154 deletions

View file

@ -67,7 +67,7 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const std::string Version = "0.0.21";
const std::string Version = "0.0.30";
int coreCount;
}
@ -329,18 +329,19 @@ int main(int argc, char **argv){
}
#if defined(LINUX)
//? Linux init
Proc::init();
#endif
//? Platform init and error check
Shared::init();
// Config::set("truecolor", false);
auto thts = time_ms();
auto thts = time_micros();
//? Generate the theme
Theme::set("Default");
//? Update theme list and generate the theme
Theme::updateThemes();
Theme::setTheme();
//? Create the btop++ banner
banner_gen();
@ -371,29 +372,64 @@ int main(int argc, char **argv){
//* Test theme
if (false) {
cout << "Theme generation took " << time_ms() - thts << "ms" << endl;
cout << "Colors:" << endl;
uint i = 0;
for(auto& item : Theme::test_colors()) {
cout << rjust(item.first, 15) << ":" << item.second << ""s * 10 << Fx::reset << " ";
// << Theme::dec(item.first)[0] << ":" << Theme::dec(item.first)[1] << ":" << Theme::dec(item.first)[2] << ;
if (++i == 4) {
i = 0;
cout << endl;
}
}
cout << Fx::reset << endl;
// cout << Theme::theme_dir << ", " << Theme::user_theme_dir << endl;
// for (auto& s : Theme::themes) {
// cout << s << endl;
// }
// exit(0);
string key;
bool no_redraw = false;
auto theme_index = v_index(Theme::themes, Config::getS("color_theme"));
while (key != "q") {
key.clear();
if (not no_redraw) {
cout << "\nTheme generation of " << fs::path(Config::getS("color_theme")).filename().replace_extension("") << " took " << time_micros() - thts << "μs" << endl;
cout << "Colors:" << endl;
uint i = 0;
for(auto& item : Theme::test_colors()) {
cout << rjust(item.first, 15) << ":" << item.second << ""s * 10 << Fx::reset << " ";
// << Theme::dec(item.first)[0] << ":" << Theme::dec(item.first)[1] << ":" << Theme::dec(item.first)[2] << ;
if (++i == 4) {
i = 0;
cout << endl;
}
}
cout << Fx::reset << endl;
cout << "Gradients:";
for (auto& [name, cvec] : Theme::test_gradients()) {
cout << endl << rjust(name + ":", 10);
for (auto& color : cvec) {
cout << color << "";
cout << "Gradients:";
for (auto& [name, cvec] : Theme::test_gradients()) {
cout << endl << rjust(name + ":", 10);
for (auto& color : cvec) {
cout << color << "";
}
cout << Fx::reset << endl;
}
}
cout << Fx::reset << endl;
no_redraw = true;
key = Input::wait();
if (key.empty()) continue;
thts = time_micros();
if (key == "right") {
if (theme_index == Theme::themes.size() - 1) theme_index = 0;
else theme_index++;
}
else if (key == "left") {
if (theme_index == 0) theme_index = Theme::themes.size() - 1;
else theme_index--;
}
else continue;
no_redraw = false;
Config::set("color_theme", Theme::themes.at(theme_index));
Theme::setTheme();
}
@ -496,7 +532,7 @@ int main(int argc, char **argv){
// else y++;
// if (y == 100 or y == 0) flip = not flip;
if (Input::poll()) {
if (Input::get() == "space") Input::wait(true);
if (Input::get() == "space") Input::wait();
else break;
}
sleep_ms(50);

View file

@ -39,15 +39,14 @@ namespace Config {
bool write_new;
vector<array<string, 2>> descriptions = {
{"color_theme", "#* Color theme, looks for a .theme file in \"/usr/[local/]share/bpytop/themes\" and \"~/.config/bpytop/themes\", \"Default\" for builtin default theme.\n"
"#* Prefix name by a plus sign (+) for a theme located in user themes folder, i.e. color_theme=\"+monokai\"." },
{"color_theme", "#* Full path to a bashtop/bpytop/btop++ formatted \".theme\" file, \"Default\" and \"TTY\" for builtin themes."},
{"theme_background", "#* If the theme set background should be shown, set to False if you want terminal background transparency."},
{"truecolor", "#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false."},
{"force_tty", "#* Set to true to force tty mode regardless if a real tty has been detected or not.\n"
"#* Will force 16-color mode, set all graph symbols to \"tty\" and swap out other non tty friendly symbols."},
"#* Will force 16-color mode and TTY theme, set all graph symbols to \"tty\" and swap out other non tty friendly symbols."},
{"graph_symbol", "#* Default symbols to use for graph creation, \"braille\", \"block\" or \"tty\".\n"
"#* \"braille\" offers the highest resolution but might not be included in all fonts.\n"

View file

@ -73,10 +73,8 @@ namespace Input {
};
}
//* Last entered key
string last = "";
//* Poll keyboard & mouse input for <timeout> ms and return input availabilty as a bool
bool poll(int timeout){
if (timeout < 1) return cin.rdbuf()->in_avail() > 0;
while (timeout > 0) {
@ -87,11 +85,10 @@ namespace Input {
return false;
}
//* Get a key or mouse action from input
string get(bool clear){
string get(){
string key;
while (cin.rdbuf()->in_avail() > 0 and key.size() < 100) key += cin.get();
if (not clear and not key.empty()){
if (not key.empty()){
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 = "";
@ -100,10 +97,9 @@ namespace Input {
return key;
}
//* Wait until input is available
void wait(bool clear){
string wait(){
while (cin.rdbuf()->in_avail() < 1) sleep_ms(10);
if (clear) get(clear);
return get();
}
//* Clears last entered key

View file

@ -36,10 +36,10 @@ namespace Input {
bool poll(int timeout=0);
//* Get a key or mouse action from input
std::string get(bool clear = false);
std::string get();
//* Wait until input is available
void wait(bool clear=false);
//* Wait until input is available and return key
std::string wait();
//* Clears last entered key
void clear();

View file

@ -56,6 +56,41 @@ namespace Tools {
}
}
namespace Shared {
fs::path proc_path;
fs::path passwd_path;
fs::file_time_type passwd_time;
long page_size;
long clk_tck;
void init(){
proc_path = (fs::is_directory(fs::path("/proc")) and access("/proc", R_OK) != -1) ? "/proc" : "";
if (proc_path.empty()) {
string errmsg = "Proc filesystem not found or no permission to read from it!";
Logger::error(errmsg);
std::cout << "ERROR: " << errmsg << std::endl;
exit(1);
}
passwd_path = (access("/etc/passwd", R_OK) != -1) ? fs::path("/etc/passwd") : passwd_path;
if (passwd_path.empty()) Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
page_size = sysconf(_SC_PAGE_SIZE);
if (page_size <= 0) {
page_size = 4096;
Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
}
clk_tck = sysconf(_SC_CLK_TCK);
if (clk_tck <= 0) {
clk_tck = 100;
Logger::warning("Could not get system clocks per second. Defaulting to 100, processes cpu usage might be incorrect.");
}
}
}
namespace Proc {
namespace {
struct p_cache {
@ -67,15 +102,9 @@ namespace Proc {
};
unordered_flat_map<uint, p_cache> cache;
unordered_flat_map<string, string> uid_user;
fs::path passwd_path;
fs::file_time_type passwd_time;
uint counter = 0;
long page_size;
long clk_tck;
}
fs::path proc_path;
uint64_t old_cputimes = 0;
size_t numpids = 500;
atomic<bool> stop (false);
@ -92,7 +121,7 @@ namespace Proc {
};
//* Generate process tree list
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, bool collapsed, string& prefix){
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, const int cur_depth, const bool collapsed, const string& prefix){
auto cur_pos = out_procs.size();
if (not collapsed)
out_procs.push_back(cur_proc);
@ -128,6 +157,8 @@ namespace Proc {
auto& per_core = Config::getB("proc_per_core");
auto& tree = Config::getB("proc_tree");
ifstream pread;
string long_string;
string short_str;
auto uptime = system_uptime();
vector<proc_info> procs;
vector<uint> pid_list;
@ -138,11 +169,11 @@ namespace Proc {
(void)tree;
//* Update uid_user map if /etc/passwd changed since last run
if (not passwd_path.empty() and fs::last_write_time(passwd_path) != passwd_time) {
if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != Shared::passwd_time) {
string r_uid, r_user;
passwd_time = fs::last_write_time(passwd_path);
Shared::passwd_time = fs::last_write_time(Shared::passwd_path);
uid_user.clear();
pread.open(passwd_path);
pread.open(Shared::passwd_path);
if (pread.good()) {
while (not pread.eof()){
getline(pread, r_user, ':');
@ -157,7 +188,7 @@ namespace Proc {
//* Get cpu total times from /proc/stat
uint64_t cputimes = 0;
pread.open(proc_path / "stat");
pread.open(Shared::proc_path / "stat");
if (pread.good()) {
pread.ignore(SSmax, ' ');
for (uint64_t times; pread >> times; cputimes += times);
@ -166,7 +197,7 @@ namespace Proc {
else return current_procs;
//* Iterate over all pids in /proc
for (auto& d: fs::directory_iterator(proc_path)){
for (auto& d: fs::directory_iterator(Shared::proc_path)){
if (pread.is_open()) pread.close();
if (stop) {
collecting = false;
@ -195,7 +226,7 @@ namespace Proc {
pread.open(d.path() / "cmdline");
if (pread.good()) {
string tmpstr = "";
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + ' ';
pread.close();
if (not cmd.empty()) cmd.pop_back();
}
@ -238,58 +269,57 @@ namespace Proc {
//* Parse /proc/[pid]/stat
pread.open(d.path() / "stat");
if (pread.good()) {
string instr;
getline(pread, instr);
getline(pread, long_string);
pread.close();
size_t c_pos = 0, s_count = 0;
size_t s_count = 0, c_pos = 0;
uint64_t cpu_t = 0;
try {
//? Skip pid and comm field and find comm fields closing ')'
size_t s_pos = pid_str.size() + cache.at(new_proc.pid).name.size() + 4;
if (instr.at(s_pos - 2) != ')')
s_pos = instr.find_last_of(')') + 2;
do {
c_pos = instr.find(' ', s_pos);
if (long_string.at(s_pos - 2) != ')')
s_pos = long_string.find_last_of(')') + 2;
while (s_count++ <= 37) {
c_pos = long_string.find(' ', s_pos);
if (c_pos == string::npos) break;
switch (s_count) {
case 0: { //? Process state
new_proc.state = instr[s_pos];
case 1: { //? Process state
new_proc.state = long_string[s_pos];
break;
}
case 1: { //? Process parent pid
new_proc.ppid = stoull(instr.substr(s_pos, c_pos - s_pos));
case 2: { //? Process parent pid
new_proc.ppid = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 11: { //? Process utime
cpu_t = stoull(instr.substr(s_pos, c_pos - s_pos));
case 12: { //? Process utime
cpu_t = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 12: { //? Process stime
cpu_t += stoull(instr.substr(s_pos, c_pos - s_pos));
case 13: { //? Process stime
cpu_t += stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 16: { //? Process nice value
new_proc.p_nice = stoull(instr.substr(s_pos, c_pos - s_pos));
case 17: { //? Process nice value
new_proc.p_nice = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 17: { //? Process number of threads
new_proc.threads = stoull(instr.substr(s_pos, c_pos - s_pos));
case 18: { //? Process number of threads
new_proc.threads = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 19: { //? Cache cpu seconds
if (new_cache) cache[new_proc.pid].cpu_s = stoull(instr.substr(s_pos, c_pos - s_pos));
case 20: { //? Cache cpu seconds
if (new_cache) cache[new_proc.pid].cpu_s = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
case 36: { //? CPU number last executed on
new_proc.cpu_n = stoull(instr.substr(s_pos, c_pos - s_pos));
case 37: { //? CPU number last executed on
new_proc.cpu_n = stoull(long_string.substr(s_pos, c_pos - s_pos));
break;
}
}
s_pos = c_pos + 1;
} while (s_count++ < 36);
}
}
catch (...) {
continue;
@ -301,7 +331,7 @@ namespace Proc {
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0;
//? Process cumulative cpu usage since process start
new_proc.cpu_c = ((double)cpu_t / clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / clk_tck));
new_proc.cpu_c = ((double)cpu_t / Shared::clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / Shared::clk_tck));
//? Update cache with latest cpu times
cache[new_proc.pid].cpu_t = cpu_t;
@ -312,9 +342,9 @@ namespace Proc {
pread.open(d.path() / "statm");
if (pread.good()) {
pread.ignore(SSmax, ' ');
pread >> new_proc.mem;
getline(pread, short_str, ' ');
pread.close();
new_proc.mem *= page_size;
new_proc.mem = stoull(short_str) * Shared::page_size;
}
//? Push process to vector
@ -345,8 +375,8 @@ namespace Proc {
target = (max > 30.0) ? max : 10.0;
if (i == offset and procs[i].cpu_p > 30.0)
offset++;
else if
(procs[i].cpu_p > target) rotate(procs.begin() + offset, procs.begin() + i, procs.begin() + i + 1);
else if (procs[i].cpu_p > target)
rotate(procs.begin() + offset, procs.begin() + i, procs.begin() + i + 1);
}
}
@ -380,32 +410,6 @@ namespace Proc {
collecting = false;
return current_procs;
}
//* Initialize needed variables for collect
void init(){
proc_path = (fs::is_directory(fs::path("/proc")) and access("/proc", R_OK) != -1) ? "/proc" : "";
if (proc_path.empty()) {
string errmsg = "Proc filesystem not found or no permission to read from it!";
Logger::error(errmsg);
std::cout << "ERROR: " << errmsg << std::endl;
exit(1);
}
passwd_path = (access("/etc/passwd", R_OK) != -1) ? fs::path("/etc/passwd") : passwd_path;
if (passwd_path.empty()) Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
page_size = sysconf(_SC_PAGE_SIZE);
if (page_size <= 0) {
page_size = 4096;
Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
}
clk_tck = sysconf(_SC_CLK_TCK);
if (clk_tck <= 0) {
clk_tck = 100;
Logger::warning("Could not get system clocks per second. Defaulting to 100, processes cpu usage might be incorrect.");
}
}
}
#endif

View file

@ -36,8 +36,12 @@ namespace Tools {
double system_uptime();
}
namespace Shared {
//* Initialize platform specific needed variables and check for errors
void init();
}
namespace Proc {
extern std::filesystem::path proc_path;
extern size_t numpids;
extern std::atomic<bool> stop;
extern std::atomic<bool> collecting;
@ -58,9 +62,6 @@ namespace Proc {
extern vector<proc_info> current_procs;
//* Collects and sorts process information from /proc, saves to and returns reference to Proc::current_procs;
//* Collects and sorts process information from /proc, saves and returns reference to Proc::current_procs;
vector<proc_info>& collect();
//* Initialize needed variables for collect
void init();
}

View file

@ -22,6 +22,7 @@ tab-size = 4
#include <unordered_map>
#include <ranges>
#include <algorithm>
#include <fstream>
#include <btop_tools.hpp>
#include <btop_config.hpp>
@ -186,21 +187,6 @@ namespace Theme {
else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m";
}
array<int, 3> esc_to_rgb(string c_string){
array<int, 3> rgb = {-1, -1, -1};
if (c_string.size() >= 14){
c_string.erase(0, 7);
auto c_split = ssplit(c_string, ';');
if (c_split.size() == 3){
rgb[0] = stoi(c_split[0]);
rgb[1] = stoi(c_split[1]);
rgb[2] = stoi(c_split[2].erase(c_split[2].size()));
}
}
return rgb;
}
namespace {
unordered_flat_map<string, string> colors;
unordered_flat_map<string, array<int, 3>> rgbs;
@ -234,24 +220,40 @@ namespace Theme {
bool t_to_256 = Config::getB("lowcolor");
colors.clear(); rgbs.clear();
for (auto& [name, color] : Default_theme) {
if (name == "main_bg" and not Config::getB("theme_background")) {
colors[name] = "\x1b[49m";
rgbs[name] = {-1, -1, -1};
continue;
}
depth = (name.ends_with("bg") and name != "meter_bg") ? "bg" : "fg";
if (source.contains(name)) {
if (source.at(name)[0] == '#') {
if (name == "main_bg" and source.at(name).empty()) {
colors[name] = "\x1b[49m";
rgbs[name] = {-1, -1, -1};
continue;
}
else if (source.at(name).empty() and (name.ends_with("_mid") or name.ends_with("_end"))) {
colors[name] = "";
rgbs[name] = {-1, -1, -1};
continue;
}
else if (source.at(name).starts_with('#')) {
colors[name] = hex_to_color(source.at(name), t_to_256, depth);
rgbs[name] = hex_to_dec(source.at(name));
}
else {
else if (not source.at(name).empty()) {
t_rgb = ssplit(source.at(name));
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])};
}
}
}
if (colors[name].empty()) {
Logger::info("Missing color value for \"" + name + "\". Using value from default.");
Logger::debug("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};
}
@ -268,21 +270,21 @@ namespace Theme {
array<array<int, 3>, 101> dec_arr;
dec_arr[0][0] = -1;
string wname = rtrim(name, "_start");
array<array<int, 3>, 3> rgb_arr = {source_arr, rgbs[wname + "_mid"], rgbs[wname + "_end"]};
array<array<int, 3>, 3> rgb_array = {source_arr, rgbs[wname + "_mid"], rgbs[wname + "_end"]};
//? Only start iteration if gradient has a _end color value defined
if (rgb_arr[2][0] >= 0) {
if (rgb_array[2][0] >= 0) {
//? Split iteration in two passes of 50 + 51 instead of 101 if gradient has _start, _mid and _end values defined
int rng = (rgb_arr[1][0] >= 0) ? 50 : 100;
int cur_range = (rgb_array[1][0] >= 0) ? 50 : 100;
for (int rgb : iota(0, 3)){
int arr1 = 0, offset = 0;
int arr2 = (rng == 50) ? 1 : 2;
int start = 0, offset = 0;
int end = (cur_range == 50) ? 1 : 2;
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;
dec_arr[i][rgb] = rgb_array[start][rgb] + (i - offset) * (rgb_array[end][rgb] - rgb_array[start][rgb]) / cur_range;
//? Switch source arrays from _start/_mid to _mid/_end at 50 passes if _mid is defined
if (i == rng) { ++arr1; ++arr2; offset = 50;}
if (i == cur_range) { ++start; ++end; offset = 50;}
}
}
}
@ -301,8 +303,8 @@ namespace Theme {
//* Set colors and generate gradients for the TTY theme
void generateTTYColors(){
rgbs.clear();
colors = TTY_theme;
gradients.clear();
colors = TTY_theme;
for (auto& c : colors) {
if (not c.first.ends_with("_start")) continue;
@ -319,10 +321,39 @@ namespace Theme {
}
}
//* Load a .theme file from disk
auto loadFile(string filename){
unordered_flat_map<string, string> out;
unordered_flat_map<string, string> theme_out;
fs::path filepath = filename;
if (not fs::exists(filepath))
return Default_theme;
return out;
std::ifstream themefile(filepath);
if (themefile.good()) {
Logger::debug("Loading theme file: " + filename);
while (not themefile.bad()) {
themefile.ignore(SSmax, '[');
if (themefile.eof()) break;
string name, value;
getline(themefile, name, ']');
if (not Default_theme.contains(name)) {
continue;
}
themefile.ignore(SSmax, '=');
themefile >> std::ws;
if (themefile.eof()) break;
if (themefile.peek() == '"') {
themefile.ignore(1);
getline(themefile, value, '"');
}
else getline(themefile, value, '\n');
theme_out[name] = value;
}
themefile.close();
return theme_out;
}
return Default_theme;
}
}
@ -331,11 +362,18 @@ namespace Theme {
themes.push_back("Default");
themes.push_back("TTY");
for (const auto& path : { theme_dir, user_theme_dir } ) {
for (auto& file : fs::directory_iterator(path)){
if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1) {
themes.push_back(file.path().c_str());
}
}
}
}
//* Set current theme using <source> map
void set(string theme){
void setTheme(){
string theme = Config::getS("color_theme");
if (theme == "TTY" or Config::getB("tty_mode"))
generateTTYColors();
else {
@ -362,10 +400,10 @@ namespace Theme {
return rgbs.at(name);
}
robin_hood::unordered_flat_map<string, string>& test_colors(){
unordered_flat_map<string, string>& test_colors(){
return colors;
}
robin_hood::unordered_flat_map<string, std::array<string, 101>>& test_gradients(){
unordered_flat_map<string, std::array<string, 101>>& test_gradients(){
return gradients;
}

View file

@ -28,6 +28,7 @@ namespace Theme {
extern std::filesystem::path theme_dir;
extern std::filesystem::path user_theme_dir;
//* Contains "Default" and "TTY" at indeces 0 and 1, otherwise full paths to theme files
extern vector<string> themes;
//* Generate escape sequence for 24-bit or 256 color and return as a string
@ -42,14 +43,11 @@ namespace Theme {
//* depth: ["fg"|"bg"] for either a foreground color or a background color
string dec_to_color(int r, int g, int b, bool t_to_256=false, string depth="fg");
//* Return an array of red, green and blue, 0-255 values for a 24-bit color escape string
std::array<int, 3> esc_to_rgb(string c_string);
//* Update list of available themes
//* Update list of paths for available themes
void updateThemes();
//* Set current theme using <source> map
void set(string theme);
//* Set current theme from current "color_theme" value in config
void setTheme();
//* Return escape code for color <name>
const string& c(string name);