summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvsey <54716634+vsey@users.noreply.github.com>2024-01-03 20:52:30 +0100
committerGitHub <noreply@github.com>2024-01-03 20:52:30 +0100
commitb28c5ffc408672e6c7b64eab456931a66a47fcdb (patch)
tree248745bbb407dd8949fc883333a4d9bba3972e5d
parentfb6af5ddb41289da56bd37dd7a557eec0c664d51 (diff)
parent114a98d813f618ab8ef594eb0de90a2a13dd3a0c (diff)
Merge branch 'main' into battery-power-2
-rw-r--r--.github/workflows/continuous-build-freebsd.yml2
-rw-r--r--.github/workflows/continuous-build-linux.yml2
-rw-r--r--.github/workflows/continuous-build-macos.yml8
-rw-r--r--.github/workflows/continuous-build-openbsd.yml58
-rw-r--r--Makefile35
-rw-r--r--README.md97
-rw-r--r--snap/snapcraft.yaml1
-rw-r--r--src/btop.cpp90
-rw-r--r--src/btop_config.cpp71
-rw-r--r--src/btop_config.hpp9
-rw-r--r--src/btop_input.cpp104
-rw-r--r--src/btop_input.hpp15
-rw-r--r--src/btop_tools.cpp44
-rw-r--r--src/openbsd/btop_collect.cpp1295
-rw-r--r--src/openbsd/internal.h157
-rw-r--r--src/openbsd/sysctlbyname.cpp46
-rw-r--r--src/openbsd/sysctlbyname.h20
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
+
diff --git a/Makefile b/Makefile
index 2b02f3d..36e2ea1 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index f220e86..fa67b70 100644
--- a/README.md
+++ b/README.md
@@ -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