diff options
author | vsey <54716634+vsey@users.noreply.github.com> | 2024-01-03 20:52:30 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-03 20:52:30 +0100 |
commit | b28c5ffc408672e6c7b64eab456931a66a47fcdb (patch) | |
tree | 248745bbb407dd8949fc883333a4d9bba3972e5d | |
parent | fb6af5ddb41289da56bd37dd7a557eec0c664d51 (diff) | |
parent | 114a98d813f618ab8ef594eb0de90a2a13dd3a0c (diff) |
Merge branch 'main' into battery-power-2
-rw-r--r-- | .github/workflows/continuous-build-freebsd.yml | 2 | ||||
-rw-r--r-- | .github/workflows/continuous-build-linux.yml | 2 | ||||
-rw-r--r-- | .github/workflows/continuous-build-macos.yml | 8 | ||||
-rw-r--r-- | .github/workflows/continuous-build-openbsd.yml | 58 | ||||
-rw-r--r-- | Makefile | 35 | ||||
-rw-r--r-- | README.md | 97 | ||||
-rw-r--r-- | snap/snapcraft.yaml | 1 | ||||
-rw-r--r-- | src/btop.cpp | 90 | ||||
-rw-r--r-- | src/btop_config.cpp | 71 | ||||
-rw-r--r-- | src/btop_config.hpp | 9 | ||||
-rw-r--r-- | src/btop_input.cpp | 104 | ||||
-rw-r--r-- | src/btop_input.hpp | 15 | ||||
-rw-r--r-- | src/btop_tools.cpp | 44 | ||||
-rw-r--r-- | src/openbsd/btop_collect.cpp | 1295 | ||||
-rw-r--r-- | src/openbsd/internal.h | 157 | ||||
-rw-r--r-- | src/openbsd/sysctlbyname.cpp | 46 | ||||
-rw-r--r-- | src/openbsd/sysctlbyname.h | 20 |
17 files changed, 1895 insertions, 159 deletions
diff --git a/.github/workflows/continuous-build-freebsd.yml b/.github/workflows/continuous-build-freebsd.yml index 041133f..d06190f 100644 --- a/.github/workflows/continuous-build-freebsd.yml +++ b/.github/workflows/continuous-build-freebsd.yml @@ -11,6 +11,7 @@ on: - 'src/**' - '!src/linux/**' - '!src/osx/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-freebsd.yml' @@ -21,6 +22,7 @@ on: - 'src/**' - '!src/linux/**' - '!src/osx/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-freebsd.yml' diff --git a/.github/workflows/continuous-build-linux.yml b/.github/workflows/continuous-build-linux.yml index 3ef236c..39de640 100644 --- a/.github/workflows/continuous-build-linux.yml +++ b/.github/workflows/continuous-build-linux.yml @@ -11,6 +11,7 @@ on: - 'src/**' - '!src/osx/**' - '!src/freebsd/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-linux.yml' @@ -21,6 +22,7 @@ on: - 'src/**' - '!src/osx/**' - '!src/freebsd/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-linux.yml' diff --git a/.github/workflows/continuous-build-macos.yml b/.github/workflows/continuous-build-macos.yml index 717e9a7..c8915dd 100644 --- a/.github/workflows/continuous-build-macos.yml +++ b/.github/workflows/continuous-build-macos.yml @@ -11,6 +11,7 @@ on: - 'src/**' - '!src/linux/**' - '!src/freebsd/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-macos.yml' @@ -21,6 +22,7 @@ on: - 'src/**' - '!src/linux/**' - '!src/freebsd/**' + - '!src/openbsd/**' - 'include/**' - 'Makefile' - '.github/workflows/continuous-build-macos.yml' @@ -44,18 +46,18 @@ jobs: with: name: btop-x86_64-macos11-BigSur path: 'bin/*' - + build-macos12: runs-on: macos-12 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: latest-stable - uses: actions/checkout@v3 with: submodules: recursive - + - name: Compile run: | make CXX=g++-12 ARCH=x86_64 STATIC=true STRIP=true diff --git a/.github/workflows/continuous-build-openbsd.yml b/.github/workflows/continuous-build-openbsd.yml new file mode 100644 index 0000000..afb1a93 --- /dev/null +++ b/.github/workflows/continuous-build-openbsd.yml @@ -0,0 +1,58 @@ +name: Continuous Build OpenBSD + +on: + workflow_dispatch: + push: + branches: + - main + tags-ignore: + - '*.*' + paths: + - 'src/**' + - '!src/linux/**' + - '!src/osx/**' + - '!src/freebsd/**' + - 'include/**' + - 'Makefile' + - '.github/workflows/continuous-build-openbsd.yml' + pull_request: + branches: + - main + paths: + - 'src/**' + - '!src/linux/**' + - '!src/osx/**' + - '!src/freebsd/**' + - 'include/**' + - 'Makefile' + - '.github/workflows/continuous-build-openbsd.yml' + +jobs: + build-openbsd: + runs-on: ubuntu-22.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Compile + uses: vmactions/openbsd-vm@v1 + with: + release: '7.4' + usesh: true + prepare: | + pkg_add gmake gcc%11 g++%11 coreutils git + git config --global --add safe.directory /home/runner/work/btop/btop + run: | + gmake CXX=eg++ STATIC=true STRIP=true + GIT_HASH=$(git rev-parse --short "$GITHUB_SHA") + mv bin/btop bin/btop-GCC11-"$GIT_HASH" + ls -alh bin + + - uses: actions/upload-artifact@v3 + with: + name: btop-x86_64-openbsd-7.4 + path: 'bin/*' + if-no-files-found: error + @@ -73,32 +73,9 @@ ifeq ($(CXX_IS_CLANG),true) ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt $(MIN_CLANG_VERSION); echo $$?),0) CLANG_WORKS := true endif -endif -ifeq ($(CLANG_WORKS),false) - #? Try to find a newer GCC version - ifeq ($(shell command -v g++-13 >/dev/null; echo $$?),0) - CXX := g++-13 - else ifeq ($(shell command -v g++13 >/dev/null; echo $$?),0) - CXX := g++13 - else ifeq ($(shell command -v g++-12 >/dev/null; echo $$?),0) - CXX := g++-12 - else ifeq ($(shell command -v g++12 >/dev/null; echo $$?),0) - CXX := g++12 - else ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0) - CXX := g++-11 - else ifeq ($(shell command -v g++11 >/dev/null; echo $$?),0) - CXX := g++11 - else ifeq ($(shell command -v g++ >/dev/null; echo $$?),0) - CXX := g++ - else - GCC_NOT_FOUND := true - endif - ifndef GCC_NOT_FOUND - override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0) - override CXX_VERSION_MAJOR := $(shell echo $(CXX_VERSION) | cut -d '.' -f 1) - ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 10; echo $$?),0) - GCC_WORKS := true - endif +else + ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 10; echo $$?),0) + GCC_WORKS := true endif endif @@ -158,6 +135,12 @@ else ifeq ($(PLATFORM_LC),macos) THREADS := $(shell sysctl -n hw.ncpu || echo 1) override ADDFLAGS += -framework IOKit -framework CoreFoundation -Wno-format-truncation SU_GROUP := wheel +else ifeq ($(PLATFORM_LC),openbsd) + PLATFORM_DIR := openbsd + THREADS := $(shell sysctl -n hw.ncpu || echo 1) + override ADDFLAGS += -lkvm + export MAKE = gmake + SU_GROUP := wheel else $(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m")) endif @@ -7,6 +7,7 @@ ![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux) ![macOS](https://img.shields.io/badge/-OSX-black?logo=apple) ![FreeBSD](https://img.shields.io/badge/-FreeBSD-red?logo=freebsd) +![OpenBSD](https://img.shields.io/badge/-OpenBSD-black?logo=openbsd) ![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow) ![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green) ![latest_release](https://img.shields.io/github/v/tag/aristocratos/btop?label=release) @@ -17,6 +18,7 @@ [![Continuous Build Linux](https://github.com/aristocratos/btop/actions/workflows/continuous-build-linux.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-linux.yml) [![Continuous Build macOS](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml) [![Continuous Build FreeBSD](https://github.com/aristocratos/btop/actions/workflows/continuous-build-freebsd.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-freebsd.yml) +[![Continuous Build OpenBSD](https://github.com/aristocratos/btop/actions/workflows/continuous-build-openbsd.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-openbsd.yml) ## Index @@ -33,6 +35,7 @@ * [Compilation Linux](#compilation-linux) * [Compilation macOS](#compilation-macos-osx) * [Compilation FreeBSD](#compilation-freebsd) +* [Compilation OpenBSD](#compilation-openbsd) * [GPU compatibility](#gpu-compatibility) * [Installing the snap](#installing-the-snap) * [Configurability](#configurability) @@ -868,6 +871,100 @@ Also needs a UTF8 locale and a font that covers: </details> +## Compilation OpenBSD + + Requires at least GCC 10. + + Note that GNU make (`gmake`) is required to compile on OpenBSD. + +<details> +<summary> + +### With gmake +</summary> + +1. **Install dependencies** + + ```bash + pkg_add gmake gcc%11 g++%11 coreutils git + ``` + +2. **Clone repository** + + ```bash + git clone https://github.com/aristocratos/btop.git + cd btop + ``` + +3. **Compile** + + ```bash + gmake CXX=eg++ + ``` + + Options for make: + + | Flag | Description | + |---------------------------------|-------------------------------------------------------------------------| + | `VERBOSE=true` | To display full compiler/linker commands | + | `STATIC=true` | For static compilation (only libgcc and libstdc++) | + | `QUIET=true` | For less verbose output | + | `STRIP=true` | To force stripping of debug symbols (adds `-s` linker flag) | + | `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging | + | `ARCH=<architecture>` | To manually set the target architecture | + | `ADDFLAGS=<flags>` | For appending flags to both compiler and linker | + | `CXX=<compiler>` | Manualy set which compiler to use | + + Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system. + +4. **Install** + + ```bash + sudo gmake install + ``` + + Append `PREFIX=/target/dir` to set target, default: `/usr/local` + + Notice! Only use "sudo" when installing to a NON user owned directory. + +5. **(Recommended) Set suid bit to make btop always run as root (or other user)** + + ```bash + sudo gmake setuid + ``` + + No need for `sudo` to see information for non user owned processes and to enable signal sending to any process. + + Run after make install and use same PREFIX if any was used at install. + + Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel` + +* **Uninstall** + + ```bash + sudo gmake uninstall + ``` + +* **Remove any object files from source dir** + + ```bash + gmake clean + ``` + +* **Remove all object files, binaries and created directories in source dir** + + ```bash + gmake distclean + ``` + +* **Show help** + + ```bash + gmake help + ``` + +</details> + ## Installing the snap [![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f6b2729..2e76ba5 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -45,6 +45,7 @@ parts: source-type: git plugin: make make-parameters: + - CXX=g++-11 - PREFIX=/usr/local - STATIC=true - ADDFLAGS="-D SNAPPED" diff --git a/src/btop.cpp b/src/btop.cpp index 8a0eb05..9ad5012 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -16,6 +16,7 @@ indent = tab tab-size = 4 */ +#include <algorithm> #include <csignal> #include <clocale> #include <pthread.h> @@ -51,6 +52,7 @@ tab-size = 4 #include "btop_theme.hpp" #include "btop_draw.hpp" #include "btop_menu.hpp" +#include "fmt/core.h" using std::atomic; using std::cout; @@ -109,15 +111,16 @@ namespace Global { bool arg_tty{}; // defaults to false bool arg_low_color{}; // defaults to false int arg_preset = -1; + int arg_update = 0; } //* A simple argument parser -void argumentParser(const int& argc, char **argv) { +void argumentParser(const int argc, char **argv) { for(int i = 1; i < argc; i++) { const string argument = argv[i]; if (is_in(argument, "-h", "--help")) { fmt::println( - "usage: btop [-h] [-v] [-/+t] [-p <id>] [--utf-force] [--debug]\n\n" + "usage: btop [-h] [-v] [-/+t] [-p <id>] [-u <ms>] [--utf-force] [--debug]\n\n" "optional arguments:\n" " -h, --help show this help message and exit\n" " -v, --version show version info and exit\n" @@ -125,6 +128,7 @@ void argumentParser(const int& argc, char **argv) { " -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n" " +t, --tty_off force (OFF) tty mode\n" " -p, --preset <id> start with preset, integer value between 0-9\n" + " -u, --update <ms> set the program update rate in milliseconds\n" " --utf-force force start even if no UTF-8 locale was detected\n" " --debug start in DEBUG mode: shows microsecond timer for information collect\n" " and screen draw functions and sets loglevel to DEBUG" @@ -159,6 +163,19 @@ void argumentParser(const int& argc, char **argv) { exit(1); } } + else if (is_in(argument, "-u", "--update")) { + if (++i >= argc) { + fmt::println("ERROR: Update option needs an argument"); + exit(1); + } + const std::string value = argv[i]; + if (isint(value)) { + Global::arg_update = std::clamp(std::stoi(value), 100, Config::ONE_DAY_MILLIS); + } else { + fmt::println("ERROR: Invalid update rate"); + exit(1); + } + } else if (argument == "--utf-force") Global::utf_force = true; else if (argument == "--debug") @@ -176,7 +193,7 @@ void term_resize(bool force) { static atomic<bool> resizing (false); if (Input::polling) { Global::resized = true; - Input::interrupt = true; + Input::interrupt(); return; } atomic_lock lck(resizing, true); @@ -246,7 +263,7 @@ void term_resize(bool force) { else if (not Term::refresh()) break; } - Input::interrupt = true; + Input::interrupt(); } //* Exit handler; stops threads, restores terminal and saves config changes @@ -255,7 +272,7 @@ void clean_quit(int sig) { Global::quitting = true; Runner::stop(); if (Global::_runner_started) { - #ifdef __APPLE__ + #if defined __APPLE__ || defined __OpenBSD__ if (pthread_join(Runner::runner_id, nullptr) != 0) { Logger::warning("Failed to join _runner thread on exit!"); pthread_cancel(Runner::runner_id); @@ -291,7 +308,7 @@ void clean_quit(int sig) { const auto excode = (sig != -1 ? sig : 0); -#ifdef __APPLE__ +#if defined __APPLE__ || defined __OpenBSD__ _Exit(excode); #else quick_exit(excode); @@ -321,7 +338,7 @@ void _signal_handler(const int sig) { if (Runner::active) { Global::should_quit = true; Runner::stopping = true; - Input::interrupt = true; + Input::interrupt(); } else { clean_quit(0); @@ -331,7 +348,7 @@ void _signal_handler(const int sig) { if (Runner::active) { Global::should_sleep = true; Runner::stopping = true; - Input::interrupt = true; + Input::interrupt(); } else { _sleep(); @@ -343,6 +360,9 @@ void _signal_handler(const int sig) { case SIGWINCH: term_resize(); break; + case SIGUSR1: + // Input::poll interrupt + break; } } @@ -477,7 +497,7 @@ namespace Runner { if (pt_lck.status != 0) { Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status); Global::thread_exception = true; - Input::interrupt = true; + Input::interrupt(); stopping = true; } @@ -488,7 +508,7 @@ namespace Runner { if (active) { Global::exit_error_msg = "Runner thread failed to get active lock!"; Global::thread_exception = true; - Input::interrupt = true; + Input::interrupt(); stopping = true; } if (stopping or Global::resized) { @@ -558,7 +578,7 @@ namespace Runner { coreNum_reset = false; Cpu::core_mapping = Cpu::get_core_mapping(); Global::resized = true; - Input::interrupt = true; + Input::interrupt(); continue; } @@ -655,7 +675,7 @@ namespace Runner { catch (const std::exception& e) { Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()}; Global::thread_exception = true; - Input::interrupt = true; + Input::interrupt(); stopping = true; } @@ -829,29 +849,23 @@ int main(int argc, char **argv) { //? Call argument parser if launched with arguments if (argc > 1) argumentParser(argc, argv); - //? Setup paths for config, log and user themes - for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) { - if (std::getenv(env) != nullptr and access(std::getenv(env), W_OK) != -1) { - Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop"); - break; - } - } - if (Config::conf_dir.empty()) { - fmt::println("WARNING: Could not get path user HOME folder.\n" - "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this."); - } - else { - if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) { - fmt::println("WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n" - "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this."); - } - else { + { + const auto config_dir = Config::get_config_dir(); + if (config_dir.has_value()) { + Config::conf_dir = config_dir.value(); Config::conf_file = Config::conf_dir / "btop.conf"; Logger::logfile = Config::conf_dir / "btop.log"; Theme::user_theme_dir = Config::conf_dir / "themes"; - if (not fs::exists(Theme::user_theme_dir) and not fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear(); + + // If necessary create the user theme directory + std::error_code error; + if (not fs::exists(Theme::user_theme_dir, error) and not fs::create_directories(Theme::user_theme_dir, error)) { + Theme::user_theme_dir.clear(); + Logger::warning("Failed to create user theme directory: " + error.message()); + } } } + //? Try to find global btop theme path relative to binary path #ifdef __linux__ { std::error_code ec; @@ -931,7 +945,7 @@ int main(int argc, char **argv) { catch (...) { found.clear(); } } } - + // #ifdef __APPLE__ if (found.empty()) { CFLocaleRef cflocale = CFLocaleCopyCurrent(); @@ -975,7 +989,7 @@ int main(int argc, char **argv) { Config::set("tty_mode", true); Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols"); } -#ifndef __APPLE__ +#if not defined __APPLE__ && not defined __OpenBSD__ else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) { Config::set("tty_mode", true); Logger::info("Real tty detected: setting 16 color mode and using tty friendly graph symbols"); @@ -1014,6 +1028,12 @@ int main(int argc, char **argv) { std::signal(SIGTSTP, _signal_handler); std::signal(SIGCONT, _signal_handler); std::signal(SIGWINCH, _signal_handler); + std::signal(SIGUSR1, _signal_handler); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &mask, &Input::signal_mask); //? Start runner thread Runner::thread_sem_init(); @@ -1035,9 +1055,10 @@ int main(int argc, char **argv) { { const auto [x, y] = Term::get_min_size(Config::getS("shown_boxes")); if (Term::height < y or Term::width < x) { + pthread_sigmask(SIG_SETMASK, &Input::signal_mask, &mask); term_resize(true); + pthread_sigmask(SIG_SETMASK, &mask, nullptr); Global::resized = false; - Input::interrupt = false; } } @@ -1050,6 +1071,9 @@ int main(int argc, char **argv) { //? ------------------------------------------------ MAIN LOOP ---------------------------------------------------- + if (Global::arg_update != 0) { + Config::set("update_ms", Global::arg_update); + } uint64_t update_ms = Config::getI("update_ms"); auto future_time = time_ms(); diff --git a/src/btop_config.cpp b/src/btop_config.cpp index fd28f67..750cc07 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -24,6 +24,7 @@ tab-size = 4 #include <utility> #include <fmt/core.h> +#include <sys/statvfs.h> #include "btop_config.hpp" #include "btop_shared.hpp" @@ -323,6 +324,65 @@ namespace Config { }; std::unordered_map<std::string_view, int> intsTmp; + // Returns a valid config dir or an empty optional + // The config dir might be read only, a warning is printed, but a path is returned anyway + [[nodiscard]] std::optional<fs::path> get_config_dir() noexcept { + fs::path config_dir; + { + std::error_code error; + if (const auto xdg_config_home = std::getenv("XDG_CONFIG_HOME"); xdg_config_home != nullptr) { + if (fs::exists(xdg_config_home, error)) { + config_dir = fs::path(xdg_config_home) / "btop"; + } + } else if (const auto home = std::getenv("HOME"); home != nullptr) { + error.clear(); + if (fs::exists(home, error)) { + config_dir = fs::path(home) / ".config" / "btop"; + } + if (error) { + fmt::print(stderr, "\033[0;31mWarning: \033[0m{} could not be accessed: {}\n", config_dir.string(), error.message()); + config_dir = ""; + } + } + } + + // FIXME: This warnings can be noisy if the user deliberately has a non-writable config dir + // offer an alternative | disable messages by default | disable messages if config dir is not writable | disable messages with a flag + // FIXME: Make happy path not branch + if (not config_dir.empty()) { + std::error_code error; + if (fs::exists(config_dir, error)) { + if (fs::is_directory(config_dir, error)) { + struct statvfs stats {}; + if ((fs::status(config_dir, error).permissions() & fs::perms::owner_write) == fs::perms::owner_write and + statvfs(config_dir.c_str(), &stats) == 0 and (stats.f_flag & ST_RDONLY) == 0) { + return config_dir; + } else { + fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not writable\n", fs::absolute(config_dir).string()); + // If the config is readable we can still use the provided config, but changes will not be persistent + if ((fs::status(config_dir, error).permissions() & fs::perms::owner_read) == fs::perms::owner_read) { + fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n"); + return config_dir; + } + } + } else { + fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not a directory\n", fs::absolute(config_dir).string()); + } + } else { + // Doesn't exist + if (fs::create_directories(config_dir, error)) { + return config_dir; + } else { + fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` could not be created: {}\n", fs::absolute(config_dir).string(), error.message()); + } + } + } else { + fmt::print(stderr, "\033[0;31mWarning: \033[0mCould not determine config path: Make sure `$XDG_CONFIG_HOME` or `$HOME` is set\n"); + } + fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n"); + return {}; + } + bool _locked(const std::string_view name) { atomic_wait(writelock, true); if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end()) @@ -431,8 +491,8 @@ namespace Config { if (name == "update_ms" and i_value < 100) validError = "Config value update_ms set too low (<100)."; - else if (name == "update_ms" and i_value > 86400000) - validError = "Config value update_ms set too high (>86400000)."; + else if (name == "update_ms" and i_value > ONE_DAY_MILLIS) + validError = fmt::format("Config value update_ms set too high (>{}).", ONE_DAY_MILLIS); else return true; @@ -597,12 +657,17 @@ namespace Config { } void load(const fs::path& conf_file, vector<string>& load_warnings) { + std::error_code error; if (conf_file.empty()) return; - else if (not fs::exists(conf_file)) { + else if (not fs::exists(conf_file, error)) { write_new = true; return; } + if (error) { + return; + } + std::ifstream cread(conf_file); if (cread.good()) { vector<string> valid_names; diff --git a/src/btop_config.hpp b/src/btop_config.hpp index 2b586af..3651566 100644 --- a/src/btop_config.hpp +++ b/src/btop_config.hpp @@ -18,9 +18,10 @@ tab-size = 4 #pragma once +#include <filesystem> +#include <optional> #include <string> #include <vector> -#include <filesystem> #include <unordered_map> @@ -57,6 +58,10 @@ namespace Config { extern vector<string> available_batteries; extern int current_preset; + constexpr int ONE_DAY_MILLIS = 1000 * 60 * 60 * 24; + + [[nodiscard]] std::optional<std::filesystem::path> get_config_dir() noexcept; + //* Check if string only contains space separated valid names for boxes bool check_boxes(const string& boxes); @@ -94,7 +99,7 @@ namespace Config { } //* Set config key <name> to int <value> - inline void set(const std::string_view name, const int& value) { + inline void set(const std::string_view name, const int value) { if (_locked(name)) intsTmp.insert_or_assign(name, value); else ints.at(name) = value; } diff --git a/src/btop_input.cpp b/src/btop_input.cpp index fb9e46d..fbbae58 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -16,12 +16,13 @@ indent = tab tab-size = 4 */ -#include <iostream> +#include <limits> #include <ranges> #include <vector> #include <thread> #include <mutex> #include <signal.h> +#include <sys/select.h> #include <utility> #include "btop_input.hpp" @@ -31,17 +32,6 @@ tab-size = 4 #include "btop_menu.hpp" #include "btop_draw.hpp" - -#include "btop_input.hpp" -#include "btop_tools.hpp" -#include "btop_config.hpp" -#include "btop_shared.hpp" -#include "btop_menu.hpp" -#include "btop_draw.hpp" - - -using std::cin; - using namespace Tools; using namespace std::literals; // for operator""s namespace rng = std::ranges; @@ -89,83 +79,45 @@ namespace Input { {"[24~", "f12"} }; - std::atomic<bool> interrupt (false); + sigset_t signal_mask; std::atomic<bool> polling (false); array<int, 2> mouse_pos; std::unordered_map<string, Mouse_loc> mouse_mappings; deque<string> history(50, ""); string old_filter; + string input; - struct InputThr { - InputThr() : thr(run, this) { - } - - static void run(InputThr* that) { - that->runImpl(); - } - - void runImpl() { - char ch = 0; - - // TODO(pg83): read whole buffer - while (cin.get(ch)) { - std::lock_guard<std::mutex> g(lock); - current.push_back(ch); - if (current.size() > 100) { - current.clear(); - } - } - } - - size_t avail() { - std::lock_guard<std::mutex> g(lock); - - return current.size(); + bool poll(const uint64_t timeout) { + atomic_lock lck(polling); + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + struct timespec wait; + struct timespec *waitptr = nullptr; + + if(timeout != std::numeric_limits<uint64_t>::max()) { + wait.tv_sec = timeout / 1000; + wait.tv_nsec = (timeout % 1000) * 1000000; + waitptr = &wait; } - std::string get() { - std::string res; - - { - std::lock_guard<std::mutex> g(lock); - - res.swap(current); + if(pselect(STDIN_FILENO + 1, &fds, nullptr, nullptr, waitptr, &signal_mask) > 0) { + input.clear(); + char buf[1024]; + ssize_t count = 0; + while((count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + input.append(std::string_view(buf, count)); } - return res; - } - - static InputThr& instance() { - // intentional memory leak, to simplify shutdown process - static InputThr* input = new InputThr(); - - return *input; + return true; } - std::string current; - // TODO(pg83): use std::conditional_variable instead of sleep - std::mutex lock; - std::thread thr; - }; - - bool poll(int timeout) { - atomic_lock lck(polling); - if (timeout < 1) return InputThr::instance().avail() > 0; - while (timeout > 0) { - if (interrupt) { - interrupt = false; - return false; - } - if (InputThr::instance().avail() > 0) return true; - sleep_ms(timeout < 10 ? timeout : 10); - timeout -= 10; - } return false; } string get() { - string key = InputThr::instance().get(); + string key = input; if (not key.empty()) { //? Remove escape code prefix if present if (key.substr(0, 2) == Fx::e) { @@ -238,12 +190,14 @@ namespace Input { } string wait() { - while (InputThr::instance().avail() < 1) { - sleep_ms(10); - } + while(not poll(std::numeric_limits<uint64_t>::max())) {} return get(); } + void interrupt() { + kill(getpid(), SIGUSR1); + } + void clear() { // do not need it, actually } diff --git a/src/btop_input.hpp b/src/btop_input.hpp index fc6fda0..13f14fe 100644 --- a/src/btop_input.hpp +++ b/src/btop_input.hpp @@ -29,9 +29,9 @@ using std::atomic; using std::deque; using std::string; -/* The input functions relies on the following std::cin options being set: - cin.sync_with_stdio(false); - cin.tie(nullptr); +/* The input functions rely on the following termios parameters being set: + Non-canonical mode (c_lflags & ~(ICANON)) + VMIN and VTIME (c_cc) set to 0 These will automatically be set when running Term::init() from btop_tools.cpp */ @@ -45,7 +45,9 @@ namespace Input { //? line, col, height, width extern std::unordered_map<string, Mouse_loc> mouse_mappings; - extern atomic<bool> interrupt; + //* Signal mask used during polling read + extern sigset_t signal_mask; + extern atomic<bool> polling; //* Mouse column and line position @@ -55,7 +57,7 @@ namespace Input { extern deque<string> history; //* Poll keyboard & mouse input for <timeout> ms and return input availabilty as a bool - bool poll(int timeout=0); + bool poll(const uint64_t timeout=0); //* Get a key or mouse action from input string get(); @@ -63,6 +65,9 @@ namespace I |