diff --git a/CMakeLists.txt b/CMakeLists.txt index cd399462..009a3192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,8 @@ if(APPLE) target_sources(btop PRIVATE src/osx/btop_collect.cpp src/osx/sensors.cpp src/osx/smc.cpp) elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") target_sources(btop PRIVATE src/freebsd/btop_collect.cpp) +elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + target_sources(btop PRIVATE src/openbsd/btop_collect.cpp src/openbsd/sysctlbyname.cpp) elseif(LINUX) target_sources(btop PRIVATE src/linux/btop_collect.cpp) else() @@ -110,6 +112,7 @@ if(HAS_FCF_PROTECTION) endif() target_compile_definitions(btop PRIVATE + FMT_HEADER_ONLY _FILE_OFFSET_BITS=64 $<$:_GLIBCXX_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS=1> # Only has an effect with optimizations enabled @@ -179,12 +182,18 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") endif() find_package(devstat REQUIRED) - target_link_libraries(btop devstat::devstat) + find_package(kvm REQUIRED) + target_link_libraries(btop devstat::devstat kvm::kvm) if(BTOP_STATIC) find_package(elf REQUIRED) - find_package(kvm REQUIRED) - target_link_libraries(btop elf::elf kvm::kvm) + target_link_libraries(btop elf::elf) endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(btop PRIVATE -static-libstdc++) + endif() + find_package(kvm REQUIRED) + target_link_libraries(btop kvm::kvm) endif() install(TARGETS btop RUNTIME) diff --git a/Makefile b/Makefile index ed808995..9af2aa97 100644 --- a/Makefile +++ b/Makefile @@ -143,7 +143,7 @@ else ifeq ($(PLATFORM_LC),macos) else ifeq ($(PLATFORM_LC),openbsd) PLATFORM_DIR := openbsd THREADS := $(shell sysctl -n hw.ncpu || echo 1) - override ADDFLAGS += -lkvm + override ADDFLAGS += -lkvm -static-libstdc++ export MAKE = gmake SU_GROUP := wheel else @@ -179,7 +179,7 @@ override GOODFLAGS := $(foreach flag,$(TESTFLAGS),$(strip $(shell echo "int main override REQFLAGS := -std=c++20 WARNFLAGS := -Wall -Wextra -pedantic OPTFLAGS := -O2 -ftree-vectorize -flto=$(LTO) -LDCXXFLAGS := -pthread -D_GLIBCXX_ASSERTIONS -D_FILE_OFFSET_BITS=64 $(GOODFLAGS) $(ADDFLAGS) +LDCXXFLAGS := -pthread -DFMT_HEADER_ONLY -D_GLIBCXX_ASSERTIONS -D_FILE_OFFSET_BITS=64 $(GOODFLAGS) $(ADDFLAGS) override CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS) override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS) INC := $(foreach incdir,$(INCDIRS),-isystem $(incdir)) -I$(SRCDIR) diff --git a/README.md b/README.md index 13e57aed..042b49ba 100644 --- a/README.md +++ b/README.md @@ -995,6 +995,74 @@ If you have an AMD GPU `rocm_smi_lib` is required, which may or may not be packa gmake help ``` + +
+ + +### With CMake (Community maintained) + + +1. **Install build dependencies** + + Requires GCC, CMake, Ninja and Git + + _**Note:** LLVM's libc++ shipped with OpenBSD 7.4 is too old and cannot compile btop._ + + ```bash + pkg_add cmake g++%11 git ninja + ``` + +2. **Clone the repository** + + ```bash + git clone https://github.com/aristocratos/btop.git && cd btop + ``` + +3. **Compile** + + ```bash + # Configure + CXX=eg++ cmake -B build -G Ninja + # Build + cmake --build build + ``` + + This will automatically build a release version of btop. + + Some useful options to pass to the configure step: + + | Configure flag | Description | + |---------------------------------|-------------------------------------------------------------------------| + | `-DBTOP_LTO=` | Enables link time optimization (ON by default) | + | `-DBTOP_USE_MOLD=` | Use mold to link btop (OFF by default) | + | `-DBTOP_PEDANTIC=` | Compile with additional warnings (OFF by default) | + | `-DBTOP_WERROR=` | Compile with warnings as errors (OFF by default) | + | `-DBTOP_FORTIFY=` | Detect buffer overflows with `_FORTIFY_SOURCE=3` (ON by default) | + | `-DCMAKE_INSTALL_PREFIX=` | The installation prefix ('/usr/local' by default) | + + To force any other compiler, run `CXX= cmake -B build -G Ninja` + +4. **Install** + + ```bash + cmake --install build + ``` + + May require root privileges + +5. **Uninstall** + + CMake doesn't generate an uninstall target by default. To remove installed files, run + ``` + cat build/install_manifest.txt | xargs rm -irv + ``` + +6. **Cleanup build directory** + + ```bash + cmake --build build -t clean + ``` +
## Installing the snap diff --git a/cmake/Modules/Findkvm.cmake b/cmake/Modules/Findkvm.cmake index a0847def..9e4d82b9 100644 --- a/cmake/Modules/Findkvm.cmake +++ b/cmake/Modules/Findkvm.cmake @@ -3,7 +3,7 @@ # Find libkvm, the Kernel Data Access Library # -if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") +if(BSD) find_path(kvm_INCLUDE_DIR NAMES kvm.h) find_library(kvm_LIBRARY NAMES kvm) diff --git a/src/btop.cpp b/src/btop.cpp index 736854fc..5b22d790 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -53,6 +53,7 @@ tab-size = 4 #include "btop_draw.hpp" #include "btop_menu.hpp" #include "fmt/core.h" +#include "fmt/ostream.h" using std::atomic; using std::cout; @@ -108,6 +109,7 @@ namespace Global { atomic should_sleep (false); atomic _runner_started (false); atomic init_conf (false); + atomic reload_conf (false); bool arg_tty{}; bool arg_low_color{}; @@ -364,7 +366,36 @@ void _signal_handler(const int sig) { case SIGUSR1: // Input::poll interrupt break; + case SIGUSR2: + Global::reload_conf = true; + Input::interrupt(); + break; + } +} + +//* Config init +void init_config(){ + atomic_lock lck(Global::init_conf); + vector load_warnings; + Config::load(Config::conf_file, load_warnings); + Config::set("lowcolor", (Global::arg_low_color ? true : not Config::getB("truecolor"))); + + static bool first_init = true; + + if (Global::debug and first_init) { + Logger::set("DEBUG"); + Logger::debug("Running in DEBUG mode!"); + } + else Logger::set(Config::getS("log_level")); + + static string log_level; + if (const string current_level = Config::getS("log_level"); log_level != current_level) { + log_level = current_level; + Logger::info("Logger set to " + (Global::debug ? "DEBUG" : log_level)); } + + for (const auto& err_str : load_warnings) Logger::warning(err_str); + first_init = false; } //* Manages secondary thread for collection and drawing of boxes @@ -895,22 +926,7 @@ int main(int argc, char **argv) { } //? Config init - { - atomic_lock lck(Global::init_conf); - vector load_warnings; - Config::load(Config::conf_file, load_warnings); - Config::set("lowcolor", (Global::arg_low_color ? true : not Config::getB("truecolor"))); - - if (Global::debug) { - Logger::set("DEBUG"); - Logger::debug("Starting in DEBUG mode!"); - } - else Logger::set(Config::getS("log_level")); - - Logger::info("Logger set to " + (Global::debug ? "DEBUG" : Config::getS("log_level"))); - - for (const auto& err_str : load_warnings) Logger::warning(err_str); - } + init_config(); //? Try to find and set a UTF-8 locale if (std::setlocale(LC_ALL, "") != nullptr and not s_contains((string)std::setlocale(LC_ALL, ""), ";") @@ -920,7 +936,7 @@ int main(int argc, char **argv) { else { string found; bool set_failure{}; - for (const auto loc_env : array{"LANG", "LC_ALL"}) { + for (const auto loc_env : array{"LANG", "LC_ALL", "LC_CTYPE"}) { if (std::getenv(loc_env) != nullptr and str_to_upper(s_replace((string)std::getenv(loc_env), "-", "")).ends_with("UTF8")) { found = std::getenv(loc_env); if (std::setlocale(LC_ALL, found.c_str()) == nullptr) { @@ -1035,6 +1051,7 @@ int main(int argc, char **argv) { std::signal(SIGCONT, _signal_handler); std::signal(SIGWINCH, _signal_handler); std::signal(SIGUSR1, _signal_handler); + std::signal(SIGUSR2, _signal_handler); sigset_t mask; sigemptyset(&mask); @@ -1086,9 +1103,27 @@ int main(int argc, char **argv) { try { while (not true not_eq not false) { //? Check for exceptions in secondary thread and exit with fail signal if true - if (Global::thread_exception) clean_quit(1); - else if (Global::should_quit) clean_quit(0); - else if (Global::should_sleep) { Global::should_sleep = false; _sleep(); } + if (Global::thread_exception) { + clean_quit(1); + } + else if (Global::should_quit) { + clean_quit(0); + } + else if (Global::should_sleep) { + Global::should_sleep = false; + _sleep(); + } + //? Hot reload config from CTRL + R or SIGUSR2 + else if (Global::reload_conf) { + Global::reload_conf = false; + if (Runner::active) Runner::stop(); + Config::unlock(); + init_config(); + Theme::updateThemes(); + Theme::setTheme(); + Draw::banner_gen(0, 0, false, true); + Global::resized = true; + } //? Make sure terminal size hasn't changed (in case of SIGWINCH not working properly) term_resize(Global::resized); @@ -1123,9 +1158,9 @@ int main(int argc, char **argv) { update_ms = Config::getI("update_ms"); future_time = time_ms() + update_ms; } - else if (future_time - current_time > update_ms) + else if (future_time - current_time > update_ms) { future_time = current_time; - + } //? Poll for input and process any input detected else if (Input::poll(min((uint64_t)1000, future_time - current_time))) { if (not Runner::active) Config::unlock(); diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 18e60ae5..aa4d8131 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -199,6 +199,8 @@ namespace Config { {"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."}, + {"show_battery_watts", "#* Show power stats of battery next to charge indicator."}, + {"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."}, #ifdef GPU_SUPPORT @@ -293,6 +295,7 @@ namespace Config { {"net_auto", true}, {"net_sync", true}, {"show_battery", true}, + {"show_battery_watts", true}, {"vim_keys", false}, {"tty_mode", false}, {"disk_free_priv", false}, @@ -729,9 +732,9 @@ namespace Config { if (geteuid() != Global::real_uid and seteuid(Global::real_uid) != 0) return; std::ofstream cwrite(conf_file, std::ios::trunc); if (cwrite.good()) { - cwrite << "#? Config file for btop v. " << Global::Version; + cwrite << "#? Config file for btop v. " << Global::Version << "\n"; for (auto [name, description] : descriptions) { - cwrite << "\n\n" << (description.empty() ? "" : description + "\n") + cwrite << "\n" << (description.empty() ? "" : description + "\n") << name << " = "; if (strings.contains(name)) cwrite << "\"" << strings.at(name) << "\""; @@ -739,6 +742,7 @@ namespace Config { cwrite << ints.at(name); else if (bools.contains(name)) cwrite << (bools.at(name) ? "True" : "False"); + cwrite << "\n"; } } } diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index acab14c6..bfd12cef 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -706,6 +706,7 @@ namespace Cpu { if (Config::getB("show_battery") and has_battery) { static int old_percent{}; // defaults to = 0 static long old_seconds{}; // defaults to = 0 + static float old_watts{}; // defaults to = 0 static string old_status; static Draw::Meter bat_meter {10, "cpu", true}; static const std::unordered_map bat_symbols = { @@ -715,16 +716,18 @@ namespace Cpu { {"unknown", "○"} }; - const auto& [percent, seconds, status] = current_bat; + const auto& [percent, watts, seconds, status] = current_bat; - if (redraw or percent != old_percent or seconds != old_seconds or status != old_status) { + if (redraw or percent != old_percent or (watts != old_watts and Config::getB("show_battery_watts")) or seconds != old_seconds or status != old_status) { old_percent = percent; + old_watts = watts; old_seconds = seconds; old_status = status; const string str_time = (seconds > 0 ? sec_to_dhms(seconds, true, true) : ""); const string str_percent = to_string(percent) + '%'; + const string str_watts = (watts != -1 and Config::getB("show_battery_watts") ? fmt::format("{:.2f}", watts) + 'W' : ""); const auto& bat_symbol = bat_symbols.at((bat_symbols.contains(status) ? status : "unknown")); - const int current_len = (Term::width >= 100 ? 11 : 0) + str_time.size() + str_percent.size() + to_string(Config::getI("update_ms")).size(); + const int current_len = (Term::width >= 100 ? 11 : 0) + str_time.size() + str_percent.size() + str_watts.size() + to_string(Config::getI("update_ms")).size(); const int current_pos = Term::width - current_len - 17; if ((bat_pos != current_pos or bat_len != current_len) and bat_pos > 0 and not redraw) @@ -734,7 +737,7 @@ namespace Cpu { out += Mv::to(y, bat_pos) + title_left + Theme::c("title") + Fx::b + "BAT" + bat_symbol + ' ' + str_percent + (Term::width >= 100 ? Fx::ub + ' ' + bat_meter(percent) + Fx::b : "") - + (not str_time.empty() ? ' ' + Theme::c("title") + str_time : " ") + Fx::ub + title_right; + + (not str_time.empty() ? ' ' + Theme::c("title") + str_time : "") + (not str_watts.empty() ? " " + Theme::c("title") + Fx::b + str_watts : "") + Fx::ub + title_right; } } else if (bat_pos > 0) { @@ -1361,6 +1364,7 @@ namespace Net { int x = 1, y, width = 20, height; int b_x, b_y, b_width, b_height, d_graph_height, u_graph_height; bool shown = true, redraw = true; + const int MAX_IFNAMSIZ = 15; string old_ip; std::unordered_map graphs; string box; @@ -1381,7 +1385,7 @@ namespace Net { out.reserve(width * height); const string title_left = Theme::c("net_box") + Fx::ub + Symbols::title_left; const string title_right = Theme::c("net_box") + Fx::ub + Symbols::title_right; - const int i_size = min((int)selected_iface.size(), 10); + const int i_size = min((int)selected_iface.size(), MAX_IFNAMSIZ); const long long down_max = (net_auto ? safeVal(graph_max, "download"s) : ((long long)(Config::getI("net_download")) << 20) / 8); const long long up_max = (net_auto ? safeVal(graph_max, "upload"s) : ((long long)(Config::getI("net_upload")) << 20) / 8); @@ -1403,7 +1407,7 @@ namespace Net { //? Interface selector and buttons out += Mv::to(y, x+width - i_size - 9) + title_left + Fx::b + Theme::c("hi_fg") + "" + title_right + + uresize(selected_iface, MAX_IFNAMSIZ) + Theme::c("hi_fg") + " n>" + title_right + Mv::to(y, x+width - i_size - 15) + title_left + Theme::c("hi_fg") + (safeVal(net.stat, "download"s).offset + safeVal(net.stat, "upload"s).offset > 0 ? Fx::b : "") + 'z' + Theme::c("title") + "ero" + title_right; Input::mouse_mappings["b"] = {y, x+width - i_size - 8, 1, 3}; @@ -2238,3 +2242,4 @@ namespace Draw { } } } + diff --git a/src/btop_input.cpp b/src/btop_input.cpp index fbbae585..6bfe9d8f 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -41,6 +41,7 @@ namespace Input { //* Map for translating key codes to readable values const std::unordered_map Key_escapes = { {"\033", "escape"}, + {"\x12", "ctrl_r"}, {"\n", "enter"}, {" ", "space"}, {"\x7f", "backspace"}, @@ -258,8 +259,10 @@ namespace Input { Draw::calcSizes(); Runner::run("all", false, true); return; - } - else + } else if (is_in(key, "ctrl_r")) { + kill(getpid(), SIGUSR2); + return; + } else keep_going = true; if (not keep_going) return; diff --git a/src/btop_menu.cpp b/src/btop_menu.cpp index fb119aad..fbf07c29 100644 --- a/src/btop_menu.cpp +++ b/src/btop_menu.cpp @@ -177,6 +177,7 @@ namespace Menu { {"F2, o", "Shows options."}, {"F1, ?, h", "Shows this window."}, {"ctrl + z", "Sleep program and put in background."}, + {"ctrl + r", "Reloads config file from disk."}, {"q, ctrl + c", "Quits program."}, {"+, -", "Add/Subtract 100ms to/from update timer."}, {"Up, Down", "Select in process list."}, @@ -353,6 +354,11 @@ namespace Menu { "Can be both batteries and UPS.", "", "\"Auto\" for auto detection."}, + {"show_battery_watts", + "Show battery power.", + "", + "Show discharge power when discharging.", + "Show charging power when charging."}, {"log_level", "Set loglevel for error.log", "", diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index 80313210..f927e050 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -196,7 +196,7 @@ namespace Cpu { extern string cpuName, cpuHz; extern vector available_fields; extern vector available_sensors; - extern tuple current_bat; + extern tuple current_bat; struct cpu_info { std::unordered_map> cpu_percent = { @@ -231,7 +231,7 @@ namespace Cpu { auto get_cpuHz() -> string; //* Get battery info from /sys - auto get_battery() -> tuple; + auto get_battery() -> tuple; } namespace Mem { diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp index 705a5eb8..25027863 100644 --- a/src/btop_tools.hpp +++ b/src/btop_tools.hpp @@ -18,6 +18,10 @@ tab-size = 4 #pragma once +#if !defined(NDEBUG) +# define BTOP_DEBUG +#endif + #include // for std::ranges::count_if #include #include @@ -42,11 +46,9 @@ tab-size = 4 #define HOST_NAME_MAX 64 #endif #endif -#define FMT_HEADER_ONLY + #include "fmt/core.h" #include "fmt/format.h" -#include "fmt/ostream.h" -#include "fmt/ranges.h" using std::array; using std::atomic; diff --git a/src/freebsd/btop_collect.cpp b/src/freebsd/btop_collect.cpp index d64a8785..decd5425 100644 --- a/src/freebsd/btop_collect.cpp +++ b/src/freebsd/btop_collect.cpp @@ -195,7 +195,7 @@ namespace Cpu { string cpuName; string cpuHz; bool has_battery = true; - tuple current_bat; + tuple current_bat; const array time_names = {"user", "nice", "system", "idle"}; @@ -361,10 +361,11 @@ namespace Cpu { return core_map; } - auto get_battery() -> tuple { - if (not has_battery) return {0, 0, ""}; + auto get_battery() -> tuple { + if (not has_battery) return {0, 0, 0, ""}; long seconds = -1; + float watts = -1; uint32_t percent = -1; size_t size = sizeof(percent); string status = "discharging"; @@ -376,6 +377,10 @@ namespace Cpu { if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, nullptr, 0) < 0) { seconds = 0; } + size = sizeof(watts); + if (sysctlbyname("hw.acpi.battery.rate", &watts, &size, nullptr, 0) < 0) { + watts = -1; + } int state; size = sizeof(state); if (sysctlbyname("hw.acpi.battery.state", &state, &size, nullptr, 0) < 0) { @@ -390,7 +395,7 @@ namespace Cpu { } } - return {percent, seconds, status}; + return {percent, watts, seconds, status}; } auto collect(bool no_update) -> cpu_info & { diff --git a/src/linux/btop_collect.cpp b/src/linux/btop_collect.cpp index 7096ad93..0dc59a81 100644 --- a/src/linux/btop_collect.cpp +++ b/src/linux/btop_collect.cpp @@ -301,7 +301,7 @@ namespace Cpu { string cpuName; string cpuHz; bool has_battery = true; - tuple current_bat; + tuple current_bat; const array time_names { "user"s, "nice"s, "system"s, "idle"s, "iowait"s, @@ -590,7 +590,7 @@ namespace Cpu { cpuhz += " GHz"; } else if (hz > 0) - cpuhz = to_string((int)round(hz)) + " MHz"; + cpuhz = to_string((int)hz) + " MHz"; } catch (const std::exception& e) { @@ -671,13 +671,14 @@ namespace Cpu { } struct battery { - fs::path base_dir, energy_now, energy_full, power_now, status, online; + fs::path base_dir, energy_now, charge_now, energy_full, charge_full, power_now, current_now, voltage_now, status, online; string device_type; - bool use_energy = true; + bool use_energy_or_charge = true; + bool use_power = true; }; - auto get_battery() -> tuple { - if (not has_battery) return {0, 0, ""}; + auto get_battery() -> tuple { + if (not has_battery) return {0, 0, 0, ""}; static string auto_sel; static std::unordered_map batteries; @@ -709,19 +710,27 @@ namespace Cpu { } 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; + else if (fs::exists(bat_dir / "charge_now")) new_bat.charge_now = bat_dir / "charge_now"; + else new_bat.use_energy_or_charge = 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; + else if (fs::exists(bat_dir / "charge_full")) new_bat.charge_full = bat_dir / "charge_full"; + else new_bat.use_energy_or_charge = false; - if (not new_bat.use_energy and not fs::exists(bat_dir / "capacity")) { + if (not new_bat.use_energy_or_charge 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 / "power_now")) { + new_bat.power_now = bat_dir / "power_now"; + } + else if ((fs::exists(bat_dir / "current_now")) and (fs::exists(bat_dir / "current_now"))) { + new_bat.current_now = bat_dir / "current_now"; + new_bat.voltage_now = bat_dir / "voltage_now"; + } + else { + new_bat.use_power = false; + } 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"; @@ -736,7 +745,7 @@ namespace Cpu { } if (batteries.empty()) { has_battery = false; - return {0, 0, ""}; + return {0, 0, 0, ""}; } } @@ -756,25 +765,33 @@ namespace Cpu { int percent = -1; long seconds = -1; + float watts = -1; //? Try to get battery percentage - if (b.use_energy) { + if (percent < 0) { + try { + percent = stoll(readfile(b.base_dir / "capacity", "-1")); + } + catch (const std::invalid_argument&) { } + catch (const std::out_of_range&) { } + } + if (b.use_energy_or_charge and percent < 0) { try { 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) { + if (b.use_energy_or_charge and percent < 0) { try { - percent = stoll(readfile(b.base_dir / "capacity", "-1")); + percent = round(100.0 * stoll(readfile(b.charge_now, "-1")) / stoll(readfile(b.charge_full, "1"))); } catch (const std::invalid_argument&) { } catch (const std::out_of_range&) { } } if (percent < 0) { has_battery = false; - return {0, 0, ""}; + return {0, 0, 0, ""}; } //? Get charging/discharging status @@ -788,23 +805,52 @@ namespace Cpu { //? Get seconds to empty if (not is_in(status, "charging", "full")) { - if (b.use_energy and not b.power_now.empty()) { + if (b.use_energy_or_charge ) { + if (not b.power_now.empty()) { + try { + 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&) { } + } + else if (not b.current_now.empty()) { + try { + seconds = round((double)stoll(readfile(b.charge_now, "0")) / (double)stoll(readfile(b.current_now, "1")) * 3600); + } + catch (const std::invalid_argument&) { } + catch (const std::out_of_range&) { } + } + } + + if (seconds < 0 and fs::exists(b.base_dir / "time_to_empty")) { try { - seconds = round((double)stoll(readfile(b.energy_now, "0")) / stoll(readfile(b.power_now, "1")) * 3600); + seconds = stoll(readfile(b.base_dir / "time_to_empty", "0")) * 60; } catch (const std::invalid_argument&) { } catch (const std::out_of_range&) { } } - if (seconds < 0 and fs::exists(b.base_dir / "time_to_empty")) { + } + + //? Get power draw + if (b.use_power) { + if (not b.power_now.empty()) { try { - seconds = stoll(readfile(b.base_dir / "time_to_empty", "0")) * 60; + watts = (float)stoll(readfile(b.energy_now, "-1")) / 1000000.0; } catch (const std::invalid_argument&) { } catch (const std::out_of_range&) { } } + else if (not b.voltage_now.empty() and not b.current_now.empty()) { + try { + watts = (float)stoll(readfile(b.current_now, "-1")) / 1000000.0 * stoll(readfile(b.voltage_now, "1")) / 1000000.0; + } + catch (const std::invalid_argument&) { } + catch (const std::out_of_range&) { } + } + } - return {percent, seconds, status}; + return {percent, watts, seconds, status}; } auto collect(bool no_update) -> cpu_info& { diff --git a/src/openbsd/btop_collect.cpp b/src/openbsd/btop_collect.cpp index b0d36a5b..bdcb1056 100644 --- a/src/openbsd/btop_collect.cpp +++ b/src/openbsd/btop_collect.cpp @@ -190,7 +190,7 @@ namespace Cpu { string cpuName; string cpuHz; bool has_battery = true; - tuple current_bat; + tuple current_bat; const array time_names = {"user", "nice", "system", "idle"}; @@ -373,8 +373,8 @@ namespace Cpu { return core_map; } - auto get_battery() -> tuple { - if (not has_battery) return {0, 0, ""}; + auto get_battery() -> tuple { + if (not has_battery) return {0, 0, 0, ""}; long seconds = -1; uint32_t percent = -1; @@ -405,7 +405,7 @@ namespace Cpu { } } - return {percent, seconds, status}; + return {percent, -1, seconds, status}; } auto collect(bool no_update) -> cpu_info & { diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp index 860e457e..df1fca33 100644 --- a/src/osx/btop_collect.cpp +++ b/src/osx/btop_collect.cpp @@ -191,7 +191,7 @@ namespace Cpu { string cpuHz; bool has_battery = true; bool macM1 = false; - tuple current_bat; + tuple current_bat; const array time_names = {"user", "nice", "system", "idle"}; @@ -407,8 +407,8 @@ namespace Cpu { ~IOPSList_Wrap() { CFRelease(data); } }; - auto get_battery() -> tuple { - if (not has_battery) return {0, 0, ""}; + auto get_battery() -> tuple { + if (not has_battery) return {0, 0, 0, ""}; uint32_t percent = -1; long seconds = -1; @@ -447,7 +447,7 @@ namespace Cpu { has_battery = false; } } - return {percent, seconds, status}; + return {percent, -1, seconds, status}; } auto collect(bool no_update) -> cpu_info & { @@ -1212,10 +1212,14 @@ namespace Proc { //? Get program name, command, username, parent pid, nice and status if (no_cache) { char fullname[PROC_PIDPATHINFO_MAXSIZE]; - proc_pidpath(pid, fullname, sizeof(fullname)); - const string f_name = std::string(fullname); - size_t lastSlash = f_name.find_last_of('/'); - new_proc.name = f_name.substr(lastSlash + 1); + int rc = proc_pidpath(pid, fullname, sizeof(fullname)); + string f_name = ""; + if (rc != 0) { + f_name = std::string(fullname); + size_t lastSlash = f_name.find_last_of('/'); + f_name = f_name.substr(lastSlash + 1); + } + new_proc.name = f_name; //? Get process arguments if possible, fallback to process path in case of failure if (Shared::arg_max > 0) { std::unique_ptr proc_chars(new char[Shared::arg_max]); diff --git a/themes/everforest-dark-medium.theme b/themes/everforest-dark-medium.theme new file mode 100644 index 00000000..f6aeadfe --- /dev/null +++ b/themes/everforest-dark-medium.theme @@ -0,0 +1,92 @@ +# All graphs and meters can be gradients +# For single color graphs leave "mid" and "end" variable empty. +# Use "start" and "end" variables for two color gradient +# Use "start", "mid" and "end" for three color gradient + +# Main background, empty for terminal default, need to be empty if you want transparent background +theme[main_bg]="#2d353b" + +# Main text color +theme[main_fg]="#d3c6aa" + +# Title color for boxes +theme[title]="#d3c6aa" + +# Highlight color for keyboard shortcuts +theme[hi_fg]="#e67e80" + +# Background color of selected items +theme[selected_bg]="#3d484d" + +# Foreground color of selected items +theme[selected_fg]="#dbbc7f" + +# Color of inactive/disabled text +theme[inactive_fg]="#2d353b" + +# Color of text appearing on top of graphs, i.e uptime and current network graph scaling +theme[graph_text]="#d3c6aa" + +# Misc colors for processes box including mini cpu graphs, details memory graph and details status text +theme[proc_misc]="#a7c080" + +# Cpu box outline color +theme[cpu_box]="#3d484d" + +# Memory/disks box outline color +theme[mem_box]="#3d484d" + +# Net up/down box outline color +theme[net_box]="#3d484d" + +# Processes box outline color +theme[proc_box]="#3d484d" + +# Box divider line and small boxes line color +theme[div_line]="#3d484d" + +# Temperature graph colors +theme[temp_start]="#a7c080" +theme[temp_mid]="#dbbc7f" +theme[temp_end]="#f85552" + +# CPU graph colors +theme[cpu_start]="#a7c080" +theme[cpu_mid]="#dbbc7f" +theme[cpu_end]="#f85552" + +# Mem/Disk free meter +theme[free_start]="#f85552" +theme[free_mid]="#dbbc7f" +theme[free_end]="#a7c080" + +# Mem/Disk cached meter +theme[cached_start]="#7fbbb3" +theme[cached_mid]="#83c092" +theme[cached_end]="#a7c080" + +# Mem/Disk available meter +theme[available_start]="#f85552" +theme[available_mid]="#dbbc7f" +theme[available_end]="#a7c080" + +# Mem/Disk used meter +theme[used_start]="#a7c080" +theme[used_mid]="#dbbc7f" +theme[used_end]="#f85552" + +# Download graph colors +theme[download_start]="#a7c080" +theme[download_mid]="#83c092" +theme[download_end]="#7fbbb3" + +# Upload graph colors +theme[upload_start]="#dbbc7f" +theme[upload_mid]="#e69875" +theme[upload_end]="#e67e80" + +# Process box color gradient for threads, mem and cpu usage +theme[process_start]="#a7c080" +theme[process_mid]="#e67e80" +theme[process_end]="#f85552" +