Merge branch 'aristocratos:main' into main

This commit is contained in:
kz6fittycent 2024-03-13 11:02:55 -05:00 committed by GitHub
commit 8af8389bd0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 3179 additions and 3296 deletions

View file

@ -28,10 +28,9 @@ Any bug that can be solved by just reading the [prerequisites](https://github.co
[If applicable, add screenshots to help explain your problem.]
**Info (please complete the following information):**
- btop++ version: `btop -v`
- btop++ version: `btop --version`
- If using snap: `snap info btop`
- Binary: [self compiled or static binary from release]
- (If compiled) Compiler and version:
- Architecture: [x86_64, aarch64, etc.] `uname -m`
- Platform: [Linux, FreeBSD, OsX]
- (Linux) Kernel: `uname -r`

View file

@ -33,8 +33,7 @@ jobs:
with:
release: '14.0'
usesh: true
prepare: pkg install -y cmake ninja
prepare: pkg install -y cmake ninja lowdown
run: |
CXX=clang++ cmake -B build -G Ninja -DBTOP_STATIC=ON
cmake --build build --verbose

View file

@ -30,11 +30,10 @@ jobs:
- uses: actions/checkout@v4
- name: Install build tools
run: apk add --no-cache --update clang cmake lld ninja
run: apk add --no-cache --update clang cmake lld ninja lowdown
- name: Configure
run: CXX=clang++ LDFLAGS=-fuse-ld=lld cmake -B build -G Ninja -DBTOP_STATIC=ON
- name: Compile
run: cmake --build build --verbose

View file

@ -32,7 +32,7 @@ jobs:
run: |
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
brew update --quiet
brew install --force --overwrite cmake llvm@17 ninja
brew install --force --overwrite cmake llvm@17 ninja lowdown
- name: Configure
run: |
@ -44,4 +44,3 @@ jobs:
- name: Compile
run: cmake --build build --verbose

View file

@ -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'
@ -43,7 +45,7 @@ jobs:
release: '14.0'
usesh: true
prepare: |
pkg install -y gmake gcc coreutils git
pkg install -y gmake gcc coreutils git lowdown
git config --global --add safe.directory /home/runner/work/btop/btop
run: |
CXX=${{ matrix.compiler }} gmake STATIC=true STRIP=true
@ -57,4 +59,3 @@ jobs:
name: btop-x86_64-freebsd-14
path: 'bin/*'
if-no-files-found: error

View file

@ -0,0 +1,45 @@
name: Continuous Build Gpu
on:
workflow_dispatch:
push:
branches:
- main
tags-ignore:
- '*.*'
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-gpu.yml'
pull_request:
branches:
- main
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-gpu.yml'
jobs:
gpu_build_linux:
runs-on: ubuntu-latest
container: alpine:edge
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Install build tools
run: apk add --no-cache --update gcc g++ make
- name: Compile
run: make CXX=g++ GPU_SUPPORT=true

View file

@ -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'

View file

@ -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

View file

@ -0,0 +1,57 @@
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 lowdown
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

View file

@ -1,18 +1,26 @@
name: 🧪 Test snap can be built on x86_64
on:
workflow_dispatch:
push:
branches: [ main ]
branches: [ main ]
tags-ignore:
- '*.*'
paths:
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-linux.yml'
pull_request:
branches: [ main ]
- '.github/workflows/test-snap-can-build.yml'
pull_request:
branches: [ main ]
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/test-snap-can-build.yml'
jobs:
build:

5
.gitignore vendored
View file

@ -46,9 +46,14 @@ stage/
*.out
*.app
# Compiled man page
btop.1
build
bin
btop
/obj/
config.h
.*/
# Optional libraries

View file

@ -1,6 +1,30 @@
## v1.3.2
Description | Author(s) | References
--- | --- | ---
fix: Can't detect librocm 6.0.x | @imwints, @aristocratos | #761
## v1.3.1
Description | Author(s) | References
--- | --- | ---
GPU: Added support for dynamic loading of ROCm v6 libraries | @aristocratos, @fxzjshm | 5511131, #737
Increase max network interface name to 15 | @tessus | #714
Fix OpenBSD UTF-8 locale detection | @lcheylus, @imwints | #753, #717
Add hot-reloading of config file with CTRL+R or SIGUSR2 signal | @MartinPit | #722
Add battery power draw for linux and freebsd | @vsey | #689
Fix crash caused by string exception when cpu clock is exactly between 999.5 and 999.9 Mhz | @rkmcode | #735
Write newline at end of config file | @planet36 | #743
Add theme based on Everforest Dark Medium palette | @M-Sviridov | #746
fix: don't mangle memory for zombie processes | @joske | #747
Share common code from collect | @imwints | #756
Fixed incorrect used and available memory for OSX | | 4461a43
## v1.3.0
* Added Gpu Support | @romner-set | PR #529
* Added Gpu Support Linux | @romner-set | PR #529
* Added platform support for OpenBSD | @joske | PR #607
* Enable macos clang | @muneebmahmed | PR #666
@ -42,6 +66,16 @@
* Create adwaita.theme | @flipflop133 | #485
* Try get terminal size of "/dev/tty" if stdout fails | @imwints | PR #627
* Refresh rate program argument | @imwints | PR #640
* Improved error handling when determining the config directory | @imwints | #652
* Use native POSIX polling syscalls to read input | @lvxnull | #624
* Conditional compile on Big Sur and up | @joske | PR #690
+ Various fixes by @imwints, @simplepad, @joske, @gwena, @cpalv, @iambeingtracked, @mattico, @NexAdn
## v1.2.13
@ -146,7 +180,7 @@
* Fixed: Wrong memory unit when shorten and size is less than 10, by @mohi001
* Fixed: Use cpu cores avarage temp if missing cpu package temp for FreeBSD
* Fixed: Use cpu cores average temp if missing cpu package temp for FreeBSD
* Changed: Enter symbol to a more common variant
@ -442,7 +476,7 @@
* Fixed: Sizing constraints bug on start and boxes can be toggled from size error screen
* Fixed: UTF-8 check crashing if LANG was set to non existant locale
* Fixed: UTF-8 check crashing if LANG was set to non existent locale
## v1.0.4

View file

@ -11,7 +11,6 @@ if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
endif()
project("btop"
VERSION 1.2.13
DESCRIPTION "A monitor of resources"
HOMEPAGE_URL "https://github.com/aristocratos/btop"
LANGUAGES CXX
@ -41,6 +40,7 @@ option(BTOP_LTO "Enable LTO" ON)
option(BTOP_USE_MOLD "Use mold to link btop" OFF)
option(BTOP_PEDANTIC "Enable a bunch of additional warnings" OFF)
option(BTOP_WERROR "Compile with warnings as errors" OFF)
option(BTOP_FORTIFY "Detect buffer overflows with _FORTIFY_SOURCE=3" ON)
option(BTOP_GPU "Enable GPU support" ON)
cmake_dependent_option(BTOP_RSMI_STATIC "Link statically to ROCm SMI" OFF "BTOP_GPU" OFF)
@ -64,6 +64,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()
@ -75,6 +77,21 @@ if(NOT CXX_HAVE_RANGES)
message(FATAL_ERROR "The compiler doesn't support <ranges>")
endif()
# Generate build info
execute_process(
COMMAND "git" "rev-parse" "--short" "HEAD"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
set(CONFIGURE_COMMAND
"cmake -DBTOP_STATIC=${BTOP_STATIC} -DBTOP_USE_MOLD=${BTOP_USE_MOLD} -DBTOP_FORTIFY=${BTOP_FORTIFY} -DBTOP_GPU=${BTOP_GPU}"
)
get_filename_component(CXX_COMPILER_BASENAME "${CMAKE_CXX_COMPILER}" NAME)
set(COMPILER "${CXX_COMPILER_BASENAME}")
set(COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY IMMEDIATE)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Check for and enable LTO
check_ipo_supported(RESULT ipo_supported)
if(ipo_supported AND BTOP_LTO)
@ -97,7 +114,7 @@ if(BTOP_WERROR)
endif()
if(NOT APPLE)
target_compile_options(btop PRIVATE -fstack-clash-protection)
target_compile_options(btop PRIVATE -fstack-clash-protection)
endif()
check_cxx_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR)
if(HAS_FSTACK_PROTECTOR)
@ -109,10 +126,11 @@ if(HAS_FCF_PROTECTION)
endif()
target_compile_definitions(btop PRIVATE
FMT_HEADER_ONLY
_FILE_OFFSET_BITS=64
$<$<CONFIG:Debug>:_GLIBCXX_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS=1>
# Only has an effect with optimizations enabled
$<$<NOT:$<CONFIG:Debug>>:_FORTIFY_SOURCE=2>
$<$<AND:$<NOT:$<CONFIG:Debug>>,$<BOOL:${BTOP_FORTIFY}>>:_FORTIFY_SOURCE=3>
)
target_include_directories(btop SYSTEM PRIVATE include)
@ -129,7 +147,7 @@ if(LINUX AND BTOP_GPU)
if(BTOP_RSMI_STATIC)
# ROCm doesn't properly add it's folders to the module path if `CMAKE_MODULE_PATH` is already
# set
# We could also manully append ROCm's path here
# We could also manually append ROCm's path here
set(_CMAKE_MODULE_PATH CMAKE_MODULE_PATH)
unset(CMAKE_MODULE_PATH)
@ -178,12 +196,34 @@ 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()
# Check if lowdown is installed
find_program(LOWDOWN_EXECUTABLE lowdown)
if(LOWDOWN_EXECUTABLE)
# Custom target to compile Markdown to man page using lowdown
add_custom_target(btop.1 ALL
COMMAND lowdown -s -Tman -o btop.1 manpage.md
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# Install the man page
install(FILES btop.1 DESTINATION "share/man/man1")
else()
message(WARNING "Command 'lowdown' not found: skipping generating man page btop.1")
endif()
install(TARGETS btop RUNTIME)
@ -191,4 +231,3 @@ install(FILES "btop.desktop" DESTINATION "share/applications")
install(FILES "Img/icon.png" DESTINATION "share/icons/hicolor/48x48/apps" RENAME "btop.png")
install(FILES "Img/icon.svg" DESTINATION "share/icons/hicolor/scalable/apps" RENAME "btop.svg")
install(DIRECTORY "themes" DESTINATION "share/btop")

100
Makefile
View file

@ -50,6 +50,11 @@ ifeq ($(GPU_SUPPORT),true)
override ADDFLAGS += -DGPU_SUPPORT
endif
FORTIFY_SOURCE ?= true
ifeq ($(FORTIFY_SOURCE),true)
override ADDFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
endif
#? Compiler and Linker
ifeq ($(shell $(CXX) --version | grep clang >/dev/null 2>&1; echo $$?),0)
override CXX_IS_CLANG := true
@ -61,6 +66,10 @@ CLANG_WORKS = false
GCC_WORKS = false
MIN_CLANG_VERSION = 16
ifeq ($(DEBUG),true)
override ADDFLAGS += -DBTOP_DEBUG
endif
#? Supported is Clang 16.0.0 and later
ifeq ($(CXX_IS_CLANG),true)
ifeq ($(shell $(CXX) --version | grep Apple >/dev/null 2>&1; echo $$?),0)
@ -69,32 +78,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
@ -154,6 +140,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 -static-libstdc++
export MAKE = gmake
SU_GROUP := wheel
else
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
endif
@ -171,6 +163,12 @@ else
LTO := $(THREADS)
endif
GIT_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null || true)
CONFIGURE_COMMAND := $(MAKE) STATIC=$(STATIC) FORTIFY_SOURCE=$(FORTIFY_SOURCE)
ifeq ($(PLATFORM_LC),linux)
CONFIGURE_COMMAND += GPU_SUPPORT=$(GPU_SUPPORT) RSMI_STATIC=$(RSMI_STATIC)
endif
#? The Directories, Source, Includes, Objects and Binary
SRCDIR := src
INCDIRS := include $(wildcard lib/**/include)
@ -187,10 +185,10 @@ 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_FORTIFY_SOURCE=2 -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)
INC := $(foreach incdir,$(INCDIRS),-isystem $(incdir)) -I$(SRCDIR) -I$(BUILDDIR)
SU_USER := root
ifdef DEBUG
@ -230,7 +228,7 @@ endif
#? Default Make
.ONESHELL:
all: | info rocm_smi info-quiet directories btop
all: | info rocm_smi info-quiet directories btop.1 config.h btop
ifneq ($(QUIET),true)
info:
@ -251,7 +249,6 @@ info:
@true
endif
info-quiet: | info rocm_smi
@printf "\n\033[1;92mBuilding btop++ \033[91m(\033[97mv$(BTOP_VERSION)\033[91m) \033[93m$(PLATFORM) \033[96m$(ARCH)\033[0m\n"
@ -275,17 +272,33 @@ directories:
@$(VERBOSE) || printf "mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)\n"
@mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)
config.h: $(BUILDDIR)/config.h
$(BUILDDIR)/config.h: $(SRCDIR)/config.h.in | directories
@$(QUIET) || printf "\033[1mConfiguring $(BUILDDIR)/config.h\033[0m\n"
@$(VERBOSE) || printf 'sed -e "s|@GIT_COMMIT@|$(GIT_COMMIT)|" -e "s|@CONFIGURE_COMMAND@|$(CONFIGURE_COMMAND)|" -e "s|@COMPILER@|$(CXX)|" -e "s|@COMPILER_VERSION@|$(CXX_VERSION)|" $< | tee $@ > /dev/null\n'
@sed -e "s|@GIT_COMMIT@|$(GIT_COMMIT)|" -e "s|@CONFIGURE_COMMAND@|$(CONFIGURE_COMMAND)|" -e "s|@COMPILER@|$(CXX)|" -e "s|@COMPILER_VERSION@|$(CXX_VERSION)|" $< | tee $@ > /dev/null
#? Man page
btop.1: manpage.md | directories
ifeq ($(shell command -v lowdown >/dev/null; echo $$?),0)
@printf "\n\033[1;92mGenerating man page $@\033[37m...\033[0m\n"
lowdown -s -Tman -o $@ $<
else
@printf "\n\033[1;93mCommand 'lowdown' not found: skipping generating man page $@\033[0m\n"
endif
#? Clean only Objects
clean:
@printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
@rm -rf $(BUILDDIR)
@cmake --build lib/rocm_smi_lib/build --target clean &> /dev/null || true
@test -e lib/rocm_smi_lib/build && cmake --build lib/rocm_smi_lib/build --target clean &> /dev/null || true
#? Clean Objects and Binaries
distclean: clean
@printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
@rm -rf $(TARGETDIR)
@rm -rf lib/rocm_smi_lib/build
@test -e lib/rocm_smi_lib/build && rm -rf lib/rocm_smi_lib/build || true
install:
@printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
@ -306,7 +319,11 @@ install:
@printf "\033[1;92mInstalling SVG icon to: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps
@cp -p Img/icon.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg
ifneq ($(wildcard btop.1),)
@printf "\033[1;92mInstalling man page to: \033[1;97m$(DESTDIR)$(PREFIX)/share/man/man1/btop.1\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
@cp -p btop.1 $(DESTDIR)$(PREFIX)/share/man/man1/btop.1
endif
#? Set SUID bit for btop as $SU_USER in $SU_GROUP
setuid:
@ -316,17 +333,20 @@ setuid:
@printf "\033[1;92mSetting SUID bit\033[0m\n"
@chmod u+s $(DESTDIR)$(PREFIX)/bin/btop
# With 'rm -v' user will see what files (if any) got removed
uninstall:
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
@rm -rfv $(DESTDIR)$(PREFIX)/bin/btop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/btop
@rm -rfv $(DESTDIR)$(PREFIX)/share/btop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/applications/btop.desktop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/applications/btop.desktop
@rm -rfv $(DESTDIR)$(PREFIX)/share/applications/btop.desktop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png
@rm -rfv $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg
@rm -rfv $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/man/man1/btop.1\033[0m\n"
@rm -rfv $(DESTDIR)$(PREFIX)/share/man/man1/btop.1
#? Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
@ -370,7 +390,7 @@ btop: $(OBJECTS) | rocm_smi directories
#? Compile
.ONESHELL:
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | rocm_smi directories
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | rocm_smi directories config.h
@sleep 0.3 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\033[1;97mCompiling $<\033[0m\n"
@ -379,4 +399,4 @@ $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | rocm_smi directories
@printf "\033[1;92m$$($(PROGRESS))$(P)\033[10D\033[5C-> \033[1;37m$@ \033[100D\033[38C\033[1;93m(\033[1;97m$$(du -ah $@ | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$($(DATE_CMD) +%s 2>/dev/null || echo "0") - $${TSTAMP} 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo '')\033[92m)\033[0m\n"
#? Non-File Targets
.PHONY: all msg help pre
.PHONY: all config.h msg help pre

264
README.md
View file

@ -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)
@ -40,6 +43,15 @@
## News
##### 7 January 2024
Btop release v1.3.0
Big release with GPU support added for Linux and platform support for OpenBSD. Big thanks to [@romner-set](https://github.com/romner-set) (GPU support) and [@joske](https://github.com/joske) (OpenBSD support) for contributions.
And a multitude of bugfixes and small changes, see [CHANGELOG.md](CHANGELOG.md) and latest [release](https://github.com/aristocratos/btop/releases/latest) for detailed list and attributions.
See news entry below for more information regarding GPU support.
##### 25 November 2023
GPU monitoring added for Linux!
@ -66,6 +78,9 @@ Big update with version bump to 1.3 coming soon.
First release of btop4win available at https://github.com/aristocratos/btop4win
<details>
<summary>More...</summary>
##### 16 January 2022
Release v1.2.0 with FreeBSD support. No release binaries for FreeBSD provided as of yet.
@ -81,9 +96,6 @@ macOS binaries + installer are included for both x86 and ARM64 (Apple Silicon) i
Big thank you to [@joske](https://github.com/joske) who wrote the vast majority of the implementation!
<details>
<summary>More...</summary>
##### 30 October 2021
Work on the OSX [macOS] and FreeBSD branches, both initiated and mostly worked on by [@joske](https://github.com/joske), will likely be completed in the coming weeks.
@ -94,7 +106,7 @@ If you want to help out, test for bugs/fix bugs or just try out the branches:
**macOS / OSX**
```bash
# Install and use Homebrew or MacPorts package managers for easy dependency installation
brew install coreutils make gcc@11
brew install coreutils make gcc@11 lowdown
git clone https://github.com/aristocratos/btop.git
cd btop
git checkout OSX
@ -103,7 +115,7 @@ gmake
**FreeBSD**
```bash
sudo pkg install gmake gcc11 coreutils git
sudo pkg install gmake gcc11 coreutils git lowdown
git clone https://github.com/aristocratos/btop.git
cd btop
git checkout freebsd
@ -210,6 +222,22 @@ Also needs a UTF8 locale and a font that covers:
* Unicode Block “Geometric Shapes” U+25A0 - U+25FF
* Unicode Block "Box Drawing" and "Block Elements" U+2500 - U+259F
### **Optional Dependencies (Needed for GPU monitoring)**
GPU monitoring also requires a btop binary built with GPU support (`GPU_SUPPORT=true` flag).
See [GPU compatibility](#gpu-compatibility) section for more about compiling with GPU support.
* **NVIDIA**
If you have an NVIDIA GPU you must use an official NVIDIA driver, both the closed-source and open-source ones have been verified to work.
In addition to that you must also have the nvidia-ml dynamic library installed, which should be included with the driver package of your distribution.
* **AMD**
If you have an AMD GPU `rocm_smi_lib` is required, which may or may not be packaged for your distribution.
### **Notice (Text rendering issues)**
* If you are having problems with the characters in the graphs not looking like they do in the screenshots, it's likely a problem with your systems configured fallback font not having support for braille characters.
@ -350,12 +378,12 @@ Also needs a UTF8 locale and a font that covers:
* **AMD**
AMDGPU data is queried using the [ROCm SMI](https://github.com/RadeonOpenCompute/rocm_smi_lib) library, which may or may not be packaged for your distribution. If your distribution doesn't provide a package, btop++ is statically linked to ROCm SMI with the `RSMI_STATIC=true` make flag.
AMDGPU data is queried using the [ROCm SMI](https://github.com/rocm/rocm_smi_lib) library, which may or may not be packaged for your distribution. If your distribution doesn't provide a package, btop++ is statically linked to ROCm SMI with the `RSMI_STATIC=true` make flag.
This flag expects the ROCm SMI source code in `lib/rocm_smi_lib`, and compilation will fail if it's not there. The latest tested version is 5.6.x, which can be obtained with the following command:
```bash
git clone https://github.com/RadeonOpenCompute/rocm_smi_lib.git --depth 1 -b rocm-5.6.x lib/rocm_smi_lib
git clone https://github.com/rocm/rocm_smi_lib.git --depth 1 -b rocm-5.6.x lib/rocm_smi_lib
```
<details>
@ -364,11 +392,11 @@ Also needs a UTF8 locale and a font that covers:
### With Make
</summary>
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
```bash
sudo apt install coreutils sed git build-essential gcc-11 g++-11
```
sudo apt install coreutils sed git build-essential gcc-11 g++-11 lowdown
```
2. **Clone repository**
@ -391,17 +419,19 @@ Also needs a UTF8 locale and a font that covers:
| `STATIC=true` | For static compilation |
| `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 |
| `FORTIFY_SOURCE=false` | Disable fortification with `_FORTIFY_SOURCE=3` |
| `GPU_SUPPORT=<true\|false>` | Enable/disable GPU support (Enabled by default on X86_64 Linux) |
| `RSMI_STATIC=true` | To statically link the ROCm SMI library used for querying AMDGPU |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
| `CXX=<compiler>` | Manually set which compiler to use |
Example: `make ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
Notice! If using LDAP Authentication, usernames will show as UID number for LDAP users if compiling statically with glibc.
4. **Install**
4. **Install**
```bash
sudo make install
@ -411,7 +441,7 @@ Also needs a UTF8 locale and a font that covers:
Notice! Only use "sudo" when installing to a NON user owned directory.
5. **(Optional) Set suid bit to make btop always run as root (or other user)**
5. **(Optional) Set suid bit to make btop always run as root (or other user)**
```bash
sudo make setuid
@ -456,12 +486,12 @@ Also needs a UTF8 locale and a font that covers:
1. **Install build dependencies**
Requires Clang / GCC, CMake, Ninja and Git
Requires Clang / GCC, CMake, Ninja, Lowdown and Git
For example, with Debian Bookworm:
```bash
sudo apt install cmake git g++ ninja-build
sudo apt install cmake git g++ ninja-build lowdown
```
2. **Clone the repository**
@ -490,6 +520,7 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_FORTIFY=<ON\|OFF>` | Detect buffer overflows with `_FORTIFY_SOURCE=3` (ON by default) |
| `-DBTOP_GPU=<ON\|OFF>` | Enable GPU support (ON by default) |
| `-DBTOP_RSMI_STATIC=<ON\|OFF>` | Build and link the ROCm SMI library statically (OFF by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
@ -538,7 +569,7 @@ Also needs a UTF8 locale and a font that covers:
1. **Install dependencies (example for Homebrew)**
```bash
brew install coreutils make gcc@12
brew install coreutils make gcc@12 lowdown
```
2. **Clone repository**
@ -561,13 +592,15 @@ Also needs a UTF8 locale and a font that covers:
| `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) |
| `ARCH=<architecture>` | To manually set the target architecture |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `FORTIFY_SOURCE=false` | Disable fortification with `_FORTIFY_SOURCE=3` |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
| `CXX=<compiler>` | Manually set which compiler to use |
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
4. **Install**
4. **Install**
```bash
sudo gmake install
@ -577,7 +610,7 @@ Also needs a UTF8 locale and a font that covers:
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)**
5. **(Recommended) Set suid bit to make btop always run as root (or other user)**
```bash
sudo gmake setuid
@ -622,11 +655,11 @@ Also needs a UTF8 locale and a font that covers:
1. **Install build dependencies**
Requires Clang, CMake, Ninja and Git
Requires Clang, CMake, Ninja, Lowdown and Git
```bash
brew update --quiet
brew install cmake git llvm ninja
brew install cmake git llvm ninja lowdown
```
2. **Clone the repository**
@ -660,6 +693,7 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_FORTIFY=<ON\|OFF>` | Detect buffer overflows with `_FORTIFY_SOURCE=3` (ON by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
To force any specific compiler, run `CXX=<compiler> cmake -B build -G Ninja`
@ -702,7 +736,7 @@ Also needs a UTF8 locale and a font that covers:
1. **Install dependencies**
```bash
sudo pkg install gmake gcc11 coreutils git
sudo pkg install gmake gcc11 coreutils git lowdown
```
2. **Clone repository**
@ -726,18 +760,20 @@ Also needs a UTF8 locale and a font that covers:
| `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) |
| `ARCH=<architecture>` | To manually set the target architecture |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `FORTIFY_SOURCE=false` | Disable fortification with `_FORTIFY_SOURCE=3` |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
| `CXX=<compiler>` | Manually set which compiler to use |
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
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.
@ -747,7 +783,7 @@ Also needs a UTF8 locale and a font that covers:
```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.
@ -787,18 +823,18 @@ Also needs a UTF8 locale and a font that covers:
1. **Install build dependencies**
Requires Clang / GCC, CMake, Ninja and Git
Requires Clang / GCC, CMake, Ninja, Lowdown and Git
_**Note:** LLVM's libc++ shipped with FreeBSD 13 is too old and cannot compile btop._
FreeBSD 14 and later:
```bash
pkg install cmake ninja
pkg install cmake ninja lowdown
```
FreeBSD 13:
```bash
pkg install cmake gcc13 ninja
pkg install cmake gcc13 ninja lowdown
```
2. **Clone the repository**
@ -836,6 +872,7 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_FORTIFY=<ON\|OFF>` | Detect buffer overflows with `_FORTIFY_SOURCE=3` (ON by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
_**Note:** Static linking does not work with GCC._
@ -865,6 +902,169 @@ 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 lowdown
```
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 |
| `FORTIFY_SOURCE=false` | Disable fortification with `_FORTIFY_SOURCE=3` |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manually 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>
<details>
<summary>
### With CMake (Community maintained)
</summary>
1. **Install build dependencies**
Requires GCC, CMake, Ninja, Lowdown 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 lowdown
```
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=<ON\|OFF>` | Enables link time optimization (ON by default) |
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_FORTIFY=<ON\|OFF>` | Detect buffer overflows with `_FORTIFY_SOURCE=3` (ON by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
To force any other compiler, run `CXX=<compiler> 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
```
</details>
## Installing the snap
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)

View file

@ -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)

File diff suppressed because it is too large Load diff

57
manpage.md Normal file
View file

@ -0,0 +1,57 @@
% btop(1) | User Commands
%
% "January 4 2024"
# NAME
btop - Resource monitor that shows usage and stats for processor, memory, disks, network, and processes.
# SYNOPSIS
**btop** [**-lc**] [**-t** | **+t**] [**-p** _id_] [**\-\-utf-force**]
[**\-\-debug**] [{**-h** | **\-\-help**} | {**-v** | **\-\-version**}]
# DESCRIPTION
**btop** is a program that shows usage and stats for processor, memory, disks, network, and processes.
# OPTIONS
The program follows the usual GNU command line syntax, with long options
starting with two dashes ('-'). A summary of options is included below.
**-lc**, **\-\-low-color**
: Disable truecolor, converts 24-bit colors to 256-color.
**-t**, **\-\-tty_on**
: Force (ON) tty mode, max 16 colors and tty-friendly graph symbols.
**+t**, **\-\-tty_off**
: Force (OFF) tty mode.
**-p**, **\-\-preset _id_**
: Start with preset, integer value between 0-9.
**\-\-utf-force**
: Force start even if no UTF-8 locale was detected.
**\-\-debug**
: Start in DEBUG mode: shows microsecond timer for information collect and screen draw functions and sets loglevel to DEBUG.
**-h**, **\-\-help**
: Show summary of options.
**-v**, **\-\-version**
: Show version of program.
# BUGS
The upstream bug tracker can be found at https://github.com/aristocratos/btop/issues.
# SEE ALSO
**top**(1), **htop**(1)
# AUTHOR
**btop** was written by Jakob P. Liljenberg a.k.a. "Aristocratos".

View file

@ -45,9 +45,12 @@ parts:
source-type: git
plugin: make
make-parameters:
- CXX=g++-11
- PREFIX=/usr/local
- STATIC=true
- ADDFLAGS="-D SNAPPED"
# Add 'lowdown' to build dependencies as soon at Snap builder updates from
# Ubuntu 20.04 "Focal" to something newer that has Lowdown included
build-packages:
- coreutils
- sed
@ -55,7 +58,7 @@ parts:
- build-essential
- gcc-11
- g++-11
override-pull: |
snapcraftctl pull
snapcraftctl set-version "$(git describe --tags | sed 's/^v//' | cut -d "-" -f1)"

View file

@ -16,6 +16,7 @@ indent = tab
tab-size = 4
*/
#include <algorithm>
#include <csignal>
#include <clocale>
#include <pthread.h>
@ -32,6 +33,7 @@ tab-size = 4
#include <tuple>
#include <regex>
#include <chrono>
#include <utility>
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
@ -50,6 +52,9 @@ tab-size = 4
#include "btop_theme.hpp"
#include "btop_draw.hpp"
#include "btop_menu.hpp"
#include "config.h"
#include "fmt/core.h"
#include "fmt/ostream.h"
using std::atomic;
using std::cout;
@ -75,7 +80,7 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const string Version = "1.3.0";
const string Version = "1.3.2";
int coreCount;
string overlay;
@ -93,9 +98,9 @@ namespace Global {
string exit_error_msg;
atomic<bool> thread_exception (false);
bool debuginit{}; // defaults to false
bool debug{}; // defaults to false
bool utf_force{}; // defaults to false
bool debuginit{};
bool debug{};
bool utf_force{};
uint64_t start_time;
@ -104,19 +109,35 @@ namespace Global {
atomic<bool> should_quit (false);
atomic<bool> should_sleep (false);
atomic<bool> _runner_started (false);
atomic<bool> init_conf (false);
atomic<bool> reload_conf (false);
bool arg_tty{}; // defaults to false
bool arg_low_color{}; // defaults to false
bool arg_tty{};
bool arg_low_color{};
int arg_preset = -1;
int arg_update = 0;
}
static void print_version() {
if constexpr (GIT_COMMIT.empty()) {
fmt::print("btop version: {}\n", Global::Version);
} else {
fmt::print("btop version: {}+{}\n", Global::Version, GIT_COMMIT);
}
}
static void print_version_with_build_info() {
print_version();
fmt::print("Compiled with: {} ({})\nConfigured with: {}\n", COMPILER, COMPILER_VERSION, CONFIGURE_COMMAND);
}
//* 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"
@ -124,14 +145,19 @@ 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"
);
exit(0);
}
else if (is_in(argument, "-v", "--version")) {
fmt::println("btop version: {}", Global::Version);
else if (is_in(argument, "-v")) {
print_version();
exit(0);
}
else if (is_in(argument, "--version")) {
print_version_with_build_info();
exit(0);
}
else if (is_in(argument, "-lc", "--low-color")) {
@ -158,6 +184,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")
@ -175,7 +214,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);
@ -245,7 +284,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
@ -254,7 +293,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);
@ -290,7 +329,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);
@ -320,7 +359,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);
@ -330,7 +369,7 @@ void _signal_handler(const int sig) {
if (Runner::active) {
Global::should_sleep = true;
Runner::stopping = true;
Input::interrupt = true;
Input::interrupt();
}
else {
_sleep();
@ -342,9 +381,41 @@ void _signal_handler(const int sig) {
case SIGWINCH:
term_resize();
break;
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<string> 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
namespace Runner {
atomic<bool> active (false);
@ -381,7 +452,7 @@ namespace Runner {
}
};
//* Wrapper for raising priviliges when using SUID bit
//* Wrapper for raising privileges when using SUID bit
class gain_priv {
int status = -1;
public:
@ -397,7 +468,7 @@ namespace Runner {
string output;
string empty_bg;
bool pause_output{}; // defaults to false
bool pause_output{};
sigset_t mask;
pthread_t runner_id;
pthread_mutex_t mtx;
@ -416,7 +487,7 @@ namespace Runner {
};
string debug_bg;
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
std::unordered_map<string, array<uint64_t, 2>> debug_times;
class MyNumPunct : public std::numpunct<char>
{
@ -476,7 +547,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;
}
@ -487,7 +558,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) {
@ -557,7 +628,7 @@ namespace Runner {
coreNum_reset = false;
Cpu::core_mapping = Cpu::get_core_mapping();
Global::resized = true;
Input::interrupt = true;
Input::interrupt();
continue;
}
@ -654,7 +725,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;
}
@ -814,7 +885,7 @@ int main(int argc, char **argv) {
Global::start_time = time_s();
//? Save real and effective userid's and drop priviliges until needed if running with SUID bit set
//? Save real and effective userid's and drop privileges until needed if running with SUID bit set
Global::real_uid = getuid();
Global::set_uid = geteuid();
if (Global::real_uid != Global::set_uid) {
@ -828,29 +899,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;
@ -879,22 +944,7 @@ int main(int argc, char **argv) {
}
//? Config init
{ vector<string> load_warnings;
Config::load(Config::conf_file, load_warnings);
if (Config::current_boxes.empty()) Config::check_boxes(Config::getS("shown_boxes"));
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, ""), ";")
@ -903,8 +953,8 @@ int main(int argc, char **argv) {
}
else {
string found;
bool set_failure{}; // defaults to false
for (const auto loc_env : array{"LANG", "LC_ALL"}) {
bool set_failure{};
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) {
@ -930,7 +980,7 @@ int main(int argc, char **argv) {
catch (...) { found.clear(); }
}
}
//
#ifdef __APPLE__
if (found.empty()) {
CFLocaleRef cflocale = CFLocaleCopyCurrent();
@ -974,7 +1024,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");
@ -1003,6 +1053,11 @@ int main(int argc, char **argv) {
clean_quit(1);
}
if (not Config::check_boxes(Config::getS("shown_boxes"))) {
Config::check_boxes("cpu mem net proc");
Config::set("shown_boxes", "cpu mem net proc"s);
}
//? Update list of available themes and generate the selected theme
Theme::updateThemes();
Theme::setTheme();
@ -1013,6 +1068,13 @@ 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);
std::signal(SIGUSR2, _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();
@ -1034,9 +1096,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;
}
}
@ -1049,15 +1112,36 @@ 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();
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);
@ -1092,9 +1176,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();

View file

@ -21,8 +21,10 @@ tab-size = 4
#include <fstream>
#include <ranges>
#include <string_view>
#include <utility>
#include <fmt/core.h>
#include <sys/statvfs.h>
#include "btop_config.hpp"
#include "btop_shared.hpp"
@ -197,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
@ -215,7 +219,7 @@ namespace Config {
#endif
};
unordered_flat_map<std::string_view, string> strings = {
std::unordered_map<std::string_view, string> strings = {
{"color_theme", "Default"},
{"shown_boxes", "cpu mem net proc"},
{"graph_symbol", "braille"},
@ -251,9 +255,9 @@ namespace Config {
{"show_gpu_info", "Auto"}
#endif
};
unordered_flat_map<std::string_view, string> stringsTmp;
std::unordered_map<std::string_view, string> stringsTmp;
unordered_flat_map<std::string_view, bool> bools = {
std::unordered_map<std::string_view, bool> bools = {
{"theme_background", true},
{"truecolor", true},
{"rounded_corners", true},
@ -291,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},
@ -304,9 +309,9 @@ namespace Config {
{"gpu_mirror_graph", true},
#endif
};
unordered_flat_map<std::string_view, bool> boolsTmp;
std::unordered_map<std::string_view, bool> boolsTmp;
unordered_flat_map<std::string_view, int> ints = {
std::unordered_map<std::string_view, int> ints = {
{"update_ms", 2000},
{"net_download", 100},
{"net_upload", 100},
@ -317,7 +322,66 @@ namespace Config {
{"proc_selected", 0},
{"proc_last_selected", 0},
};
unordered_flat_map<std::string_view, int> intsTmp;
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);
@ -427,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;
@ -446,7 +510,7 @@ namespace Config {
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
validError = fmt::format("Invalid graph symbol identifier for {}: {}", name, value);
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
else if (name == "shown_boxes" and not Global::init_conf and not value.empty() and not check_boxes(value))
validError = "Invalid box name(s) in shown_boxes!";
#ifdef GPU_SUPPORT
@ -558,8 +622,7 @@ namespace Config {
if (not v_contains(valid_boxes, box)) return false;
#ifdef GPU_SUPPORT
if (box.starts_with("gpu")) {
size_t gpu_num = stoi(box.substr(3));
if (gpu_num == 0) gpu_num = 5;
size_t gpu_num = stoi(box.substr(3)) + 1;
if (std::cmp_greater(gpu_num, Gpu::gpu_names.size())) return false;
}
#endif
@ -593,12 +656,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;
@ -664,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) << "\"";
@ -674,6 +742,7 @@ namespace Config {
cwrite << ints.at(name);
else if (bools.contains(name))
cwrite << (bools.at(name) ? "True" : "False");
cwrite << "\n";
}
}
}

View file

@ -18,15 +18,15 @@ tab-size = 4
#pragma once
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
#include <filesystem>
#include <robin_hood.h>
#include <unordered_map>
using std::string;
using std::vector;
using robin_hood::unordered_flat_map;
//* Functions and variables for reading and writing the btop config file
namespace Config {
@ -34,12 +34,12 @@ namespace Config {
extern std::filesystem::path conf_dir;
extern std::filesystem::path conf_file;
extern unordered_flat_map<std::string_view, string> strings;
extern unordered_flat_map<std::string_view, string> stringsTmp;
extern unordered_flat_map<std::string_view, bool> bools;
extern unordered_flat_map<std::string_view, bool> boolsTmp;
extern unordered_flat_map<std::string_view, int> ints;
extern unordered_flat_map<std::string_view, int> intsTmp;
extern std::unordered_map<std::string_view, string> strings;
extern std::unordered_map<std::string_view, string> stringsTmp;
extern std::unordered_map<std::string_view, bool> bools;
extern std::unordered_map<std::string_view, bool> boolsTmp;
extern std::unordered_map<std::string_view, int> ints;
extern std::unordered_map<std::string_view, int> intsTmp;
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
@ -58,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);
@ -95,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;
}

View file

@ -22,6 +22,7 @@ tab-size = 4
#include <ranges>
#include <stdexcept>
#include <string>
#include <utility>
#include "btop_draw.hpp"
#include "btop_config.hpp"
@ -54,7 +55,7 @@ namespace Symbols {
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
const unordered_flat_map<string, vector<string>> graph_symbols = {
const std::unordered_map<string, vector<string>> graph_symbols = {
{ "braille_up", {
" ", "", "", "", "",
"", "", "", "", "",
@ -300,14 +301,14 @@ namespace Draw {
return false;
}
static const unordered_flat_map<string, string> clock_custom_format = {
static const std::unordered_map<string, string> clock_custom_format = {
{"/user", Tools::username()},
{"/host", Tools::hostname()},
{"/uptime", ""}
};
static time_t c_time{}; // defaults to 0
static size_t clock_len{}; // defaults to 0
static time_t c_time{};
static size_t clock_len{};
static string clock_str;
if (auto n_time = time(nullptr); not force and n_time == c_time)
@ -560,12 +561,12 @@ namespace Cpu {
const string& title_left = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
const string& title_right = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
static int bat_pos = 0, bat_len = 0;
if (cpu.cpu_percent.at("total").empty()
or cpu.core_percent.at(0).empty()
or (show_temps and cpu.temp.at(0).empty())) return "";
if (cpu.cpu_percent.at("total").empty()
or cpu.core_percent.at(0).empty()
or (show_temps and cpu.temp.at(0).empty())) return "";
if (safeVal(cpu.cpu_percent, "total"s).empty()
or safeVal(cpu.core_percent, 0).empty()
or (show_temps and safeVal(cpu.temp, 0).empty())) return "";
if (safeVal(cpu.cpu_percent, "total"s).empty()
or safeVal(cpu.core_percent, 0).empty()
or (show_temps and safeVal(cpu.temp, 0).empty())) return "";
string out;
out.reserve(width * height);
@ -608,7 +609,7 @@ namespace Cpu {
if (gpu.supported_functions.temp_info)
gpu_temp_graphs[i] = Draw::Graph{ 5, 1, "temp", gpu.temp, graph_symbol, false, false, gpu.temp_max, -23 };
if (gpu.supported_functions.mem_used and gpu.supported_functions.mem_total)
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", gpu.gpu_percent.at("gpu-vram-totals"), graph_symbol };
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", safeVal(gpu.gpu_percent, "gpu-vram-totals"s), graph_symbol };
if (gpu.supported_functions.gpu_utilization) {
gpu_meter_width = b_width - 12 - (int)floating_humanizer(gpu.mem_total, true).size() - (show_temps ? 24 : 12) - (int)to_string(i).size() + (gpus.size() == 1)*2 - (gpus.size() > 9 and i <= 9);
gpu_meters[i] = Draw::Meter{gpu_meter_width, "cpu" };
@ -617,12 +618,12 @@ namespace Cpu {
bool utilization_support = gpu.supported_functions.gpu_utilization;
if (++i < gpus.size()) {
if (utilization_support)
graph = Draw::Graph{graph_width, graph_height, "cpu", gpu.gpu_percent.at(graph_field), graph_symbol, invert, true};
graph = Draw::Graph{graph_width, graph_height, "cpu", safeVal(gpu.gpu_percent, graph_field), graph_symbol, invert, true};
} else {
if (utilization_support)
graph = Draw::Graph{
graph_width + graph_default_width%graph_width - (int)gpus.size() + 1,
graph_height, "cpu", gpu.gpu_percent.at(graph_field), graph_symbol, invert, true
graph_height, "cpu", safeVal(gpu.gpu_percent, graph_field), graph_symbol, invert, true
};
break;
}
@ -630,7 +631,7 @@ namespace Cpu {
} else {
graphs.resize(1);
graph_width = graph_default_width;
graphs[0] = Draw::Graph{ graph_width, graph_height, "cpu", Gpu::shared_gpu_percent.at(graph_field), graph_symbol, invert, true };
graphs[0] = Draw::Graph{ graph_width, graph_height, "cpu", safeVal(Gpu::shared_gpu_percent, graph_field), graph_symbol, invert, true };
gpu_temp_graphs.resize(gpus.size());
gpu_mem_graphs.resize(gpus.size());
gpu_meters.resize(gpus.size());
@ -638,7 +639,7 @@ namespace Cpu {
if (gpus[i].supported_functions.temp_info)
gpu_temp_graphs[i] = Draw::Graph{ 5, 1, "temp", gpus[i].temp, graph_symbol, false, false, gpus[i].temp_max, -23 };
if (gpus[i].supported_functions.mem_used and gpus[i].supported_functions.mem_total)
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", gpus[i].gpu_percent.at("gpu-vram-totals"), graph_symbol };
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", safeVal(gpus[i].gpu_percent, "gpu-vram-totals"s), graph_symbol };
if (gpus[i].supported_functions.gpu_utilization) {
gpu_meter_width = b_width - 12 - (int)floating_humanizer(gpus[i].mem_total, true).size() - (show_temps ? 24 : 12) - (int)to_string(i).size() + (gpus.size() == 1)*2 - (gpus.size() > 9 and i <= 9);
gpu_meters[i] = Draw::Meter{gpu_meter_width, "cpu" };
@ -649,7 +650,7 @@ namespace Cpu {
#endif
graphs.resize(1);
graph_width = graph_default_width;
graphs[0] = Draw::Graph{ graph_width, graph_height, "cpu", cpu.cpu_percent.at(graph_field), graph_symbol, invert, true };
graphs[0] = Draw::Graph{ graph_width, graph_height, "cpu", safeVal(cpu.cpu_percent, graph_field), graph_symbol, invert, true };
#ifdef GPU_SUPPORT
if (std::cmp_less(Gpu::shown, gpus.size())) {
gpu_temp_graphs.resize(gpus.size());
@ -659,7 +660,7 @@ namespace Cpu {
if (gpus[i].supported_functions.temp_info)
gpu_temp_graphs[i] = Draw::Graph{ 5, 1, "temp", gpus[i].temp, graph_symbol, false, false, gpus[i].temp_max, -23 };
if (gpus[i].supported_functions.mem_used and gpus[i].supported_functions.mem_total)
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", gpus[i].gpu_percent.at("gpu-vram-totals"), graph_symbol };
gpu_mem_graphs[i] = Draw::Graph{ 5, 1, "used", safeVal(gpus[i].gpu_percent, "gpu-vram-totals"s), graph_symbol };
if (gpus[i].supported_functions.gpu_utilization) {
gpu_meter_width = b_width - 12 - (int)floating_humanizer(gpus[i].mem_total, true).size() - (show_temps ? 24 : 12) - (int)to_string(i).size() + (gpus.size() == 1)*2 - (gpus.size() > 9 and i <= 9);
gpu_meters[i] = Draw::Meter{gpu_meter_width, "cpu" };
@ -692,10 +693,10 @@ namespace Cpu {
if (show_temps) {
temp_graphs.clear();
temp_graphs.emplace_back(5, 1, "temp", cpu.temp.at(0), graph_symbol, false, false, cpu.temp_max, -23);
temp_graphs.emplace_back(5, 1, "temp", safeVal(cpu.temp, 0), graph_symbol, false, false, cpu.temp_max, -23);
if (not hide_cores and b_column_size > 1) {
for (const auto& i : iota((size_t)1, cpu.temp.size())) {
temp_graphs.emplace_back(5, 1, "temp", cpu.temp.at(i), graph_symbol, false, false, cpu.temp_max, -23);
temp_graphs.emplace_back(5, 1, "temp", safeVal(cpu.temp, i), graph_symbol, false, false, cpu.temp_max, -23);
}
}
}
@ -705,25 +706,28 @@ 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 unordered_flat_map<string, string> bat_symbols = {
static const std::unordered_map<string, string> bat_symbols = {
{"charging", ""},
{"discharging", ""},
{"full", ""},
{"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)
@ -733,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) {
@ -749,7 +753,7 @@ namespace Cpu {
if (graph_field.starts_with("gpu"))
if (graph_field.find("totals") != string::npos)
for (unsigned long i = 0;;) {
out += graphs[i](gpus[i].gpu_percent.at(graph_field), (data_same or redraw));
out += graphs[i](safeVal(gpus[i].gpu_percent, graph_field), (data_same or redraw));
if (gpus.size() > 1) {
auto i_str = to_string(i);
out += Mv::l(graph_width-1) + Mv::u(graph_height/2) + (graph_width > 5 ? "GPU " : "") + i_str
@ -761,13 +765,13 @@ namespace Cpu {
else break;
}
else
out += graphs[0](Gpu::shared_gpu_percent.at(graph_field), (data_same or redraw));
out += graphs[0](safeVal(Gpu::shared_gpu_percent, graph_field), (data_same or redraw));
else
#else
(void)graph_height;
(void)graph_width;
#endif
out += graphs[0](cpu.cpu_percent.at(graph_field), (data_same or redraw));
out += graphs[0](safeVal(cpu.cpu_percent, graph_field), (data_same or redraw));
};
draw_graphs(graphs_upper, graph_up_height, graph_up_width, graph_up_field);
@ -792,14 +796,14 @@ namespace Cpu {
out += Mv::to(b_y, b_x + b_width - 10) + Fx::ub + Theme::c("div_line") + Symbols::h_line * (7 - cpuHz.size())
+ Symbols::title_left + Fx::b + Theme::c("title") + cpuHz + Fx::ub + Theme::c("div_line") + Symbols::title_right;
out += Mv::to(b_y + 1, b_x + 1) + Theme::c("main_fg") + Fx::b + "CPU " + cpu_meter(cpu.cpu_percent.at("total").back())
+ Theme::g("cpu").at(clamp(cpu.cpu_percent.at("total").back(), 0ll, 100ll)) + rjust(to_string(cpu.cpu_percent.at("total").back()), 4) + Theme::c("main_fg") + '%';
out += Mv::to(b_y + 1, b_x + 1) + Theme::c("main_fg") + Fx::b + "CPU " + cpu_meter(safeVal(cpu.cpu_percent, "total"s).back())
+ Theme::g("cpu").at(clamp(safeVal(cpu.cpu_percent, "total"s).back(), 0ll, 100ll)) + rjust(to_string(safeVal(cpu.cpu_percent, "total"s).back()), 4) + Theme::c("main_fg") + '%';
if (show_temps) {
const auto [temp, unit] = celsius_to(cpu.temp.at(0).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(cpu.temp.at(0).back() * 100 / cpu.temp_max, 0ll, 100ll));
if (b_column_size > 1 or b_columns > 1)
const auto [temp, unit] = celsius_to(safeVal(cpu.temp, 0).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, 0).back() * 100 / cpu.temp_max, 0ll, 100ll));
if ((b_column_size > 1 or b_columns > 1) and temp_graphs.size() >= 1ll)
out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5) + temp_color
+ temp_graphs.at(0)(cpu.temp.at(0), data_same or redraw);
+ temp_graphs.at(0)(safeVal(cpu.temp, 0), data_same or redraw);
out += rjust(to_string(temp), 4) + Theme::c("main_fg") + unit;
}
out += Theme::c("div_line") + Symbols::v_line;
@ -810,21 +814,22 @@ namespace Cpu {
int cx = 0, cy = 1, cc = 0, core_width = (b_column_size == 0 ? 2 : 3);
if (Shared::coreCount >= 100) core_width++;
for (const auto& n : iota(0, Shared::coreCount)) {
if (cmp_less(core_graphs.size(), n+1)) break;
out += Mv::to(b_y + cy + 1, b_x + cx + 1) + Theme::c("main_fg") + (Shared::coreCount < 100 ? Fx::b + 'C' + Fx::ub : "")
+ ljust(to_string(n), core_width);
if (b_column_size > 0 or extra_width > 0)
out += Theme::c("inactive_fg") + graph_bg * (5 * b_column_size + extra_width) + Mv::l(5 * b_column_size + extra_width)
+ core_graphs.at(n)(cpu.core_percent.at(n), data_same or redraw);
+ core_graphs.at(n)(safeVal(cpu.core_percent, n), data_same or redraw);
out += Theme::g("cpu").at(clamp(cpu.core_percent.at(n).back(), 0ll, 100ll));
out += rjust(to_string(cpu.core_percent.at(n).back()), (b_column_size < 2 ? 3 : 4)) + Theme::c("main_fg") + '%';
out += Theme::g("cpu").at(clamp(safeVal(cpu.core_percent, n).back(), 0ll, 100ll));
out += rjust(to_string(safeVal(cpu.core_percent, n).back()), (b_column_size < 2 ? 3 : 4)) + Theme::c("main_fg") + '%';
if (show_temps and not hide_cores) {
const auto [temp, unit] = celsius_to(cpu.temp.at(n+1).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(cpu.temp.at(n+1).back() * 100 / cpu.temp_max, 0ll, 100ll));
if (show_temps and not hide_cores and std::cmp_greater_equal(temp_graphs.size(), n)) {
const auto [temp, unit] = celsius_to(safeVal(cpu.temp, n+1).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(safeVal(cpu.temp, n+1).back() * 100 / cpu.temp_max, 0ll, 100ll));
if (b_column_size > 1)
out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5)
+ temp_graphs.at(n+1)(cpu.temp.at(n+1), data_same or redraw);
+ temp_graphs.at(n+1)(safeVal(cpu.temp, n+1), data_same or redraw);
out += temp_color + rjust(to_string(temp), 4) + Theme::c("main_fg") + unit;
}
@ -882,14 +887,14 @@ namespace Cpu {
}
if (gpus.size() > 1) out += rjust(to_string(i), 1 + (gpus.size() > 9));
if (gpus[i].supported_functions.gpu_utilization) {
string meter = gpu_meters[i](gpus[i].gpu_percent.at("gpu-totals").back());
string meter = gpu_meters[i](safeVal(gpus[i].gpu_percent, "gpu-totals"s).back());
out += (meter.size() > 1 ? " " : "") + meter
+ Theme::g("cpu").at(gpus[i].gpu_percent.at("gpu-totals").back()) + rjust(to_string(gpus[i].gpu_percent.at("gpu-totals").back()), 4) + Theme::c("main_fg") + '%';
+ Theme::g("cpu").at(clamp(safeVal(gpus[i].gpu_percent, "gpu-totals"s).back(), 0ll, 100ll)) + rjust(to_string(safeVal(gpus[i].gpu_percent, "gpu-totals"s).back()), 4) + Theme::c("main_fg") + '%';
} else out += Mv::r(gpu_meter_width);
if (gpus[i].supported_functions.mem_used) {
out += ' ' + Theme::c("inactive_fg") + graph_bg * 6 + Mv::l(6) + Theme::g("used").at(gpus[i].gpu_percent.at("gpu-vram-totals").back())
+ gpu_mem_graphs[i](gpus[i].gpu_percent.at("gpu-vram-totals"), data_same or redraw) + Theme::c("main_fg")
out += ' ' + Theme::c("inactive_fg") + graph_bg * 6 + Mv::l(6) + Theme::g("used").at(safeVal(gpus[i].gpu_percent, "gpu-vram-totals"s).back())
+ gpu_mem_graphs[i](safeVal(gpus[i].gpu_percent, "gpu-vram-totals"s), data_same or redraw) + Theme::c("main_fg")
+ rjust(floating_humanizer(gpus[i].mem_used, true), 5);
if (gpus[i].supported_functions.mem_total)
out += Theme::c("inactive_fg") + '/' + Theme::c("main_fg") + floating_humanizer(gpus[i].mem_total, true);
@ -967,12 +972,12 @@ namespace Gpu {
out += box[index];
if (gpu.supported_functions.gpu_utilization) {
graph_upper = Draw::Graph{x + width - b_width - 3, graph_up_height, "cpu", gpu.gpu_percent.at("gpu-totals"), graph_symbol, false, true}; // TODO cpu -> gpu
graph_upper = Draw::Graph{x + width - b_width - 3, graph_up_height, "cpu", safeVal(gpu.gpu_percent, "gpu-totals"s), graph_symbol, false, true}; // TODO cpu -> gpu
if (not single_graph) {
graph_lower = Draw::Graph{
x + width - b_width - 3,
graph_low_height, "cpu",
gpu.gpu_percent.at("gpu-totals"),
safeVal(gpu.gpu_percent, "gpu-totals"s),
graph_symbol,
Config::getB("cpu_invert_lower"), true
};
@ -986,7 +991,7 @@ namespace Gpu {
if (gpu.supported_functions.mem_utilization)
mem_util_graph = Draw::Graph{b_width/2 - 1, 2, "free", gpu.mem_utilization_percent, graph_symbol, 0, 0, 100, 4}; // offset so the graph isn't empty at 0-5% utilization
if (gpu.supported_functions.mem_used and gpu.supported_functions.mem_total)
mem_used_graph = Draw::Graph{b_width/2 - 2, 2 + 2*(gpu.supported_functions.mem_utilization), "used", gpu.gpu_percent.at("gpu-vram-totals"), graph_symbol};
mem_used_graph = Draw::Graph{b_width/2 - 2, 2 + 2*(gpu.supported_functions.mem_utilization), "used", safeVal(gpu.gpu_percent, "gpu-vram-totals"s), graph_symbol};
}
@ -994,12 +999,12 @@ namespace Gpu {
//? Gpu graph, meter & clock speed
if (gpu.supported_functions.gpu_utilization) {
out += Fx::ub + Mv::to(y + 1, x + 1) + graph_upper(gpu.gpu_percent.at("gpu-totals"), (data_same or redraw[index]));
out += Fx::ub + Mv::to(y + 1, x + 1) + graph_upper(safeVal(gpu.gpu_percent, "gpu-totals"s), (data_same or redraw[index]));
if (not single_graph)
out += Mv::to(y + graph_up_height + 1, x + 1) + graph_lower(gpu.gpu_percent.at("gpu-totals"), (data_same or redraw[index]));
out += Mv::to(y + graph_up_height + 1, x + 1) + graph_lower(safeVal(gpu.gpu_percent, "gpu-totals"s), (data_same or redraw[index]));
out += Mv::to(b_y + 1, b_x + 1) + Theme::c("main_fg") + Fx::b + "GPU " + gpu_meter(gpu.gpu_percent.at("gpu-totals").back())
+ Theme::g("cpu").at(gpu.gpu_percent.at("gpu-totals").back()) + rjust(to_string(gpu.gpu_percent.at("gpu-totals").back()), 4) + Theme::c("main_fg") + '%';
out += Mv::to(b_y + 1, b_x + 1) + Theme::c("main_fg") + Fx::b + "GPU " + gpu_meter(safeVal(gpu.gpu_percent, "gpu-totals"s).back())
+ Theme::g("cpu").at(clamp(safeVal(gpu.gpu_percent, "gpu-totals"s).back(), 0ll, 100ll)) + rjust(to_string(safeVal(gpu.gpu_percent, "gpu-totals"s).back()), 4) + Theme::c("main_fg") + '%';
//? Temperature graph, I assume the device supports utilization if it supports temperature
if (show_temps) {
@ -1019,10 +1024,10 @@ namespace Gpu {
//? Power usage meter, power state
if (gpu.supported_functions.pwr_usage) {
out += Mv::to(b_y + 2, b_x + 1) + Theme::c("main_fg") + Fx::b + "PWR " + pwr_meter(gpu.gpu_percent.at("gpu-pwr-totals").back())
+ Theme::g("cached").at(gpu.gpu_percent.at("gpu-pwr-totals").back()) + rjust(to_string(gpu.pwr_usage/1000), 4) + Theme::c("main_fg") + 'W';
out += Mv::to(b_y + 2, b_x + 1) + Theme::c("main_fg") + Fx::b + "PWR " + pwr_meter(safeVal(gpu.gpu_percent, "gpu-pwr-totals"s).back())
+ Theme::g("cached").at(clamp(safeVal(gpu.gpu_percent, "gpu-pwr-totals"s).back(), 0ll, 100ll)) + rjust(to_string(gpu.pwr_usage/1000), 4) + Theme::c("main_fg") + 'W';
if (gpu.supported_functions.pwr_state and gpu.pwr_state != 32) // NVML_PSTATE_UNKNOWN; unsupported or non-nvidia card
out += std::string(" P-state: ") + (gpu.pwr_state > 9 ? "" : " ") + 'P' + Theme::g("cached").at(gpu.pwr_state) + to_string(gpu.pwr_state);
out += std::string(" P-state: ") + (gpu.pwr_state > 9 ? "" : " ") + 'P' + Theme::g("cached").at(clamp(gpu.pwr_state, 0ll, 100ll)) + to_string(gpu.pwr_state);
}
if (gpu.supported_functions.mem_total or gpu.supported_functions.mem_used) {
@ -1038,9 +1043,9 @@ namespace Gpu {
+ Symbols::h_line*(b_width/2-8) + Symbols::div_up + Mv::d(offset)+Mv::l(1) + Symbols::div_down + Mv::l(1)+Mv::u(1) + (Symbols::v_line + Mv::l(1)+Mv::u(1))*(offset-1) + Symbols::div_up
+ Symbols::h_line + Theme::c("title") + "Used:" + Theme::c("div_line")
+ Symbols::h_line*(b_width/2+b_width%2-9-used_memory_string.size()) + Theme::c("title") + used_memory_string + Theme::c("div_line") + Symbols::h_line + Symbols::div_right
+ Mv::d(1) + Mv::l(b_width/2-1) + mem_used_graph(gpu.gpu_percent.at("gpu-vram-totals"), (data_same or redraw[index]))
+ Mv::d(1) + Mv::l(b_width/2-1) + mem_used_graph(safeVal(gpu.gpu_percent, "gpu-vram-totals"s), (data_same or redraw[index]))
+ Mv::l(b_width-3) + Mv::u(1+2*gpu.supported_functions.mem_utilization) + Theme::c("main_fg") + Fx::b + "Total:" + rjust(floating_humanizer(gpu.mem_total), b_width/2-9) + Fx::ub
+ Mv::r(3) + rjust(to_string(gpu.gpu_percent.at("gpu-vram-totals").back()), 3) + '%';
+ Mv::r(3) + rjust(to_string(safeVal(gpu.gpu_percent, "gpu-vram-totals"s).back()), 3) + '%';
//? Memory utilization
if (gpu.supported_functions.mem_utilization)
@ -1096,11 +1101,11 @@ namespace Mem {
int disks_io_half = 0;
bool shown = true, redraw = true;
string box;
unordered_flat_map<string, Draw::Meter> mem_meters;
unordered_flat_map<string, Draw::Graph> mem_graphs;
unordered_flat_map<string, Draw::Meter> disk_meters_used;
unordered_flat_map<string, Draw::Meter> disk_meters_free;
unordered_flat_map<string, Draw::Graph> io_graphs;
std::unordered_map<string, Draw::Meter> mem_meters;
std::unordered_map<string, Draw::Graph> mem_graphs;
std::unordered_map<string, Draw::Meter> disk_meters_used;
std::unordered_map<string, Draw::Meter> disk_meters_free;
std::unordered_map<string, Draw::Graph> io_graphs;
string draw(const mem_info& mem, bool force_redraw, bool data_same) {
if (Runner::stopping) return "";
@ -1132,14 +1137,14 @@ namespace Mem {
for (const auto& name : mem_names) {
if (use_graphs)
mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name, mem.percent.at(name), graph_symbol};
mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name, safeVal(mem.percent, name), graph_symbol};
else
mem_meters[name] = Draw::Meter{mem_meter, name};
}
if (show_swap and has_swap) {
for (const auto& name : swap_names) {
if (use_graphs)
mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name.substr(5), mem.percent.at(name), graph_symbol};
mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name.substr(5), safeVal(mem.percent, name), graph_symbol};
else
mem_meters[name] = Draw::Meter{mem_meter, name.substr(5)};
}
@ -1148,7 +1153,7 @@ namespace Mem {
//? Disk meters and io graphs
if (show_disks) {
if (show_io_stat or io_mode) {
unordered_flat_map<string, int> custom_speeds;
std::unordered_map<string, int> custom_speeds;
int half_height = 0;
if (io_mode) {
disks_io_h = max((int)floor((double)(height - 2 - (disk_ios * 2)) / max(1, disk_ios)), (io_graph_combined ? 1 : 2));
@ -1230,7 +1235,7 @@ namespace Mem {
if (graph_height > 0) out += Mv::to(y+1+cy, x+1+cx) + divider;
cy += 1;
}
out += Mv::to(y+1+cy, x+1+cx) + Theme::c("title") + Fx::b + "Swap:" + rjust(floating_humanizer(mem.stats.at("swap_total")), mem_width - 8)
out += Mv::to(y+1+cy, x+1+cx) + Theme::c("title") + Fx::b + "Swap:" + rjust(floating_humanizer(safeVal(mem.stats, "swap_total"s)), mem_width - 8)
+ Theme::c("main_fg") + Fx::ub;
cy += 1;
title = "Used";
@ -1239,13 +1244,16 @@ namespace Mem {
title = "Free";
if (title.empty()) title = capitalize(name);
const string humanized = floating_humanizer(mem.stats.at(name));
const string humanized = floating_humanizer(safeVal(mem.stats, name));
const int offset = max(0, divider.empty() ? 9 - (int)humanized.size() : 0);
const string graphics = (use_graphs ? mem_graphs.at(name)(mem.percent.at(name), redraw or data_same) : mem_meters.at(name)(mem.percent.at(name).back()));
const string graphics = (
use_graphs and mem_graphs.contains(name) ? mem_graphs.at(name)(safeVal(mem.percent, name), redraw or data_same)
: mem_meters.contains(name) ? mem_meters.at(name)(safeVal(mem.percent, name).back())
: "");
if (mem_size > 2) {
out += Mv::to(y+1+cy, x+1+cx) + divider + title.substr(0, big_mem ? 10 : 5) + ":"
+ Mv::to(y+1+cy, x+cx + mem_width - 2 - humanized.size()) + (divider.empty() ? Mv::l(offset) + string(" ") * offset + humanized : trans(humanized))
+ Mv::to(y+2+cy, x+cx + (graph_height >= 2 ? 0 : 1)) + graphics + up + rjust(to_string(mem.percent.at(name).back()) + "%", 4);
+ Mv::to(y+2+cy, x+cx + (graph_height >= 2 ? 0 : 1)) + graphics + up + rjust(to_string(safeVal(mem.percent, name).back()) + "%", 4);
cy += (graph_height == 0 ? 2 : graph_height + 1);
}
else {
@ -1268,7 +1276,7 @@ namespace Mem {
for (const auto& mount : mem.disks_order) {
if (not disks.contains(mount)) continue;
if (cy > height - 3) break;
const auto& disk = disks.at(mount);
const auto& disk = safeVal(disks, mount);
if (disk.io_read.empty()) continue;
const string total = floating_humanizer(disk.total, not big_disk);
out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 8) + Mv::to(y+1+cy, x+cx + disks_width - total.size())
@ -1277,9 +1285,12 @@ namespace Mem {
const string used_percent = to_string(disk.used_percent);
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2) - 1) + hu_div + used_percent + '%' + hu_div;
}
if (io_graphs.contains(mount + "_activity")) {
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6)
+ Mv::l(disks_width - 6) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same) + Theme::c("main_fg");
}
if (++cy > height - 3) break;
if (not io_graphs.contains(mount)) continue;
if (io_graph_combined) {
auto comb_val = disk.io_read.back() + disk.io_write.back();
const string humanized = (disk.io_write.back() > 0 ? ""s : ""s) + (disk.io_read.back() > 0 ? ""s : ""s)
@ -1304,7 +1315,8 @@ namespace Mem {
for (const auto& mount : mem.disks_order) {
if (not disks.contains(mount)) continue;
if (cy > height - 3) break;
const auto& disk = disks.at(mount);
const auto& disk = safeVal(disks, mount);
if (disk.name.empty() or not disk_meters_used.contains(mount)) continue;
auto comb_val = (not disk.io_read.empty() ? disk.io_read.back() + disk.io_write.back() : 0ll);
const string human_io = (comb_val > 0 ? (disk.io_write.back() > 0 and big_disk ? ""s : ""s) + (disk.io_read.back() > 0 and big_disk ? ""s : ""s)
+ floating_humanizer(comb_val, true) : "");
@ -1328,7 +1340,7 @@ namespace Mem {
+ disk_meters_used.at(mount)(disk.used_percent) + rjust(human_used, (big_disk ? 9 : 5));
if (++cy > height - 3) break;
if (cmp_less_equal(disks.size() * 3 + (show_io_stat ? disk_ios : 0), height - 1)) {
if (disk_meters_free.contains(mount) and cmp_less_equal(disks.size() * 3 + (show_io_stat ? disk_ios : 0), height - 1)) {
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " Free:" + rjust(to_string(disk.free_percent) + '%', 4) : "F") + ' '
+ disk_meters_free.at(mount)(disk.free_percent) + rjust(human_free, (big_disk ? 9 : 5));
cy++;
@ -1352,8 +1364,9 @@ 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;
unordered_flat_map<string, Draw::Graph> graphs;
std::unordered_map<string, Draw::Graph> graphs;
string box;
string draw(const net_info& net, bool force_redraw, bool data_same) {
@ -1372,16 +1385,16 @@ 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 long long down_max = (net_auto ? graph_max.at("download") : ((long long)(Config::getI("net_download")) << 20) / 8);
const long long up_max = (net_auto ? graph_max.at("upload") : ((long long)(Config::getI("net_upload")) << 20) / 8);
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);
//* Redraw elements not needed to be updated every cycle
if (redraw) {
out = box;
//? Graphs
graphs.clear();
if (net.bandwidth.at("download").empty() or net.bandwidth.at("upload").empty())
if (safeVal(net.bandwidth, "download"s).empty() or safeVal(net.bandwidth, "upload"s).empty())
return out + Fx::reset;
graphs["download"] = Draw::Graph{
width - b_width - 2, u_graph_height, "download",
@ -1394,8 +1407,8 @@ namespace Net {
//? Interface selector and buttons
out += Mv::to(y, x+width - i_size - 9) + title_left + Fx::b + Theme::c("hi_fg") + "<b " + Theme::c("title")
+ uresize(selected_iface, 10) + Theme::c("hi_fg") + " n>" + title_right
+ Mv::to(y, x+width - i_size - 15) + title_left + Theme::c("hi_fg") + (net.stat.at("download").offset + net.stat.at("upload").offset > 0 ? Fx::b : "") + 'z'
+ 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};
Input::mouse_mappings["n"] = {y, x+width - 6, 1, 3};
@ -1419,13 +1432,13 @@ namespace Net {
//? Graphs and stats
int cy = 0;
for (const string dir : {"download", "upload"}) {
out += Mv::to(y+1 + (dir == "upload" ? u_graph_height : 0), x + 1) + graphs.at(dir)(net.bandwidth.at(dir), redraw or data_same or not net.connected)
out += Mv::to(y+1 + (dir == "upload" ? u_graph_height : 0), x + 1) + graphs.at(dir)(safeVal(net.bandwidth, dir), redraw or data_same or not net.connected)
+ Mv::to(y+1 + (dir == "upload" ? height - 3: 0), x + 1) + Fx::ub + Theme::c("graph_text")
+ floating_humanizer((dir == "upload" ? up_max : down_max), true);
const string speed = floating_humanizer(net.stat.at(dir).speed, false, 0, false, true);
const string speed_bits = (b_width >= 20 ? floating_humanizer(net.stat.at(dir).speed, false, 0, true, true) : "");
const string top = floating_humanizer(net.stat.at(dir).top, false, 0, true, true);
const string total = floating_humanizer(net.stat.at(dir).total);
const string speed = floating_humanizer(safeVal(net.stat, dir).speed, false, 0, false, true);
const string speed_bits = (b_width >= 20 ? floating_humanizer(safeVal(net.stat, dir).speed, false, 0, true, true) : "");
const string top = floating_humanizer(safeVal(net.stat, dir).top, false, 0, true, true);
const string total = floating_humanizer(safeVal(net.stat, dir).total);
const string symbol = (dir == "upload" ? "" : "");
out += Mv::to(b_y+1+cy, b_x+1) + Fx::ub + Theme::c("main_fg") + symbol + ' ' + ljust(speed, 10) + (b_width >= 20 ? rjust('(' + speed_bits + ')', 13) : "");
cy += (b_height == 5 ? 2 : 1);
@ -1453,9 +1466,9 @@ namespace Proc {
bool shown = true, redraw = true;
int selected_pid = 0, selected_depth = 0;
string selected_name;
unordered_flat_map<size_t, Draw::Graph> p_graphs;
unordered_flat_map<size_t, bool> p_wide_cmd;
unordered_flat_map<size_t, int> p_counters;
std::unordered_map<size_t, Draw::Graph> p_graphs;
std::unordered_map<size_t, bool> p_wide_cmd;
std::unordered_map<size_t, int> p_counters;
int counter = 0;
Draw::TextEdit filter;
Draw::Graph detailed_cpu_graph;
@ -1790,11 +1803,11 @@ namespace Proc {
p_counters[p.pid] = 0;
}
else if (p.cpu_p < 0.1 and ++p_counters[p.pid] >= 10) {
p_graphs.erase(p.pid);
if (p_graphs.contains(p.pid)) p_graphs.erase(p.pid);
p_counters.erase(p.pid);
}
else
p_counters.at(p.pid) = 0;
p_counters[p.pid] = 0;
}
out += Fx::reset;
@ -1918,25 +1931,18 @@ namespace Proc {
//? Clear out left over graphs from dead processes at a regular interval
if (not data_same and ++counter >= 100) {
counter = 0;
for (auto element = p_graphs.begin(); element != p_graphs.end();) {
if (rng::find(plist, element->first, &proc_info::pid) == plist.end()) {
element = p_graphs.erase(element);
p_counters.erase(element->first);
}
else
++element;
}
p_graphs.compact();
p_counters.compact();
for (auto element = p_wide_cmd.begin(); element != p_wide_cmd.end();) {
if (rng::find(plist, element->first, &proc_info::pid) == plist.end()) {
element = p_wide_cmd.erase(element);
}
else
++element;
}
p_wide_cmd.compact();
std::erase_if(p_graphs, [&](const auto& pair) {
return rng::find(plist, pair.first, &proc_info::pid) == plist.end();
});
std::erase_if(p_counters, [&](const auto& pair) {
return rng::find(plist, pair.first, &proc_info::pid) == plist.end();
});
std::erase_if(p_wide_cmd, [&](const auto& pair) {
return rng::find(plist, pair.first, &proc_info::pid) == plist.end();
});
}
if (selected == 0 and selected_pid != 0) {
@ -2008,7 +2014,7 @@ namespace Draw {
#ifdef GPU_SUPPORT
const bool show_gpu_on = Config::getS("show_gpu_info") == "On";
const bool gpus_shown_in_cpu_panel = Gpu::gpu_names.size() > 0 and (
show_gpu_on or (Config::getS("cpu_graph_lower") == "Auto" and Gpu::shown == 0)
show_gpu_on or (Config::getS("show_gpu_info") == "Auto" and Gpu::shown == 0)
);
const int gpus_height_offset = (Gpu::gpu_names.size() - Gpu::shown)*gpus_shown_in_cpu_panel;
int gpus_extra_height = gpus_shown_in_cpu_panel ? Gpu::gpu_names.size() - (show_gpu_on ? 0 : Gpu::shown) : 0;
@ -2236,3 +2242,4 @@ namespace Draw {
}
}
}

View file

@ -21,10 +21,9 @@ tab-size = 4
#include <string>
#include <vector>
#include <array>
#include <robin_hood.h>
#include <unordered_map>
#include <deque>
using robin_hood::unordered_flat_map;
using std::array;
using std::deque;
using std::string;
@ -66,8 +65,8 @@ namespace Draw {
//* An editable text field
class TextEdit {
size_t pos{}; // defaults to 0
size_t upos{}; // defaults to 0
size_t pos{};
size_t upos{};
bool numeric;
public:
string text;
@ -108,7 +107,7 @@ namespace Draw {
long long offset;
long long last = 0, max_value = 0;
bool current = true, tty_mode = false;
unordered_flat_map<bool, vector<string>> graphs = { {true, {}}, {false, {}}};
std::unordered_map<bool, vector<string>> graphs = { {true, {}}, {false, {}}};
//* Create two representations of the graph to switch between to represent two values for each braille character
void _create(const deque<long long>& data, int data_offset);
@ -135,6 +134,6 @@ namespace Draw {
namespace Proc {
extern Draw::TextEdit filter;
extern unordered_flat_map<size_t, Draw::Graph> p_graphs;
extern unordered_flat_map<size_t, int> p_counters;
extern std::unordered_map<size_t, Draw::Graph> p_graphs;
extern std::unordered_map<size_t, int> p_counters;
}

View file

@ -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;
@ -49,8 +39,9 @@ namespace rng = std::ranges;
namespace Input {
//* Map for translating key codes to readable values
const unordered_flat_map<string, string> Key_escapes = {
const std::unordered_map<string, string> Key_escapes = {
{"\033", "escape"},
{"\x12", "ctrl_r"},
{"\n", "enter"},
{" ", "space"},
{"\x7f", "backspace"},
@ -89,83 +80,45 @@ namespace Input {
{"[24~", "f12"}
};
std::atomic<bool> interrupt (false);
sigset_t signal_mask;
std::atomic<bool> polling (false);
array<int, 2> mouse_pos;
unordered_flat_map<string, Mouse_loc> mouse_mappings;
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();
}
std::string get() {
std::string res;
{
std::lock_guard<std::mutex> g(lock);
res.swap(current);
}
return res;
}
static InputThr& instance() {
// intentional memory leak, to simplify shutdown process
static InputThr* input = new InputThr();
return *input;
}
std::string current;
// TODO(pg83): use std::conditional_variable instead of sleep
std::mutex lock;
std::thread thr;
};
bool poll(int timeout) {
bool poll(const uint64_t 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;
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;
}
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 true;
}
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 +191,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
}
@ -304,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;

View file

@ -21,18 +21,17 @@ tab-size = 4
#include <string>
#include <atomic>
#include <array>
#include <robin_hood.h>
#include <unordered_map>
#include <deque>
using robin_hood::unordered_flat_map;
using std::array;
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
*/
@ -44,9 +43,11 @@ namespace Input {
};
//? line, col, height, width
extern unordered_flat_map<string, Mouse_loc> mouse_mappings;
extern std::unordered_map<string, Mouse_loc> mouse_mappings;
//* Signal mask used during polling read
extern sigset_t signal_mask;
extern atomic<bool> interrupt;
extern atomic<bool> polling;
//* Mouse column and line position
@ -55,8 +56,8 @@ namespace Input {
//* Last entered key
extern deque<string> history;
//* Poll keyboard & mouse input for <timeout> ms and return input availabilty as a bool
bool poll(int timeout=0);
//* Poll keyboard & mouse input for <timeout> ms and return input availability as a bool
bool poll(const uint64_t timeout=0);
//* Get a key or mouse action from input
string get();
@ -64,6 +65,9 @@ namespace Input {
//* Wait until input is available and return key
string wait();
//* Interrupt poll/wait
void interrupt();
//* Clears last entered key
void clear();

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#include <deque>
#include <robin_hood.h>
#include <unordered_map>
#include <array>
#include <signal.h>
#include <errno.h>
@ -31,7 +31,6 @@ tab-size = 4
#include "btop_draw.hpp"
#include "btop_shared.hpp"
using robin_hood::unordered_flat_map;
using std::array;
using std::ceil;
using std::max;
@ -50,8 +49,8 @@ namespace Menu {
bool redraw{true};
int currentMenu = -1;
msgBox messageBox;
int signalToSend{}; // defaults to 0
int signalKillRet{}; // defaults to 0
int signalToSend{};
int signalKillRet{};
const array<string, 32> P_Signals = {
"0",
@ -123,7 +122,7 @@ namespace Menu {
#endif
};
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
std::unordered_map<string, Input::Mouse_loc> mouse_mappings;
const array<array<string, 3>, 3> menu_normal = {
array<string, 3>{
@ -178,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."},
@ -354,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",
"",
@ -485,7 +490,7 @@ namespace Menu {
"Kelvin, 0 = absolute zero, 1 degree change",
"equals 1 degree change in Celsius.",
"",
"Rankine, 0 = abosulte zero, 1 degree change",
"Rankine, 0 = absolute zero, 1 degree change",
"equals 1 degree change in Fahrenheit."},
{"show_cpu_freq",
"Show CPU frequency.",
@ -646,7 +651,7 @@ namespace Menu {
"",
"Begin line with \"exclude=\" to change to",
"exclude filter.",
"Oterwise defaults to \"most include\" filter.",
"Otherwise defaults to \"most include\" filter.",
"",
"Example:",
"\"exclude=/boot /home/user\""},
@ -869,8 +874,8 @@ namespace Menu {
int signalChoose(const string& key) {
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
static int x{}; // defaults to 0
static int y{}; // defaults to 0
static int x{};
static int y{};
static int selected_signal = -1;
if (bg.empty()) selected_signal = -1;
@ -1071,8 +1076,8 @@ namespace Menu {
int mainMenu(const string& key) {
enum MenuItems { Options, Help, Quit };
static int y{}; // defaults to 0
static int selected{}; // defaults to 0
static int y{};
static int selected{};
static vector<string> colors_selected;
static vector<string> colors_normal;
auto tty_mode = Config::getB("tty_mode");
@ -1150,22 +1155,22 @@ namespace Menu {
int optionsMenu(const string& key) {
enum Predispositions { isBool, isInt, isString, is2D, isBrowseable, isEditable};
static int y{}; // defaults to 0
static int x{}; // defaults to 0
static int height{}; // defaults to 0
static int page{}; // defaults to 0
static int pages{}; // defaults to 0
static int selected{}; // defaults to 0
static int select_max{}; // defaults to 0
static int item_height{}; // defaults to 0
static int selected_cat{}; // defaults to 0
static int max_items{}; // defaults to 0
static int last_sel{}; // defaults to 0
static bool editing{}; // defaults to false
static int y{};
static int x{};
static int height{};
static int page{};
static int pages{};
static int selected{};
static int select_max{};
static int item_height{};
static int selected_cat{};
static int max_items{};
static int last_sel{};
static bool editing{};
static Draw::TextEdit editor;
static string warnings;
static bitset<8> selPred;
static const unordered_flat_map<string, std::reference_wrapper<const vector<string>>> optionsList = {
static const std::unordered_map<string, std::reference_wrapper<const vector<string>>> optionsList = {
{"color_theme", std::cref(Theme::themes)},
{"log_level", std::cref(Logger::log_levels)},
{"temp_scale", std::cref(Config::temp_scales)},
@ -1197,9 +1202,9 @@ namespace Menu {
Theme::updateThemes();
}
int retval = Changed;
bool recollect{}; // defaults to false
bool screen_redraw{}; // defaults to false
bool theme_refresh{}; // defaults to false
bool recollect{};
bool screen_redraw{};
bool theme_refresh{};
//? Draw background if needed else process input
if (redraw) {
@ -1501,11 +1506,11 @@ namespace Menu {
}
int helpMenu(const string& key) {
static int y{}; // defaults to 0
static int x{}; // defaults to 0
static int height{}; // defaults to 0
static int page{}; // defaults to 0
static int pages{}; // defaults to 0
static int y{};
static int x{};
static int height{};
static int page{};
static int pages{};
if (bg.empty()) page = 0;
int retval = Changed;

View file

@ -38,7 +38,7 @@ namespace Menu {
extern bool redraw;
//? line, col, height, width
extern unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
extern std::unordered_map<string, Input::Mouse_loc> mouse_mappings;
//* Creates a message box centered on screen
//? Height of box is determined by size of content vector
@ -46,12 +46,12 @@ namespace Menu {
//? Strings in content vector is not checked for box width overflow
class msgBox {
string box_contents, button_left, button_right;
int height{}; // defaults to 0
int width{}; // defaults to 0
int boxtype{}; // defaults to 0
int selected{}; // defaults to 0
int x{}; // defaults to 0
int y{}; // defaults to 0
int height{};
int width{};
int boxtype{};
int selected{};
int x{};
int y{};
public:
enum BoxTypes { OK, YES_NO, NO_YES };
enum msgReturn {

View file

@ -29,7 +29,7 @@ using namespace Tools;
namespace Gpu {
vector<string> gpu_names;
vector<int> gpu_b_height_offsets;
unordered_flat_map<string, deque<long long>> shared_gpu_percent = {
std::unordered_map<string, deque<long long>> shared_gpu_percent = {
{"gpu-average", {}},
{"gpu-vram-total", {}},
{"gpu-pwr-total", {}},

View file

@ -25,11 +25,19 @@ tab-size = 4
#include <string>
#include <tuple>
#include <vector>
#include <ifaddrs.h>
#include <robin_hood.h>
#include <unordered_map>
#include <unistd.h>
using robin_hood::unordered_flat_map;
// From `man 3 getifaddrs`: <net/if.h> must be included before <ifaddrs.h>
// clang-format off
#include <net/if.h>
#include <ifaddrs.h>
// clang-format on
#if defined(__FreeBSD__) || defined(__OpenBSD__)
# include <kvm.h>
#endif
using std::array;
using std::atomic;
using std::deque;
@ -55,6 +63,7 @@ namespace Global {
extern string overlay;
extern string clock;
extern uid_t real_uid, set_uid;
extern atomic<bool> init_conf;
}
namespace Runner {
@ -83,6 +92,15 @@ namespace Shared {
void init();
extern long coreCount, page_size, clk_tck;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct KvmDeleter {
void operator()(kvm_t* handle) {
kvm_close(handle);
}
};
using KvmPtr = std::unique_ptr<kvm_t, KvmDeleter>;
#endif
}
@ -98,7 +116,7 @@ namespace Gpu {
extern vector<int> gpu_b_height_offsets;
extern long long gpu_pwr_total_max;
extern unordered_flat_map<string, deque<long long>> shared_gpu_percent; // averages, power/vram total
extern std::unordered_map<string, deque<long long>> shared_gpu_percent; // averages, power/vram total
const array mem_names { "used"s, "free"s };
@ -124,7 +142,7 @@ namespace Gpu {
//* Per-device container for GPU info
struct gpu_info {
unordered_flat_map<string, deque<long long>> gpu_percent = {
std::unordered_map<string, deque<long long>> gpu_percent = {
{"gpu-totals", {}},
{"gpu-vram-totals", {}},
{"gpu-pwr-totals", {}},
@ -178,10 +196,10 @@ namespace Cpu {
extern string cpuName, cpuHz;
extern vector<string> available_fields;
extern vector<string> available_sensors;
extern tuple<int, long, string> current_bat;
extern tuple<int, float, long, string> current_bat;
struct cpu_info {
unordered_flat_map<string, deque<long long>> cpu_percent = {
std::unordered_map<string, deque<long long>> cpu_percent = {
{"total", {}},
{"user", {}},
{"nice", {}},
@ -207,13 +225,13 @@ namespace Cpu {
string draw(const cpu_info& cpu, const vector<Gpu::gpu_info>& gpu, bool force_redraw = false, bool data_same = false);
//* Parse /proc/cpu info for mapping of core ids
auto get_core_mapping() -> unordered_flat_map<int, int>;
extern unordered_flat_map<int, int> core_mapping;
auto get_core_mapping() -> std::unordered_map<int, int>;
extern std::unordered_map<int, int> core_mapping;
auto get_cpuHz() -> string;
//* Get battery info from /sys
auto get_battery() -> tuple<int, long, string>;
auto get_battery() -> tuple<int, float, long, string>;
}
namespace Mem {
@ -229,11 +247,11 @@ namespace Mem {
string name;
string fstype{}; // defaults to ""
std::filesystem::path stat{}; // defaults to ""
int64_t total{}; // defaults to 0
int64_t used{}; // defaults to 0
int64_t free{}; // defaults to 0
int used_percent{}; // defaults to 0
int free_percent{}; // defaults to 0
int64_t total{};
int64_t used{};
int64_t free{};
int used_percent{};
int free_percent{};
array<int64_t, 3> old_io = {0, 0, 0};
deque<long long> io_read = {};
@ -242,13 +260,13 @@ namespace Mem {
};
struct mem_info {
unordered_flat_map<string, uint64_t> stats =
std::unordered_map<string, uint64_t> stats =
{{"used", 0}, {"available", 0}, {"cached", 0}, {"free", 0},
{"swap_total", 0}, {"swap_used", 0}, {"swap_free", 0}};
unordered_flat_map<string, deque<long long>> percent =
std::unordered_map<string, deque<long long>> percent =
{{"used", {}}, {"available", {}}, {"cached", {}}, {"free", {}},
{"swap_total", {}}, {"swap_used", {}}, {"swap_free", {}}};
unordered_flat_map<string, disk_info> disks;
std::unordered_map<string, disk_info> disks;
vector<string> disks_order;
};
@ -270,26 +288,37 @@ namespace Net {
extern string selected_iface;
extern vector<string> interfaces;
extern bool rescale;
extern unordered_flat_map<string, uint64_t> graph_max;
extern std::unordered_map<string, uint64_t> graph_max;
struct net_stat {
uint64_t speed{}; // defaults to 0
uint64_t top{}; // defaults to 0
uint64_t total{}; // defaults to 0
uint64_t last{}; // defaults to 0
uint64_t offset{}; // defaults to 0
uint64_t rollover{}; // defaults to 0
uint64_t speed{};
uint64_t top{};
uint64_t total{};
uint64_t last{};
uint64_t offset{};
uint64_t rollover{};
};
struct net_info {
unordered_flat_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
unordered_flat_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
string ipv4{}; // defaults to ""
string ipv6{}; // defaults to ""
bool connected{}; // defaults to false
bool connected{};
};
extern unordered_flat_map<string, net_info> current_net;
class IfAddrsPtr {
struct ifaddrs* ifaddr;
int status;
public:
IfAddrsPtr() { status = getifaddrs(&ifaddr); }
~IfAddrsPtr() { freeifaddrs(ifaddr); }
[[nodiscard]] constexpr auto operator()() -> struct ifaddrs* { return ifaddr; }
[[nodiscard]] constexpr auto get() -> struct ifaddrs* { return ifaddr; }
[[nodiscard]] constexpr auto get_status() const noexcept -> int { return status; };
};
extern std::unordered_map<string, net_info> current_net;
//* Collect net upload/download stats
auto collect(bool no_update=false) -> net_info&;
@ -322,7 +351,7 @@ namespace Proc {
};
//? Translation from process state char to explanative string
const unordered_flat_map<char, string> proc_states = {
const std::unordered_map<char, string> proc_states = {
{'R', "Running"},
{'S', "Sleeping"},
{'D', "Waiting"},
@ -338,32 +367,32 @@ namespace Proc {
//* Container for process information
struct proc_info {
size_t pid{}; // defaults to 0
size_t pid{};
string name{}; // defaults to ""
string cmd{}; // defaults to ""
string short_cmd{}; // defaults to ""
size_t threads{}; // defaults to 0
int name_offset{}; // defaults to 0
size_t threads{};
int name_offset{};
string user{}; // defaults to ""
uint64_t mem{}; // defaults to 0
uint64_t mem{};
double cpu_p{}; // defaults to = 0.0
double cpu_c{}; // defaults to = 0.0
char state = '0';
int64_t p_nice{}; // defaults to 0
uint64_t ppid{}; // defaults to 0
uint64_t cpu_s{}; // defaults to 0
uint64_t cpu_t{}; // defaults to 0
int64_t p_nice{};
uint64_t ppid{};
uint64_t cpu_s{};
uint64_t cpu_t{};
string prefix{}; // defaults to ""
size_t depth{}; // defaults to 0
size_t tree_index{}; // defaults to 0
bool collapsed{}; // defaults to false
bool filtered{}; // defaults to false
size_t depth{};
size_t tree_index{};
bool collapsed{};
bool filtered{};
};
//* Container for process info box
struct detail_container {
size_t last_pid{}; // defaults to 0
bool skip_smaps{}; // defaults to false
size_t last_pid{};
bool skip_smaps{};
proc_info entry;
string elapsed, parent, status, io_read, io_write, memory;
long long first_mem = -1;

View file

@ -42,11 +42,11 @@ namespace Theme {
fs::path theme_dir;
fs::path user_theme_dir;
vector<string> themes;
unordered_flat_map<string, string> colors;
unordered_flat_map<string, array<int, 3>> rgbs;
unordered_flat_map<string, array<string, 101>> gradients;
std::unordered_map<string, string> colors;
std::unordered_map<string, array<int, 3>> rgbs;
std::unordered_map<string, array<string, 101>> gradients;
const unordered_flat_map<string, string> Default_theme = {
const std::unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
@ -91,7 +91,7 @@ namespace Theme {
{ "process_end", "#d45454" }
};
const unordered_flat_map<string, string> TTY_theme = {
const std::unordered_map<string, string> TTY_theme = {
{ "main_bg", "\x1b[0;40m" },
{ "main_fg", "\x1b[37m" },
{ "title", "\x1b[97m" },
@ -224,7 +224,7 @@ namespace Theme {
}
//* Generate colors and rgb decimal vectors for the theme
void generateColors(const unordered_flat_map<string, string>& source) {
void generateColors(const std::unordered_map<string, string>& source) {
vector<string> t_rgb;
string depth;
bool t_to_256 = Config::getB("lowcolor");
@ -372,7 +372,7 @@ namespace Theme {
//* Load a .theme file from disk
auto loadFile(const string& filename) {
unordered_flat_map<string, string> theme_out;
std::unordered_map<string, string> theme_out;
const fs::path filepath = filename;
if (not fs::exists(filepath))
return Default_theme;

View file

@ -22,18 +22,17 @@ tab-size = 4
#include <filesystem>
#include <string>
#include <vector>
#include <robin_hood.h>
#include <unordered_map>
using std::array;
using std::string;
using std::vector;
using robin_hood::unordered_flat_map;
namespace Theme {
extern std::filesystem::path theme_dir;
extern std::filesystem::path user_theme_dir;
//* Contains "Default" and "TTY" at indeces 0 and 1, otherwise full paths to theme files
//* Contains "Default" and "TTY" at indices 0 and 1, otherwise full paths to theme files
extern vector<string> themes;
//* Generate escape sequence for 24-bit or 256 color and return as a string
@ -54,9 +53,9 @@ namespace Theme {
//* Set current theme from current "color_theme" value in config
void setTheme();
extern unordered_flat_map<string, string> colors;
extern unordered_flat_map<string, array<int, 3>> rgbs;
extern unordered_flat_map<string, array<string, 101>> gradients;
extern std::unordered_map<string, string> colors;
extern std::unordered_map<string, array<int, 3>> rgbs;
extern std::unordered_map<string, array<string, 101>> gradients;
//* Return escape code for color <name>
inline const string& c(const string& name) { return colors.at(name); }

View file

@ -26,24 +26,23 @@ tab-size = 4
#include <utility>
#include <ranges>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include "robin_hood.h"
#include "unordered_map"
#include "widechar_width.hpp"
#include "btop_shared.hpp"
#include "btop_tools.hpp"
#include "btop_config.hpp"
using std::cin;
using std::cout;
using std::floor;
using std::flush;
using std::max;
using std::string_view;
using std::to_string;
using robin_hood::unordered_flat_map;
using namespace std::literals; // to use operator""s
@ -55,9 +54,9 @@ namespace rng = std::ranges;
//* Collection of escape codes and functions for terminal manipulation
namespace Term {
atomic<bool> initialized{}; // defaults to false
atomic<int> width{}; // defaults to 0
atomic<int> height{}; // defaults to 0
atomic<bool> initialized{};
atomic<int> width{};
atomic<int> height{};
string current_tty;
namespace {
@ -77,7 +76,11 @@ namespace Term {
struct termios settings;
if (tcgetattr(STDIN_FILENO, &settings)) return false;
if (on) settings.c_lflag |= ICANON;
else settings.c_lflag &= ~(ICANON);
else {
settings.c_lflag &= ~(ICANON);
settings.c_cc[VMIN] = 0;
settings.c_cc[VTIME] = 0;
}
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
if (on) setlinebuf(stdin);
else setbuf(stdin, nullptr);
@ -86,12 +89,27 @@ namespace Term {
}
bool refresh(bool only_check) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) return false;
if (width != w.ws_col or height != w.ws_row) {
// Query dimensions of '/dev/tty' of the 'STDOUT_FILENO' isn't available.
// This variable is set in those cases to avoid calls to ioctl
constinit static bool uses_dev_tty = false;
struct winsize wsize {};
if (uses_dev_tty || ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsize) < 0 || (wsize.ws_col == 0 && wsize.ws_row == 0)) {
Logger::error(R"(Couldn't determine terminal size of "STDOUT_FILENO"!)");
auto dev_tty = open("/dev/tty", O_RDONLY);
if (dev_tty != -1) {
ioctl(dev_tty, TIOCGWINSZ, &wsize);
close(dev_tty);
}
else {
Logger::error(R"(Couldn't determine terminal size of "/dev/tty"!)");
return false;
}
uses_dev_tty = true;
}
if (width != wsize.ws_col or height != wsize.ws_row) {
if (not only_check) {
width = w.ws_col;
height = w.ws_row;
width = wsize.ws_col;
height = wsize.ws_row;
}
return true;
}
@ -135,12 +153,12 @@ namespace Term {
tcgetattr(STDIN_FILENO, &initial_settings);
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
//? Disable stream sync
cin.sync_with_stdio(false);
//? Disable stream sync - this does not seem to work on OpenBSD
#ifndef __OpenBSD__
cout.sync_with_stdio(false);
#endif
//? Disable stream ties
cin.tie(nullptr);
cout.tie(nullptr);
echo(false);
linebuffered(false);
@ -626,7 +644,7 @@ namespace Logger {
size_t loglevel;
fs::path logfile;
//* Wrapper for lowering priviliges if using SUID bit and currently isn't using real userid
//* Wrapper for lowering privileges if using SUID bit and currently isn't using real userid
class lose_priv {
int status = -1;
public:
@ -652,6 +670,7 @@ namespace Logger {
lose_priv neutered{};
std::error_code ec;
try {
// NOTE: `exist()` could throw but since we return with an empty logfile we don't care
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
auto old_log = logfile;
old_log += ".1";

View file

@ -18,6 +18,10 @@ tab-size = 4
#pragma once
#if !defined(NDEBUG)
# define BTOP_DEBUG
#endif
#include <algorithm> // for std::ranges::count_if
#include <array>
#include <atomic>
@ -31,6 +35,10 @@ tab-size = 4
#include <vector>
#include <pthread.h>
#include <limits.h>
#include <unordered_map>
#ifdef BTOP_DEBUG
#include <source_location>
#endif
#ifndef HOST_NAME_MAX
#ifdef __APPLE__
#define HOST_NAME_MAX 255
@ -38,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;
@ -108,7 +114,7 @@ namespace Mv {
//* Save cursor position
const string save = Fx::e + "s";
//* Restore saved cursor postion
//* Restore saved cursor position
const string restore = Fx::e + "u";
}
@ -146,6 +152,35 @@ namespace Term {
void restore();
}
//* Simple logging implementation
namespace Logger {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
};
extern std::filesystem::path logfile;
enum Level : size_t {
DISABLED = 0,
ERROR = 1,
WARNING = 2,
INFO = 3,
DEBUG = 4,
};
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void set(const string& level);
void log_write(const Level level, const string& msg);
inline void error(const string msg) { log_write(ERROR, msg); }
inline void warning(const string msg) { log_write(WARNING, msg); }
inline void info(const string msg) { log_write(INFO, msg); }
inline void debug(const string msg) { log_write(DEBUG, msg); }
}
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Tools {
@ -251,7 +286,7 @@ namespace Tools {
return is_in(str, "true", "True");
}
//* Check if a string is a valid integer value (only postive)
//* Check if a string is a valid integer value (only positive)
inline bool isint(const string& str) {
return all_of(str.begin(), str.end(), ::isdigit);
}
@ -304,6 +339,50 @@ namespace Tools {
//* Add std::string operator * : Repeat string <str> <n> number of times
std::string operator*(const string& str, int64_t n);
template <typename K, typename T>
#ifdef BTOP_DEBUG
const T& safeVal(const std::unordered_map<K, T>& map, const K& key, const T& fallback = T{}, std::source_location loc = std::source_location::current()) {
if (map.contains(key)) {
return map.at(key);
} else {
Logger::error(fmt::format("safeVal() called with invalid key: [{}] in file: {} on line: {}", key, loc.file_name(), loc.line()));
return fallback;
}
};
#else
const T& safeVal(const std::unordered_map<K, T>& map, const K& key, const T& fallback = T{}) {
if (map.contains(key)) {
return map.at(key);
} else {
Logger::error(fmt::format("safeVal() called with invalid key: [{}] (Compile btop with DEBUG=true for more extensive logging!)", key));
return fallback;
}
};
#endif
template <typename T>
#ifdef BTOP_DEBUG
const T& safeVal(const std::vector<T>& vec, const size_t& index, const T& fallback = T{}, std::source_location loc = std::source_location::current()) {
if (index < vec.size()) {
return vec.at(index);
} else {
Logger::error(fmt::format("safeVal() called with invalid index: [{}] in file: {} on line: {}", index, loc.file_name(), loc.line()));
return fallback;
}
};
#else
const T& safeVal(const std::vector<T>& vec, const size_t& index, const T& fallback = T{}) {
if (index < vec.size()) {
return vec.at(index);
} else {
Logger::error(fmt::format("safeVal() called with invalid index: [{}] (Compile btop with DEBUG=true for more extensive logging!)", index));
return fallback;
}
};
#endif
//* Return current time in <strf> format
string strf_time(const string& strf);
@ -329,7 +408,7 @@ namespace Tools {
//* Sets atomic<bool> to true on construct, sets to false on destruct
class atomic_lock {
atomic<bool>& atom;
bool not_true{}; // defaults to false
bool not_true{};
public:
atomic_lock(atomic<bool>& atom, bool wait = false);
~atomic_lock();
@ -342,35 +421,6 @@ namespace Tools {
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string>;
}
//* Simple logging implementation
namespace Logger {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
};
extern std::filesystem::path logfile;
enum Level : size_t {
DISABLED = 0,
ERROR = 1,
WARNING = 2,
INFO = 3,
DEBUG = 4,
};
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void set(const string& level);
void log_write(const Level level, const string& msg);
inline void error(const string msg) { log_write(ERROR, msg); }
inline void warning(const string msg) { log_write(WARNING, msg); }
inline void info(const string msg) { log_write(INFO, msg); }
inline void debug(const string msg) { log_write(DEBUG, msg); }
}
namespace Tools {
//* Creates a named timer that is started on construct (by default) and reports elapsed time in microseconds to Logger::debug() on destruct if running
//* Unless delayed_report is set to false, all reporting is buffered and delayed until DebugTimer is destructed or .force_report() is called

10
src/config.h.in Normal file
View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string_view>
constexpr std::string_view GIT_COMMIT = "@GIT_COMMIT@";
constexpr std::string_view COMPILER = "@COMPILER@";
constexpr std::string_view COMPILER_VERSION = "@COMPILER_VERSION@";
constexpr std::string_view CONFIGURE_COMMAND = "@CONFIGURE_COMMAND@";

View file

@ -58,6 +58,7 @@ tab-size = 4
#include <regex>
#include <string>
#include <memory>
#include <utility>
#include "../btop_config.hpp"
#include "../btop_shared.hpp"
@ -98,7 +99,7 @@ namespace Cpu {
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
} // namespace Cpu
namespace Mem {
@ -169,42 +170,36 @@ namespace Shared {
Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
Cpu::core_old_idles.insert(Cpu::core_old_idles.begin(), Shared::coreCount, 0);
Logger::debug("Init -> Cpu::collect()");
Cpu::collect();
for (auto &[field, vec] : Cpu::current_cpu.cpu_percent) {
if (not vec.empty() and not v_contains(Cpu::available_fields, field)) Cpu::available_fields.push_back(field);
}
Logger::debug("Init -> Cpu::get_cpuName()");
Cpu::cpuName = Cpu::get_cpuName();
Logger::debug("Init -> Cpu::get_sensors()");
Cpu::got_sensors = Cpu::get_sensors();
Logger::debug("Init -> Cpu::get_core_mapping()");
Cpu::core_mapping = Cpu::get_core_mapping();
//? Init for namespace Mem
Mem::old_uptime = system_uptime();
Logger::debug("Init -> Mem::collect()");
Mem::collect();
Logger::debug("Init -> Mem::get_zpools()");
Mem::get_zpools();
}
//* RAII wrapper for kvm_openfiles
class kvm_openfiles_wrapper {
kvm_t* kd = nullptr;
public:
kvm_openfiles_wrapper(const char* execf, const char* coref, const char* swapf, int flags, char* err) {
this->kd = kvm_openfiles(execf, coref, swapf, flags, err);
}
~kvm_openfiles_wrapper() { kvm_close(kd); }
auto operator()() -> kvm_t* { return kd; }
};
} // namespace Shared
namespace Cpu {
string cpuName;
string cpuHz;
bool has_battery = true;
tuple<int, long, string> current_bat;
tuple<int, float, long, string> current_bat;
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -270,7 +265,7 @@ namespace Cpu {
got_sensors = true;
int temp;
size_t size = sizeof(temp);
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, nullptr, 0); //asuming the max temp is same for all cores
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, nullptr, 0); //assuming the max temp is same for all cores
temp = (temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
current_cpu.temp_max = temp;
}
@ -323,8 +318,8 @@ namespace Cpu {
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
for (long i = 0; i < Shared::coreCount; i++) {
@ -366,10 +361,11 @@ namespace Cpu {
return core_map;
}
auto get_battery() -> tuple<int, long, string> {
if (not has_battery) return {0, 0, ""};
auto get_battery() -> tuple<int, float, long, string> {
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";
@ -381,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) {
@ -395,7 +395,7 @@ namespace Cpu {
}
}
return {percent, seconds, status};
return {percent, watts, seconds, status};
}
auto collect(bool no_update) -> cpu_info & {
@ -557,7 +557,7 @@ namespace Mem {
}
}
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
void collect_disk(std::unordered_map<string, disk_info> &disks, std::unordered_map<string, string> &mapping) {
// this bit is for 'regular' mounts
static struct statinfo cur;
long double etime = 0;
@ -572,7 +572,7 @@ namespace Mem {
auto d = cur.dinfo->devices[i];
string devStatName = "/dev/" + string(d.device_name) + std::to_string(d.unit_number);
for (auto& [ignored, disk] : disks) { // find matching mountpoints - could be multiple as d.device_name is only ada (and d.unit_number is the device number), while the disk.dev is like /dev/ada0s1
if (disk.dev.string().rfind(devStatName, 0) == 0) {
if (disk.dev.string().rfind(devStatName, 0) == 0 and mapping.contains(disk.dev)) {
devstat_compute_statistics(&d, nullptr, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
assign_values(disk, total_bytes_read, total_bytes_write);
string mountpoint = mapping.at(disk.dev);
@ -581,7 +581,6 @@ namespace Mem {
}
}
Logger::debug("");
}
// this code is for ZFS mounts
@ -662,9 +661,9 @@ namespace Mem {
if (show_swap) {
char buf[_POSIX2_LINE_MAX];
Shared::kvm_openfiles_wrapper kd(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf);
Shared::KvmPtr kd {kvm_openfiles(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf)};
struct kvm_swap swap[16];
int nswap = kvm_getswapinfo(kd(), swap, 16, 0);
int nswap = kvm_getswapinfo(kd.get(), swap, 16, 0);
int totalSwap = 0, usedSwap = 0;
for (int i = 0; i < nswap; i++) {
totalSwap += swap[i].ksw_total;
@ -691,7 +690,7 @@ namespace Mem {
}
if (show_disks) {
unordered_flat_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
std::unordered_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
double uptime = system_uptime();
auto &disks_filter = Config::getS("disks_filter");
bool filter_exclude = false;
@ -807,27 +806,16 @@ namespace Mem {
} // namespace Mem
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors = 0;
unordered_flat_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
unordered_flat_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
bool rescale = true;
uint64_t timestamp = 0;
//* RAII wrapper for getifaddrs
class getifaddr_wrapper {
struct ifaddrs *ifaddr;
public:
int status;
getifaddr_wrapper() { status = getifaddrs(&ifaddr); }
~getifaddr_wrapper() { freeifaddrs(ifaddr); }
auto operator()() -> struct ifaddrs * { return ifaddr; }
};
auto collect(bool no_update) -> net_info & {
auto &net = current_net;
auto &config_iface = Config::getS("net_iface");
@ -837,10 +825,10 @@ namespace Net {
if (not no_update and errors < 3) {
//? Get interface list using getifaddrs() wrapper
getifaddr_wrapper if_wrap{};
if (if_wrap.status != 0) {
IfAddrsPtr if_addrs {};
if (if_addrs.get_status() != 0) {
errors++;
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_wrap.status));
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_addrs.get_status()));
redraw = true;
return empty_net;
}
@ -852,7 +840,7 @@ namespace Net {
string ipv4, ipv6;
//? Iteration over all items in getifaddrs() list
for (auto *ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
for (auto *ifa = if_addrs.get(); ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) continue;
family = ifa->ifa_addr->sa_family;
const auto &iface = ifa->ifa_name;
@ -892,7 +880,7 @@ namespace Net {
} //else, ignoring family==AF_LINK (see man 3 getifaddrs)
}
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
std::unordered_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
size_t len;
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
@ -919,7 +907,7 @@ namespace Net {
}
}
//? Get total recieved and transmitted bytes + device address if no ip was found
//? Get total received and transmitted bytes + device address if no ip was found
for (const auto &iface : interfaces) {
for (const string dir : {"download", "upload"}) {
auto &saved_stat = net.at(iface).stat.at(dir);
@ -966,7 +954,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -1037,7 +1024,7 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev = false;
@ -1159,8 +1146,8 @@ namespace Proc {
int count = 0;
char buf[_POSIX2_LINE_MAX];
Shared::kvm_openfiles_wrapper kd(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf);
const struct kinfo_proc* kprocs = kvm_getprocs(kd(), KERN_PROC_PROC, 0, &count);
Shared::KvmPtr kd {kvm_openfiles(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf)};
const struct kinfo_proc* kprocs = kvm_getprocs(kd.get(), KERN_PROC_PROC, 0, &count);
for (int i = 0; i < count; i++) {
const struct kinfo_proc* kproc = &kprocs[i];
@ -1187,7 +1174,7 @@ namespace Proc {
continue;
}
new_proc.name = kproc->ki_comm;
char** argv = kvm_getargv(kd(), kproc, 0);
char** argv = kvm_getargv(kd.get(), kproc, 0);
if (argv) {
for (int i = 0; argv[i] and cmp_less(new_proc.cmd.size(), 1000); i++) {
new_proc.cmd += argv[i] + " "s;

View file

@ -17,7 +17,8 @@ tab-size = 4
*/
#include <cstdlib>
#include <robin_hood.h>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <ranges>
#include <cmath>
@ -31,6 +32,8 @@ tab-size = 4
#include <filesystem>
#include <future>
#include <dlfcn.h>
#include <unordered_map>
#include <utility>
#if defined(RSMI_STATIC)
#include <rocm_smi/rocm_smi.h>
@ -74,8 +77,8 @@ namespace Cpu {
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
bool got_sensors{}; // defaults to false
bool cpu_temp_only{}; // defaults to false
bool got_sensors{};
bool cpu_temp_only{};
//* Populate found_sensors map
bool get_sensors();
@ -89,15 +92,15 @@ namespace Cpu {
struct Sensor {
fs::path path;
string label;
int64_t temp{}; // defaults to 0
int64_t high{}; // defaults to 0
int64_t crit{}; // defaults to 0
int64_t temp{};
int64_t high{};
int64_t crit{};
};
unordered_flat_map<string, Sensor> found_sensors;
std::unordered_map<string, Sensor> found_sensors;
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
}
namespace Gpu {
@ -157,37 +160,44 @@ namespace Gpu {
namespace Rsmi {
#if !defined(RSMI_STATIC)
//? RSMI defines, structs & typedefs
#define RSMI_MAX_NUM_FREQUENCIES 32
#define RSMI_STATUS_SUCCESS 0
#define RSMI_MEM_TYPE_VRAM 0
#define RSMI_TEMP_CURRENT 0
#define RSMI_TEMP_TYPE_EDGE 0
#define RSMI_CLK_TYPE_MEM 4
#define RSMI_CLK_TYPE_SYS 0
#define RSMI_TEMP_MAX 1
#define RSMI_MAX_NUM_FREQUENCIES_V5 32
#define RSMI_MAX_NUM_FREQUENCIES_V6 33
#define RSMI_STATUS_SUCCESS 0
#define RSMI_MEM_TYPE_VRAM 0
#define RSMI_TEMP_CURRENT 0
#define RSMI_TEMP_TYPE_EDGE 0
#define RSMI_CLK_TYPE_MEM 4
#define RSMI_CLK_TYPE_SYS 0
#define RSMI_TEMP_MAX 1
typedef int rsmi_status_t,
rsmi_temperature_metric_t,
rsmi_clk_type_t,
rsmi_memory_type_t;
struct rsmi_frequencies_t {uint32_t num_supported, current, frequency[RSMI_MAX_NUM_FREQUENCIES];};
struct rsmi_version_t {uint32_t major, minor, patch; const char* build;};
struct rsmi_frequencies_t_v5 {uint32_t num_supported, current; uint64_t frequency[RSMI_MAX_NUM_FREQUENCIES_V5];};
struct rsmi_frequencies_t_v6 {bool has_deep_sleep; uint32_t num_supported, current; uint64_t frequency[RSMI_MAX_NUM_FREQUENCIES_V6];};
//? Function pointers
rsmi_status_t (*rsmi_init)(uint64_t);
rsmi_status_t (*rsmi_shut_down)();
rsmi_status_t (*rsmi_version_get)(rsmi_version_t*);
rsmi_status_t (*rsmi_num_monitor_devices)(uint32_t*);
rsmi_status_t (*rsmi_dev_name_get)(uint32_t, char*, size_t);
rsmi_status_t (*rsmi_dev_power_cap_get)(uint32_t, uint32_t, uint64_t*);
rsmi_status_t (*rsmi_dev_temp_metric_get)(uint32_t, uint32_t, rsmi_temperature_metric_t, int64_t*);
rsmi_status_t (*rsmi_dev_busy_percent_get)(uint32_t, uint32_t*);
rsmi_status_t (*rsmi_dev_memory_busy_percent_get)(uint32_t, uint32_t*);
rsmi_status_t (*rsmi_dev_gpu_clk_freq_get)(uint32_t, rsmi_clk_type_t, rsmi_frequencies_t*);
rsmi_status_t (*rsmi_dev_gpu_clk_freq_get_v5)(uint32_t, rsmi_clk_type_t, rsmi_frequencies_t_v5*);
rsmi_status_t (*rsmi_dev_gpu_clk_freq_get_v6)(uint32_t, rsmi_clk_type_t, rsmi_frequencies_t_v6*);
rsmi_status_t (*rsmi_dev_power_ave_get)(uint32_t, uint32_t, uint64_t*);
rsmi_status_t (*rsmi_dev_memory_total_get)(uint32_t, rsmi_memory_type_t, uint64_t*);
rsmi_status_t (*rsmi_dev_memory_usage_get)(uint32_t, rsmi_memory_type_t, uint64_t*);
rsmi_status_t (*rsmi_dev_pci_throughput_get)(uint32_t, uint64_t*, uint64_t*, uint64_t*);
uint32_t version_major = 0;
//? Data
void* rsmi_dl_handle;
#endif
@ -291,14 +301,14 @@ namespace Cpu {
string cpuName;
string cpuHz;
bool has_battery = true;
tuple<int, long, string> current_bat;
tuple<int, float, long, string> current_bat;
const array time_names {
"user"s, "nice"s, "system"s, "idle"s, "iowait"s,
"irq"s, "softirq"s, "steal"s, "guest"s, "guest_nice"s
};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -539,14 +549,14 @@ namespace Cpu {
}
string get_cpuHz() {
static int failed{}; // defaults to 0
static int failed{};
if (failed > 4)
return ""s;
string cpuhz;
try {
double hz{}; // defaults to 0.0
double hz{};
//? Try to get freq from /sys/devices/system/cpu/cpufreq/policy first (faster)
if (not freq_path.empty()) {
hz = stod(readfile(freq_path, "0.0")) / 1000;
@ -580,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) {
@ -595,16 +605,16 @@ namespace Cpu {
return cpuhz;
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
//? Try to get core mapping from /proc/cpuinfo
ifstream cpuinfo(Shared::procPath / "cpuinfo");
if (cpuinfo.good()) {
int cpu{}; // defaults to 0
int core{}; // defaults to 0
int n{}; // defaults to 0
int cpu{};
int core{};
int n{};
for (string instr; cpuinfo >> instr;) {
if (instr == "processor") {
cpuinfo.ignore(SSmax, ':');
@ -661,15 +671,16 @@ 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<int, long, string> {
if (not has_battery) return {0, 0, ""};
auto get_battery() -> tuple<int, float, long, string> {
if (not has_battery) return {0, 0, 0, ""};
static string auto_sel;
static unordered_flat_map<string, battery> batteries;
static std::unordered_map<string, battery> batteries;
//? Get paths to needed files and check for valid values on first run
if (batteries.empty() and has_battery) {
@ -699,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";
@ -726,7 +745,7 @@ namespace Cpu {
}
if (batteries.empty()) {
has_battery = false;
return {0, 0, ""};
return {0, 0, 0, ""};
}
}
@ -746,15 +765,9 @@ namespace Cpu {
int percent = -1;
long seconds = -1;
float watts = -1;
//? Try to get battery percentage
if (b.use_energy) {
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) {
try {
percent = stoll(readfile(b.base_dir / "capacity", "-1"));
@ -762,9 +775,23 @@ namespace Cpu {
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 (b.use_energy_or_charge and percent < 0) {
try {
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
@ -778,13 +805,23 @@ namespace Cpu {
//? Get seconds to empty
if (not is_in(status, "charging", "full")) {
if (b.use_energy and not b.power_now.empty()) {
try {
seconds = round((double)stoll(readfile(b.energy_now, "0")) / stoll(readfile(b.power_now, "1")) * 3600);
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&) { }
}
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 = stoll(readfile(b.base_dir / "time_to_empty", "0")) * 60;
@ -794,7 +831,26 @@ namespace Cpu {
}
}
return {percent, seconds, status};
//? Get power draw
if (b.use_power) {
if (not b.power_now.empty()) {
try {
watts = (float)stoll(readfile(b.power_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, watts, seconds, status};
}
auto collect(bool no_update) -> cpu_info& {
@ -946,16 +1002,27 @@ namespace Gpu {
if (initialized) return false;
//? Dynamic loading & linking
nvml_dl_handle = dlopen("libnvidia-ml.so", RTLD_LAZY);
if (!nvml_dl_handle) {
Logger::info(std::string("Failed to load libnvidia-ml.so, NVIDIA GPUs will not be detected: ") + dlerror());
return false;
//? Try possible library names for libnvidia-ml.so
const array libNvAlts = {
"libnvidia-ml.so",
"libnvidia-ml.so.1",
};
for (const auto& l : libNvAlts) {
nvml_dl_handle = dlopen(l, RTLD_LAZY);
if (nvml_dl_handle != nullptr) {
break;
}
}
if (!nvml_dl_handle) {
Logger::info("Failed to load libnvidia-ml.so, NVIDIA GPUs will not be detected: "s + dlerror());
return false;
}
auto load_nvml_sym = [&](const char sym_name[]) {
auto sym = dlsym(nvml_dl_handle, sym_name);
auto err = dlerror();
if (err != NULL) {
if (err != nullptr) {
Logger::error(string("NVML: Couldn't find function ") + sym_name + ": " + err);
return (void*)nullptr;
} else return sym;
@ -1211,19 +1278,32 @@ namespace Gpu {
//? Dynamic loading & linking
#if !defined(RSMI_STATIC)
rsmi_dl_handle = dlopen("/opt/rocm/lib/librocm_smi64.so", RTLD_LAZY); // first try /lib and /usr/lib, then /opt/rocm/lib if that fails
if (dlerror() != NULL) {
rsmi_dl_handle = dlopen("librocm_smi64.so", RTLD_LAZY);
}
//? Try possible library paths and names for librocm_smi64.so
const array libRocAlts = {
"/opt/rocm/lib/librocm_smi64.so",
"librocm_smi64.so",
"librocm_smi64.so.5", // fedora
"librocm_smi64.so.1.0", // debian
"librocm_smi64.so.6"
};
for (const auto& l : libRocAlts) {
rsmi_dl_handle = dlopen(l, RTLD_LAZY);
if (rsmi_dl_handle != nullptr) {
break;
}
}
if (!rsmi_dl_handle) {
Logger::debug(std::string("Failed to load librocm_smi64.so, AMD GPUs will not be detected: ") + dlerror());
Logger::info("Failed to load librocm_smi64.so, AMD GPUs will not be detected: "s + dlerror());
return false;
}
auto load_rsmi_sym = [&](const char sym_name[]) {
auto sym = dlsym(rsmi_dl_handle, sym_name);
auto err = dlerror();
if (err != NULL) {
if (err != nullptr) {
Logger::error(string("ROCm SMI: Couldn't find function ") + sym_name + ": " + err);
return (void*)nullptr;
} else return sym;
@ -1233,13 +1313,13 @@ namespace Gpu {
LOAD_SYM(rsmi_init);
LOAD_SYM(rsmi_shut_down);
LOAD_SYM(rsmi_version_get);
LOAD_SYM(rsmi_num_monitor_devices);
LOAD_SYM(rsmi_dev_name_get);
LOAD_SYM(rsmi_dev_power_cap_get);
LOAD_SYM(rsmi_dev_temp_metric_get);
LOAD_SYM(rsmi_dev_busy_percent_get);
LOAD_SYM(rsmi_dev_memory_busy_percent_get);
LOAD_SYM(rsmi_dev_gpu_clk_freq_get);
LOAD_SYM(rsmi_dev_power_ave_get);
LOAD_SYM(rsmi_dev_memory_total_get);
LOAD_SYM(rsmi_dev_memory_usage_get);
@ -1255,6 +1335,27 @@ namespace Gpu {
return false;
}
#if !defined(RSMI_STATIC)
//? Check version
rsmi_version_t version;
result = rsmi_version_get(&version);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get version");
return false;
} else if (version.major == 5) {
if ((rsmi_dev_gpu_clk_freq_get_v5 = (decltype(rsmi_dev_gpu_clk_freq_get_v5))load_rsmi_sym("rsmi_dev_gpu_clk_freq_get")) == nullptr)
return false;
// In the release tarballs of rocm 6.0.0 and 6.0.2 the version queried with rsmi_version_get is 7.0.0.0
} else if (version.major == 6 || version.major == 7) {
if ((rsmi_dev_gpu_clk_freq_get_v6 = (decltype(rsmi_dev_gpu_clk_freq_get_v6))load_rsmi_sym("rsmi_dev_gpu_clk_freq_get")) == nullptr)
return false;
} else {
Logger::warning("ROCm SMI: Dynamic loading only supported for version 5 and 6");
return false;
}
version_major = version.major;
#endif
//? Device count
result = rsmi_num_monitor_devices(&device_count);
if (result != RSMI_STATUS_SUCCESS) {
@ -1338,7 +1439,46 @@ namespace Gpu {
if constexpr(is_init) gpus_slice[i].supported_functions.mem_utilization = false;
} else gpus_slice[i].mem_utilization_percent.push_back((long long)utilization);
}
#if !defined(RSMI_STATIC)
//? Clock speeds
if (gpus_slice[i].supported_functions.gpu_clock) {
if (version_major == 5) {
rsmi_frequencies_t_v5 frequencies;
result = rsmi_dev_gpu_clk_freq_get_v5(i, RSMI_CLK_TYPE_SYS, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_clock = false;
} else gpus_slice[i].gpu_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
else if (version_major == 6 || version_major == 7) {
rsmi_frequencies_t_v6 frequencies;
result = rsmi_dev_gpu_clk_freq_get_v6(i, RSMI_CLK_TYPE_SYS, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_clock = false;
} else gpus_slice[i].gpu_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
}
if (gpus_slice[i].supported_functions.mem_clock) {
if (version_major == 5) {
rsmi_frequencies_t_v5 frequencies;
result = rsmi_dev_gpu_clk_freq_get_v5(i, RSMI_CLK_TYPE_MEM, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get VRAM clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_clock = false;
} else gpus_slice[i].mem_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
else if (version_major == 6 || version_major == 7) {
rsmi_frequencies_t_v6 frequencies;
result = rsmi_dev_gpu_clk_freq_get_v6(i, RSMI_CLK_TYPE_MEM, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get VRAM clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_clock = false;
} else gpus_slice[i].mem_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
}
#else
//? Clock speeds
if (gpus_slice[i].supported_functions.gpu_clock) {
rsmi_frequencies_t frequencies;
@ -1357,6 +1497,7 @@ namespace Gpu {
if constexpr(is_init) gpus_slice[i].supported_functions.mem_clock = false;
} else gpus_slice[i].mem_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
#endif
//? Power usage & state
if (gpus_slice[i].supported_functions.pwr_usage) {
@ -1482,10 +1623,10 @@ namespace Gpu {
#endif
namespace Mem {
bool has_swap{}; // defaults to false
bool has_swap{};
vector<string> fstab;
fs::file_time_type fstab_time;
int disk_ios{}; // defaults to 0
int disk_ios{};
vector<string> last_found;
//?* Find the filepath to the specified ZFS object's stat file
@ -1613,7 +1754,7 @@ namespace Mem {
auto only_physical = Config::getB("only_physical");
auto zfs_hide_datasets = Config::getB("zfs_hide_datasets");
auto& disks = mem.disks;
static unordered_flat_map<string, future<pair<disk_info, int>>> disks_stats_promises;
static std::unordered_map<string, future<pair<disk_info, int>>> disks_stats_promises;
ifstream diskread;
vector<string> filter;
@ -1920,7 +2061,7 @@ namespace Mem {
if (access(zfs_pool_stat_path.c_str(), R_OK) == 0) {
return zfs_pool_stat_path;
} else {
Logger::debug("Cant access folder: " + zfs_pool_stat_path.string());
Logger::debug("Can't access folder: " + zfs_pool_stat_path.string());
return "";
}
}
@ -1973,10 +2114,10 @@ namespace Mem {
int64_t bytes_read;
int64_t bytes_write;
int64_t io_ticks;
int64_t bytes_read_total{}; // defaults to 0
int64_t bytes_write_total{}; // defaults to 0
int64_t io_ticks_total{}; // defaults to 0
int64_t objects_read{}; // defaults to 0
int64_t bytes_read_total{};
int64_t bytes_write_total{};
int64_t io_ticks_total{};
int64_t objects_read{};
// looking through all files that start with 'objset'
for (const auto& file: fs::directory_iterator(disk.stat)) {
@ -2048,25 +2189,15 @@ namespace Mem {
}
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors{}; // defaults to 0
unordered_flat_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
unordered_flat_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
int errors{};
std::unordered_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
bool rescale{true};
uint64_t timestamp{}; // defaults to 0
//* RAII wrapper for getifaddrs
class getifaddr_wrapper {
struct ifaddrs* ifaddr;
public:
int status;
getifaddr_wrapper() { status = getifaddrs(&ifaddr); }
~getifaddr_wrapper() { freeifaddrs(ifaddr); }
auto operator()() -> struct ifaddrs* { return ifaddr; }
};
uint64_t timestamp{};
auto collect(bool no_update) -> net_info& {
if (Runner::stopping) return empty_net;
@ -2078,10 +2209,10 @@ namespace Net {
if (not no_update and errors < 3) {
//? Get interface list using getifaddrs() wrapper
getifaddr_wrapper if_wrap {};
if (if_wrap.status != 0) {
IfAddrsPtr if_addrs {};
if (if_addrs.get_status() != 0) {
errors++;
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_wrap.status));
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_addrs.get_status()));
redraw = true;
return empty_net;
}
@ -2093,7 +2224,7 @@ namespace Net {
string ipv4, ipv6;
//? Iteration over all items in getifaddrs() list
for (auto* ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
for (auto* ifa = if_addrs.get(); ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) continue;
family = ifa->ifa_addr->sa_family;
const auto& iface = ifa->ifa_name;
@ -2135,7 +2266,7 @@ namespace Net {
} //else, ignoring family==AF_PACKET (see man 3 getifaddrs) which is the first one in the `for` loop.
}
//? Get total recieved and transmitted bytes + device address if no ip was found
//? Get total received and transmitted bytes + device address if no ip was found
for (const auto& iface : interfaces) {
if (net.at(iface).ipv4.empty() and net.at(iface).ipv6.empty())
net.at(iface).ipv4 = readfile("/sys/class/net/" + iface + "/address");
@ -2145,7 +2276,7 @@ namespace Net {
auto& saved_stat = net.at(iface).stat.at(dir);
auto& bandwidth = net.at(iface).bandwidth.at(dir);
uint64_t val{}; // defaults to 0
uint64_t val{};
try { val = (uint64_t)stoull(readfile(sys_file, "0")); }
catch (const std::invalid_argument&) {}
catch (const std::out_of_range&) {}
@ -2193,7 +2324,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -2263,22 +2393,22 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev{}; // defaults to false
bool current_rev{};
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1;
uint64_t old_cputimes{}; // defaults to 0
atomic<int> numpids{}; // defaults to 0
int filter_found{}; // defaults to 0
uint64_t old_cputimes{};
atomic<int> numpids{};
int filter_found{};
detail_container detailed;
constexpr size_t KTHREADD = 2;
static robin_hood::unordered_set<size_t> kernels_procs = {KTHREADD};
static std::unordered_set<size_t> kernels_procs = {KTHREADD};
//* Get detailed info for selected process
void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info>& procs) {
@ -2406,7 +2536,7 @@ namespace Proc {
const int cmult = (per_core) ? Shared::coreCount : 1;
bool got_detailed = false;
static size_t proc_clear_count{}; // defaults to 0
static size_t proc_clear_count{};
//* Use pids from last update if only changing filter, sorting or tree options
if (no_update and not current_procs.empty()) {
@ -2481,7 +2611,7 @@ namespace Proc {
//? Check if pid already exists in current_procs
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
bool no_cache{}; // defaults to false
bool no_cache{};
if (find_old == current_procs.end()) {
current_procs.push_back({pid});
find_old = current_procs.end() - 1;

1272
src/openbsd/btop_collect.cpp Normal file

File diff suppressed because it is too large Load diff

157
src/openbsd/internal.h Normal file
View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2019-2021 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct sysctls {
const char *name;
int mib0;
int mib1;
int mib2;
} sysctlnames[] = {
{ "hw.machine", CTL_HW, HW_MACHINE, 0 },
{ "hw.model", CTL_HW, HW_MODEL, 0 },
{ "hw.ncpu", CTL_HW, HW_NCPU, 0 },
{ "hw.byteorder", CTL_HW, HW_BYTEORDER, 0 },
{ "hw.pagesize", CTL_HW, HW_PAGESIZE, 0 },
{ "hw.disknames", CTL_HW, HW_DISKNAMES, 0 },
{ "hw.diskcount", CTL_HW, HW_DISKCOUNT, 0 },
{ "hw.sensors", CTL_HW, HW_SENSORS, 0 },
{ "hw.model", CTL_HW, HW_MODEL, 0 },
{ "hw.ncpu", CTL_HW, HW_NCPU, 0 },
{ "hw.byteorder", CTL_HW, HW_BYTEORDER, 0 },
{ "hw.pagesize", CTL_HW, HW_PAGESIZE, 0 },
{ "hw.disknames", CTL_HW, HW_DISKNAMES, 0 },
{ "hw.diskcount", CTL_HW, HW_DISKCOUNT, 0 },
{ "hw.sensors", CTL_HW, HW_SENSORS, 0 },
{ "hw.cpuspeed", CTL_HW, HW_CPUSPEED, 0 },
{ "hw.setperf", CTL_HW, HW_SETPERF, 0 },
{ "hw.vendor", CTL_HW, HW_VENDOR, 0 },
{ "hw.product", CTL_HW, HW_PRODUCT, 0 },
{ "hw.serialno", CTL_HW, HW_SERIALNO, 0 },
{ "hw.uuid", CTL_HW, HW_UUID, 0 },
{ "hw.physmem", CTL_HW, HW_PHYSMEM64, 0 },
{ "hw.usermem", CTL_HW, HW_USERMEM64, 0 },
{ "hw.ncpufound", CTL_HW, HW_NCPUFOUND, 0 },
{ "hw.allowpowerdown", CTL_HW, HW_ALLOWPOWERDOWN, 0 },
{ "hw.perfpolicy", CTL_HW, HW_PERFPOLICY, 0 },
{ "hw.smt", CTL_HW, HW_SMT, 0 },
{ "hw.ncpuonline", CTL_HW, HW_NCPUONLINE, 0 },
{ "hw.cpuspeed", CTL_HW, HW_CPUSPEED, 0 },
{ "hw.setperf", CTL_HW, HW_SETPERF, 0 },
{ "hw.vendor", CTL_HW, HW_VENDOR, 0 },
{ "hw.product", CTL_HW, HW_PRODUCT, 0 },
{ "hw.serialno", CTL_HW, HW_SERIALNO, 0 },
{ "hw.uuid", CTL_HW, HW_UUID, 0 },
{ "hw.physmem", CTL_HW, HW_PHYSMEM64, 0 },
{ "hw.usermem", CTL_HW, HW_USERMEM64, 0 },
{ "hw.ncpufound", CTL_HW, HW_NCPUFOUND, 0 },
{ "hw.allowpowerdown", CTL_HW, HW_ALLOWPOWERDOWN, 0 },
{ "hw.perfpolicy", CTL_HW, HW_PERFPOLICY, 0 },
{ "hw.smt", CTL_HW, HW_SMT, 0 },
{ "hw.ncpuonline", CTL_HW, HW_NCPUONLINE, 0 },
{ "kern.ostype", CTL_KERN, KERN_OSTYPE, 0 },
{ "kern.osrelease", CTL_KERN, KERN_OSRELEASE, 0 },
{ "kern.osrevision", CTL_KERN, KERN_OSREV, 0 },
{ "kern.version", CTL_KERN, KERN_VERSION, 0 },
{ "kern.maxvnodes", CTL_KERN, KERN_MAXVNODES, 0 },
{ "kern.maxproc", CTL_KERN, KERN_MAXPROC, 0 },
{ "kern.maxfiles", CTL_KERN, KERN_MAXFILES, 0 },
{ "kern.argmax", CTL_KERN, KERN_ARGMAX, 0 },
{ "kern.securelevel", CTL_KERN, KERN_SECURELVL, 0 },
{ "kern.hostname", CTL_KERN, KERN_HOSTNAME, 0 },
{ "kern.hostid", CTL_KERN, KERN_HOSTID, 0 },
{ "kern.clockrate", CTL_KERN, KERN_CLOCKRATE, 0 },
{ "kern.profiling", CTL_KERN, KERN_PROF, 0 },
{ "kern.posix1version", CTL_KERN, KERN_POSIX1, 0 },
{ "kern.ngroups", CTL_KERN, KERN_NGROUPS, 0 },
{ "kern.job_control", CTL_KERN, KERN_JOB_CONTROL, 0 },
{ "kern.saved_ids", CTL_KERN, KERN_SAVED_IDS, 0 },
{ "kern.boottime", CTL_KERN, KERN_BOOTTIME, 0 },
{ "kern.domainname", CTL_KERN, KERN_DOMAINNAME, 0 },
{ "kern.maxpartitions", CTL_KERN, KERN_MAXPARTITIONS, 0 },
{ "kern.rawpartition", CTL_KERN, KERN_RAWPARTITION, 0 },
{ "kern.maxthread", CTL_KERN, KERN_MAXTHREAD, 0 },
{ "kern.nthreads", CTL_KERN, KERN_NTHREADS, 0 },
{ "kern.osversion", CTL_KERN, KERN_OSVERSION, 0 },
{ "kern.somaxconn", CTL_KERN, KERN_SOMAXCONN, 0 },
{ "kern.sominconn", CTL_KERN, KERN_SOMINCONN, 0 },
{ "kern.nosuidcoredump", CTL_KERN, KERN_NOSUIDCOREDUMP, 0 },
{ "kern.fsync", CTL_KERN, KERN_FSYNC, 0 },
{ "kern.sysvmsg", CTL_KERN, KERN_SYSVMSG, 0 },
{ "kern.sysvsem", CTL_KERN, KERN_SYSVSEM, 0 },
{ "kern.sysvshm", CTL_KERN, KERN_SYSVSHM, 0 },
{ "kern.msgbufsize", CTL_KERN, KERN_MSGBUFSIZE, 0 },
{ "kern.malloc", CTL_KERN, KERN_MALLOCSTATS, 0 },
{ "kern.cp_time", CTL_KERN, KERN_CPTIME, 0 },
{ "kern.nchstats", CTL_KERN, KERN_NCHSTATS, 0 },
{ "kern.forkstat", CTL_KERN, KERN_FORKSTAT, 0 },
{ "kern.tty", CTL_KERN, KERN_TTY, 0 },
{ "kern.ccpu", CTL_KERN, KERN_CCPU, 0 },
{ "kern.fscale", CTL_KERN, KERN_FSCALE, 0 },
{ "kern.nprocs", CTL_KERN, KERN_NPROCS, 0 },
{ "kern.msgbuf", CTL_KERN, KERN_MSGBUF, 0 },
{ "kern.pool", CTL_KERN, KERN_POOL, 0 },
{ "kern.stackgap_random", CTL_KERN, KERN_STACKGAPRANDOM, 0 },
{ "kern.sysvipc_info", CTL_KERN, KERN_SYSVIPC_INFO, 0 },
{ "kern.allowkmem", CTL_KERN, KERN_ALLOWKMEM, 0 },
{ "kern.witnesswatch", CTL_KERN, KERN_WITNESSWATCH, 0 },
{ "kern.splassert", CTL_KERN, KERN_SPLASSERT, 0 },
{ "kern.procargs", CTL_KERN, KERN_PROC_ARGS, 0 },
{ "kern.nfiles", CTL_KERN, KERN_NFILES, 0 },
{ "kern.ttycount", CTL_KERN, KERN_TTYCOUNT, 0 },
{ "kern.numvnodes", CTL_KERN, KERN_NUMVNODES, 0 },
{ "kern.mbstat", CTL_KERN, KERN_MBSTAT, 0 },
{ "kern.witness", CTL_KERN, KERN_WITNESS, 0 },
{ "kern.seminfo", CTL_KERN, KERN_SEMINFO, 0 },
{ "kern.shminfo", CTL_KERN, KERN_SHMINFO, 0 },
{ "kern.intrcnt", CTL_KERN, KERN_INTRCNT, 0 },
{ "kern.watchdog", CTL_KERN, KERN_WATCHDOG, 0 },
{ "kern.proc", CTL_KERN, KERN_PROC, 0 },
{ "kern.maxclusters", CTL_KERN, KERN_MAXCLUSTERS, 0 },
{ "kern.evcount", CTL_KERN, KERN_EVCOUNT, 0 },
{ "kern.timecounter", CTL_KERN, KERN_TIMECOUNTER, 0 },
{ "kern.maxlocksperuid", CTL_KERN, KERN_MAXLOCKSPERUID, 0 },
{ "kern.cp_time2", CTL_KERN, KERN_CPTIME2, 0 },
{ "kern.bufcachepercent", CTL_KERN, KERN_CACHEPCT, 0 },
{ "kern.file", CTL_KERN, KERN_FILE, 0 },
{ "kern.wxabort", CTL_KERN, KERN_WXABORT, 0 },
{ "kern.consdev", CTL_KERN, KERN_CONSDEV, 0 },
{ "kern.netlivelocks", CTL_KERN, KERN_NETLIVELOCKS, 0 },
{ "kern.pool_debug", CTL_KERN, KERN_POOL_DEBUG, 0 },
{ "kern.proc_cwd", CTL_KERN, KERN_PROC_CWD, 0 },
{ "kern.proc_nobroadcastkill", CTL_KERN, KERN_PROC_NOBROADCASTKILL, 0 },
{ "kern.proc_vmap", CTL_KERN, KERN_PROC_VMMAP, 0 },
{ "kern.global_ptrace", CTL_KERN, KERN_GLOBAL_PTRACE, 0 },
{ "kern.consbufsize", CTL_KERN, KERN_CONSBUFSIZE, 0 },
{ "kern.consbuf", CTL_KERN, KERN_CONSBUF, 0 },
{ "kern.audio", CTL_KERN, KERN_AUDIO, 0 },
{ "kern.cpustats", CTL_KERN, KERN_CPUSTATS, 0 },
{ "kern.pfstatus", CTL_KERN, KERN_PFSTATUS, 0 },
{ "kern.timeout_stats", CTL_KERN, KERN_TIMEOUT_STATS, 0 },
{ "kern.utc_offset", CTL_KERN, KERN_UTC_OFFSET, 0 },
{ "vm.vmmeter", CTL_VM, VM_METER, 0 },
{ "vm.loadavg", CTL_VM, VM_LOADAVG, 0 },
{ "vm.psstrings", CTL_VM, VM_PSSTRINGS, 0 },
{ "vm.uvmexp", CTL_VM, VM_UVMEXP, 0 },
{ "vm.swapencrypt", CTL_VM, VM_SWAPENCRYPT, 0 },
{ "vm.nkmempages", CTL_VM, VM_NKMEMPAGES, 0 },
{ "vm.anonmin", CTL_VM, VM_ANONMIN, 0 },
{ "vm.vtextmin", CTL_VM, VM_VTEXTMIN, 0 },
{ "vm.vnodemin", CTL_VM, VM_VNODEMIN, 0 },
{ "vm.maxslp", CTL_VM, VM_MAXSLP, 0 },
{ "vm.uspace", CTL_VM, VM_USPACE, 0 },
{ "vm.malloc_conf", CTL_VM, VM_MALLOC_CONF, 0 },
{ NULL, 0, 0, 0 },
};

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "internal.h"
#include "../btop_tools.hpp"
int
sysctlbyname(const char *name, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int i, mib[2];
for (i = 0; i < 132; i++) {
// for (i = 0; i < sizeof(sysctlnames) / sizeof(sysctlnames[0]); i++) {
if (!strcmp(name, sysctlnames[i].name)) {
mib[0] = sysctlnames[i].mib0;
mib[1] = sysctlnames[i].mib1;
return sysctl(mib, 2, oldp, oldlenp, newp, newlen);
}
}
errno = ENOENT;
return (-1);
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2019 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/sysctl.h>
extern int sysctlbyname(const char *, void *, size_t *, void *, size_t);

View file

@ -16,6 +16,7 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <arpa/inet.h>
@ -44,6 +45,7 @@ tab-size = 4
#include <netinet/in.h> // for inet_ntop
#include <unistd.h>
#include <stdexcept>
#include <utility>
#include <cmath>
#include <fstream>
@ -56,7 +58,9 @@ tab-size = 4
#include "../btop_shared.hpp"
#include "../btop_tools.hpp"
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
#include "sensors.hpp"
#endif
#include "smc.hpp"
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
@ -95,7 +99,7 @@ namespace Cpu {
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
} // namespace Cpu
namespace Mem {
@ -187,11 +191,11 @@ namespace Cpu {
string cpuHz;
bool has_battery = true;
bool macM1 = false;
tuple<int, long, string> current_bat;
tuple<int, float, long, string> current_bat;
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -250,6 +254,7 @@ namespace Cpu {
Logger::debug("get_sensors(): show_coretemp=" + std::to_string(Config::getB("show_coretemp")) + " check_temp=" + std::to_string(Config::getB("check_temp")));
got_sensors = false;
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
ThermalSensors sensors;
if (sensors.getSensors() > 0) {
Logger::debug("M1 sensors found");
@ -257,6 +262,7 @@ namespace Cpu {
cpu_temp_only = true;
macM1 = true;
} else {
#endif
// try SMC (intel)
Logger::debug("checking intel");
SMCConnection smcCon;
@ -281,7 +287,9 @@ namespace Cpu {
// ignore, we don't have temp
got_sensors = false;
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
}
#endif
}
return got_sensors;
}
@ -290,11 +298,12 @@ namespace Cpu {
current_cpu.temp_max = 95; // we have no idea how to get the critical temp
try {
if (macM1) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
ThermalSensors sensors;
current_cpu.temp.at(0).push_back(sensors.getSensors());
if (current_cpu.temp.at(0).size() > 20)
current_cpu.temp.at(0).pop_front();
#endif
} else {
SMCConnection smcCon;
int threadsPerCore = Shared::coreCount / Shared::physicalCoreCount;
@ -329,8 +338,8 @@ namespace Cpu {
return std::to_string(freq / 1000.0 / 1000.0 / 1000.0).substr(0, 3);
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
natural_t cpu_count;
@ -398,8 +407,8 @@ namespace Cpu {
~IOPSList_Wrap() { CFRelease(data); }
};
auto get_battery() -> tuple<int, long, string> {
if (not has_battery) return {0, 0, ""};
auto get_battery() -> tuple<int, float, long, string> {
if (not has_battery) return {0, 0, 0, ""};
uint32_t percent = -1;
long seconds = -1;
@ -438,7 +447,7 @@ namespace Cpu {
has_battery = false;
}
}
return {percent, seconds, status};
return {percent, -1, seconds, status};
}
auto collect(bool no_update) -> cpu_info & {
@ -591,7 +600,7 @@ namespace Mem {
io_object_t &object;
};
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
void collect_disk(std::unordered_map<string, disk_info> &disks, std::unordered_map<string, string> &mapping) {
io_registry_entry_t drive;
io_iterator_t drive_list;
@ -677,7 +686,7 @@ namespace Mem {
if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&p, &info_size) == 0) {
mem.stats.at("free") = p.free_count * Shared::pageSize;
mem.stats.at("cached") = p.external_page_count * Shared::pageSize;
mem.stats.at("used") = (p.active_count + p.inactive_count + p.wire_count) * Shared::pageSize;
mem.stats.at("used") = (p.active_count + p.wire_count) * Shared::pageSize;
mem.stats.at("available") = Shared::totalMem - mem.stats.at("used");
}
@ -708,7 +717,7 @@ namespace Mem {
}
if (show_disks) {
unordered_flat_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
std::unordered_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
double uptime = system_uptime();
auto &disks_filter = Config::getS("disks_filter");
bool filter_exclude = false;
@ -821,13 +830,13 @@ namespace Mem {
} // namespace Mem
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors = 0;
unordered_flat_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
unordered_flat_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
bool rescale = true;
uint64_t timestamp = 0;
@ -904,7 +913,7 @@ namespace Net {
} // else, ignoring family==AF_LINK (see man 3 getifaddrs)
}
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
std::unordered_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
size_t len;
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
@ -931,7 +940,7 @@ namespace Net {
}
}
//? Get total recieved and transmitted bytes + device address if no ip was found
//? Get total received and transmitted bytes + device address if no ip was found
for (const auto &iface : interfaces) {
for (const string dir : {"download", "upload"}) {
auto &saved_stat = net.at(iface).stat.at(dir);
@ -978,7 +987,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -1049,7 +1057,7 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev = false;
@ -1204,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 = "<defunct>";
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<char[]> proc_chars(new char[Shared::arg_max]);

View file

@ -16,6 +16,8 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
#include "sensors.hpp"
#include <CoreFoundation/CoreFoundation.h>
@ -109,3 +111,4 @@ long long Cpu::ThermalSensors::getSensors() {
if (temps.empty()) return 0ll;
return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size());
}
#endif

View file

@ -16,9 +16,12 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
namespace Cpu {
class ThermalSensors {
public:
long long getSensors();
};
} // namespace Cpu
#endif

View file

@ -18,7 +18,7 @@ theme[main_fg]="#cfd8dc"
# Title color for boxes
theme[title]="#ff"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#90"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#2e3436"
# Title color for boxes
theme[title]="#2e3436"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#1a5fb4"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#99DFFF"
# Title color for boxes
theme[title]="#99FFFF"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#FF7F00"
# Background color of selected item in processes box

View file

@ -11,7 +11,7 @@ theme[main_fg]="#eee8d5"
# Title color for boxes
theme[title]="#eee8d5"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#d1302c"
# Background color of selected item in processes box

View file

@ -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"

View file

@ -18,7 +18,7 @@ theme[main_fg]="#737680"
# Title color for boxes
theme[title]="#272a34"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#90"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#E6E6E6"
# Title color for boxes
theme[title]="#ff"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#90"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#bb"
# Title color for boxes
theme[title]="#cc"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#90"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#a89984"
# Title color for boxes
theme[title]="#ebdbb2"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#d79921"
# Background color of selected items

View file

@ -18,7 +18,7 @@ theme[main_fg]="#d4be98"
# Title color for boxes
theme[title]="#d4be98"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#ea6962"
# Background color of selected items

View file

@ -18,7 +18,7 @@ theme[main_fg]="#F8F8F2"
# Title color for boxes
theme[title]="#F8F8F2"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#2eb398"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#F8F8F2"
# Title color for boxes
theme[title]="#F8F8F2"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#F92672"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#d6deeb"
# Title color for boxes
theme[title]="#ffffff"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#addb67"
# Background color of selected items

View file

@ -18,7 +18,7 @@ theme[main_fg]="#D8DEE9"
# Title color for boxes
theme[title]="#8FBCBB"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#5E81AC"
# Background color of selected item in processes box

View file

@ -10,7 +10,7 @@ theme[main_fg]="#abb2bf"
# Title color for boxes
theme[title]="#abb2bf"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#61afef"
# Background color of selected item in processes box

View file

@ -12,7 +12,7 @@ theme[main_fg]="#00"
# Title color for boxes
theme[title]="#00"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#CC3E28"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#eee8d5"
# Title color for boxes
theme[title]="#fdf6e3"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#b58900"
# Background color of selected items

View file

@ -18,7 +18,7 @@ theme[main_fg]="#586e75"
# Title color for boxes
theme[title]="#002b36"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#b58900"
# Background color of selected items

View file

@ -10,7 +10,7 @@ theme[main_fg]="#cfc9c2"
# Title color for boxes
theme[title]="#cfc9c2"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#7dcfff"
# Background color of selected item in processes box

View file

@ -10,7 +10,7 @@ theme[main_fg]="#cfc9c2"
# Title color for boxes
theme[title]="#cfc9c2"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#7dcfff"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#c5c8c6"
# Title color for boxes
theme[title]="#c5c8c6"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#81beb7"
# Background color of selected item in processes box

View file

@ -18,7 +18,7 @@ theme[main_fg]="#30"
# Title color for boxes
theme[title]="#10"
# Higlight color for keyboard shortcuts
# Highlight color for keyboard shortcuts
theme[hi_fg]="#284d75"
# Background color of selected item in processes box