Added: Displayed battery selectable in options menu

This commit is contained in:
aristocratos 2021-10-17 22:26:43 +02:00
parent dfef4fdcc8
commit 4cacdf28fe
4 changed files with 77 additions and 39 deletions

View file

@ -170,6 +170,8 @@ namespace Config {
{"show_battery", "#* Show battery stats in top right if battery is present."},
{"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."},
{"log_level", "#* Set loglevel for \"~/.config/btop/btop.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."}
};
@ -187,6 +189,7 @@ namespace Config {
{"cpu_graph_upper", "total"},
{"cpu_graph_lower", "total"},
{"cpu_sensor", "Auto"},
{"selected_battery", "Auto"},
{"cpu_core_map", ""},
{"temp_scale", "celsius"},
{"clock_format", "%X"},
@ -265,6 +268,8 @@ namespace Config {
fs::path conf_dir;
fs::path conf_file;
vector<string> available_batteries = {"Auto"};
vector<string> current_boxes;
vector<string> preset_list = {"cpu:0:default,mem:0:default,net:0:default,proc:0:default"};
int current_preset = -1;

View file

@ -45,6 +45,7 @@ namespace Config {
extern vector<string> current_boxes;
extern vector<string> preset_list;
extern vector<string> available_batteries;
extern int current_preset;
//* Check if string only contains space seperated valid names for boxes

View file

@ -270,6 +270,13 @@ namespace Menu {
"",
"Show battery stats in the top right corner",
"if a battery is present."},
{"selected_battery",
"Select battery.",
"",
"Which battery to use if multiple are present.",
"Can be both batteries and UPS.",
"",
"\"Auto\" for auto detection."},
{"log_level",
"Set loglevel for error.log",
"",
@ -962,7 +969,8 @@ namespace Menu {
{"graph_symbol_proc", std::cref(Config::valid_graph_symbols_def)},
{"cpu_graph_upper", std::cref(Cpu::available_fields)},
{"cpu_graph_lower", std::cref(Cpu::available_fields)},
{"cpu_sensor", std::cref(Cpu::available_sensors)}
{"cpu_sensor", std::cref(Cpu::available_sensors)},
{"selected_battery", std::cref(Config::available_batteries)},
};
auto& tty_mode = Config::getB("tty_mode");
auto& vim_keys = Config::getB("vim_keys");

View file

@ -468,75 +468,99 @@ namespace Cpu {
return core_map;
}
struct battery {
fs::path base_dir, energy_now, energy_full, power_now, status, online;
string device_type;
bool use_energy = true;
};
auto get_battery() -> tuple<int, long, string> {
if (not has_battery) return {0, 0, ""};
static fs::path bat_dir, energy_now_path, energy_full_path, power_now_path, status_path, online_path;
static bool use_energy = true;
static string auto_sel;
static unordered_flat_map<string, battery> batteries;
//? Get paths to needed files and check for valid values on first run
if (bat_dir.empty() and has_battery) {
if (batteries.empty() and has_battery) {
if (fs::exists("/sys/class/power_supply")) {
for (const auto& d : fs::directory_iterator("/sys/class/power_supply")) {
//? Only consider online power supplies of type Battery or UPS
//? see kernel docs for details on the file structure and contents
//? https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power
battery new_bat;
fs::path bat_dir;
try {
if (not d.is_directory()
or not fs::exists(d.path() / "type")
or not fs::exists(d.path() / "type")
or not fs::exists(d.path() / "present")
or stoi(readfile(d.path() / "present")) != 1)
continue;
string type = readfile(d.path() / "type");
if (type == "Battery" or type == "UPS") {
string dev_type = readfile(d.path() / "type");
if (is_in(dev_type, "Battery", "UPS")) {
bat_dir = d.path();
break;
new_bat.base_dir = d.path();
new_bat.device_type = dev_type;
}
} catch (...) {
//? skip power supplies not conforming to the kernel standard
continue;
}
if (fs::exists(bat_dir / "energy_now")) new_bat.energy_now = bat_dir / "energy_now";
else if (fs::exists(bat_dir / "charge_now")) new_bat.energy_now = bat_dir / "charge_now";
else new_bat.use_energy = false;
if (fs::exists(bat_dir / "energy_full")) new_bat.energy_full = bat_dir / "energy_full";
else if (fs::exists(bat_dir / "charge_full")) new_bat.energy_full = bat_dir / "charge_full";
else new_bat.use_energy = false;
if (not new_bat.use_energy and not fs::exists(bat_dir / "capacity")) {
continue;
}
if (fs::exists(bat_dir / "power_now")) new_bat.power_now = bat_dir / "power_now";
else if (fs::exists(bat_dir / "current_now")) new_bat.power_now = bat_dir / "current_now";
if (fs::exists(bat_dir / "AC0/online")) new_bat.online = bat_dir / "AC0/online";
else if (fs::exists(bat_dir / "AC/online")) new_bat.online = bat_dir / "AC/online";
batteries[bat_dir.filename()] = new_bat;
Config::available_batteries.push_back(bat_dir.filename());
}
}
if (bat_dir.empty()) {
if (batteries.empty()) {
has_battery = false;
return {0, 0, ""};
}
else {
if (fs::exists(bat_dir / "energy_now")) energy_now_path = bat_dir / "energy_now";
else if (fs::exists(bat_dir / "charge_now")) energy_now_path = bat_dir / "charge_now";
else use_energy = false;
if (fs::exists(bat_dir / "energy_full")) energy_full_path = bat_dir / "energy_full";
else if (fs::exists(bat_dir / "charge_full")) energy_full_path = bat_dir / "charge_full";
else use_energy = false;
if (not use_energy and not fs::exists(bat_dir / "capacity")) {
has_battery = false;
return {0, 0, ""};
}
if (fs::exists(bat_dir / "power_now")) power_now_path = bat_dir / "power_now";
else if (fs::exists(bat_dir / "current_now")) power_now_path = bat_dir / "current_now";
if (fs::exists(bat_dir / "AC0/online")) online_path = bat_dir / "AC0/online";
else if (fs::exists(bat_dir / "AC/online")) online_path = bat_dir / "AC/online";
}
}
auto& battery_sel = Config::getS("selected_battery");
if ((battery_sel == "Auto" and auto_sel.empty())) {
for (auto& [name, bat] : batteries) {
if (bat.device_type == "Battery") {
auto_sel = name;
break;
}
}
if (auto_sel.empty()) auto_sel = batteries.begin()->first;
}
auto& b = (battery_sel != "Auto" and batteries.contains(battery_sel) ? batteries.at(battery_sel) : batteries.at(auto_sel));
int percent = -1;
long seconds = -1;
//? Try to get battery percentage
if (use_energy) {
if (b.use_energy) {
try {
percent = round(100.0 * stoll(readfile(energy_now_path, "-1")) / stoll(readfile(energy_full_path, "1")));
percent = round(100.0 * stoll(readfile(b.energy_now, "-1")) / stoll(readfile(b.energy_full, "1")));
}
catch (const std::invalid_argument&) { }
catch (const std::out_of_range&) { }
}
if (percent < 0) {
try {
percent = stoll(readfile(bat_dir / "capacity", "-1"));
percent = stoll(readfile(b.base_dir / "capacity", "-1"));
}
catch (const std::invalid_argument&) { }
catch (const std::out_of_range&) { }
@ -547,9 +571,9 @@ namespace Cpu {
}
//? Get charging/discharging status
string status = str_to_lower(readfile(bat_dir / "status", "unknown"));
if (status == "unknown" and not online_path.empty()) {
const auto online = readfile(online_path, "0");
string status = str_to_lower(readfile(b.base_dir / "status", "unknown"));
if (status == "unknown" and not b.online.empty()) {
const auto online = readfile(b.online, "0");
if (online == "1" and percent < 100) status = "charging";
else if (online == "1") status = "full";
else status = "discharging";
@ -557,16 +581,16 @@ namespace Cpu {
//? Get seconds to empty
if (not is_in(status, "charging", "full")) {
if (use_energy and not power_now_path.empty()) {
if (b.use_energy and not b.power_now.empty()) {
try {
seconds = round((double)stoll(readfile(energy_now_path, "0")) / stoll(readfile(power_now_path, "1")) * 3600);
seconds = round((double)stoll(readfile(b.energy_now, "0")) / stoll(readfile(b.power_now, "1")) * 3600);
}
catch (const std::invalid_argument&) { }
catch (const std::out_of_range&) { }
}
if (seconds < 0 and fs::exists(bat_dir / "time_to_empty")) {
if (seconds < 0 and fs::exists(b.base_dir / "time_to_empty")) {
try {
seconds = stoll(readfile(bat_dir / "time_to_empty", "0")) * 60;
seconds = stoll(readfile(b.base_dir / "time_to_empty", "0")) * 60;
}
catch (const std::invalid_argument&) { }
catch (const std::out_of_range&) { }