mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-29 22:51:35 +02:00
Merge branch 'aristocratos:main' into main
This commit is contained in:
commit
2973a76f2b
@ -1,4 +1,4 @@
|
|||||||
[*.{cpp,h,sh,md,cfg,sample}]
|
[*.{cpp,h,hpp,sh,md,cfg,sample}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -3,10 +3,10 @@
|
|||||||
github: aristocratos
|
github: aristocratos
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: aristocratos
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
liberapay: # Replace with a single Liberapay username
|
liberapay: # Replace with a single Liberapay username
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie username
|
otechie: # Replace with a single Otechie username
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
custom: https://paypal.me/aristocratos
|
||||||
|
75
.github/workflows/continuous-build-freebsd.yml
vendored
Normal file
75
.github/workflows/continuous-build-freebsd.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Continuous Build FreeBSD
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags-ignore:
|
||||||
|
- '*.*'
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- '!src/linux/**'
|
||||||
|
- '!src/osx/**'
|
||||||
|
- 'include/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/continuous-build-freebsd.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- '!src/linux/**'
|
||||||
|
- '!src/osx/**'
|
||||||
|
- 'include/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/continuous-build-freebsd.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-freebsd:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
compiler: ["clang++", "g++"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
release: '14.0'
|
||||||
|
usesh: true
|
||||||
|
prepare: |
|
||||||
|
pkg install -y gmake gcc coreutils git
|
||||||
|
git config --global --add safe.directory /home/runner/work/btop/btop
|
||||||
|
run: |
|
||||||
|
CXX=${{ matrix.compiler }} gmake STATIC=true STRIP=true
|
||||||
|
GIT_HASH=$(git rev-parse --short "$GITHUB_SHA")
|
||||||
|
COMPILER=$(echo ${{ matrix.compiler }} | sed 's/clang++/llvm/' | sed 's/g++/gcc/')
|
||||||
|
mv bin/btop bin/btop-"$COMPILER"-"$GIT_HASH"
|
||||||
|
ls -alh bin
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: btop-x86_64-freebsd-14
|
||||||
|
path: 'bin/*'
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-freebsd-cmake:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
release: '14.0'
|
||||||
|
usesh: true
|
||||||
|
prepare: pkg install -y cmake git ninja
|
||||||
|
run: |
|
||||||
|
CXX=clang++ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBTOP_STATIC=ON
|
||||||
|
cmake --build build
|
18
.github/workflows/continuous-build-linux.yml
vendored
18
.github/workflows/continuous-build-linux.yml
vendored
@ -13,7 +13,17 @@ on:
|
|||||||
- '!src/freebsd/**'
|
- '!src/freebsd/**'
|
||||||
- 'include/**'
|
- 'include/**'
|
||||||
- 'Makefile'
|
- 'Makefile'
|
||||||
- '.github/workflows/continuous-build.yml'
|
- '.github/workflows/continuous-build-linux.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- '!src/osx/**'
|
||||||
|
- '!src/freebsd/**'
|
||||||
|
- 'include/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/continuous-build-linux.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
static-build:
|
static-build:
|
||||||
@ -83,7 +93,9 @@ jobs:
|
|||||||
run: git config --global --add safe.directory /__w/btop/btop
|
run: git config --global --add safe.directory /__w/btop/btop
|
||||||
|
|
||||||
- name: Checkout source
|
- name: Checkout source
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
- name: Fix - Stopping at filesystem boundary
|
- name: Fix - Stopping at filesystem boundary
|
||||||
run: git init # [fix Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).]
|
run: git init # [fix Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).]
|
||||||
@ -107,7 +119,7 @@ jobs:
|
|||||||
cp bin/btop .artifacts/$FILENAME
|
cp bin/btop .artifacts/$FILENAME
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: btop-${{ matrix.toolchain }}
|
name: btop-${{ matrix.toolchain }}
|
||||||
path: '.artifacts/**'
|
path: '.artifacts/**'
|
||||||
|
49
.github/workflows/continuous-build-macos.yml
vendored
49
.github/workflows/continuous-build-macos.yml
vendored
@ -1,6 +1,7 @@
|
|||||||
name: Continuous Build MacOS
|
name: Continuous Build MacOS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
@ -12,15 +13,26 @@ on:
|
|||||||
- '!src/freebsd/**'
|
- '!src/freebsd/**'
|
||||||
- 'include/**'
|
- 'include/**'
|
||||||
- 'Makefile'
|
- 'Makefile'
|
||||||
- '.github/workflows/*'
|
- '.github/workflows/continuous-build-macos.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- '!src/linux/**'
|
||||||
|
- '!src/freebsd/**'
|
||||||
|
- 'include/**'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/continuous-build-macos.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-osx:
|
build-macos11:
|
||||||
|
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: |
|
run: |
|
||||||
make CXX=g++-11 ARCH=x86_64 STATIC=true STRIP=true
|
make CXX=g++-11 ARCH=x86_64 STATIC=true STRIP=true
|
||||||
@ -28,7 +40,30 @@ jobs:
|
|||||||
mv bin/btop bin/btop-x86_64-BigSur-$GIT_HASH
|
mv bin/btop bin/btop-x86_64-BigSur-$GIT_HASH
|
||||||
ls -alh bin
|
ls -alh bin
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: btop-x86_64-macos-BigSur
|
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
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
run: |
|
||||||
|
make CXX=g++-12 ARCH=x86_64 STATIC=true STRIP=true
|
||||||
|
GIT_HASH=$(git rev-parse --short "$GITHUB_SHA")
|
||||||
|
mv bin/btop bin/btop-x86_64-Monterey-$GIT_HASH
|
||||||
|
ls -alh bin
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: btop-x86_64-macos12-Monterey
|
||||||
path: 'bin/*'
|
path: 'bin/*'
|
||||||
|
32
.gitignore
vendored
32
.gitignore
vendored
@ -51,6 +51,34 @@ bin
|
|||||||
btop
|
btop
|
||||||
.*/
|
.*/
|
||||||
|
|
||||||
|
# Optional libraries
|
||||||
|
lib/rocm_smi_lib
|
||||||
|
|
||||||
#do not ignore .github directory
|
# Don't ignore .github directory
|
||||||
!.github
|
!.github/
|
||||||
|
|
||||||
|
# Ignore files created by Qt Creator
|
||||||
|
*.config
|
||||||
|
*.creator
|
||||||
|
*.creator.user
|
||||||
|
*.creator.user.*
|
||||||
|
*.cflags
|
||||||
|
*.cxxflags
|
||||||
|
*.files
|
||||||
|
*.includes
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake
|
||||||
|
_deps
|
||||||
|
|
||||||
|
# CLion
|
||||||
|
cmake-build-*
|
||||||
|
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
112
CHANGELOG.md
112
CHANGELOG.md
@ -1,3 +1,115 @@
|
|||||||
|
## v1.3.0
|
||||||
|
|
||||||
|
* Added Gpu Support | @romner-set | PR #529
|
||||||
|
|
||||||
|
* Elementarish theme: color update according to Elementary palette | @stradicat | PR #660
|
||||||
|
|
||||||
|
* Add alternative key codes for Delete, Insert, Home, End | @ivanp7 | PR #659
|
||||||
|
|
||||||
|
* Fix scrollbar not clearing sometimes. | @DecklynKern | PR #643
|
||||||
|
|
||||||
|
* Add keybind for toggling memory display mode in PROC box | @rahulaggarwal965 | PR #623
|
||||||
|
|
||||||
|
* Minor string initialization improvement | @imwints | PR #636
|
||||||
|
|
||||||
|
* Made disks statvfs logic asynchronous. | @crestfallnatwork | PR #633
|
||||||
|
|
||||||
|
* Fix signal list on non-linux/weird linux platforms | @lvxnull | PR #630
|
||||||
|
|
||||||
|
* Add option to accumulate a child's resources in parent in tree-view | @imwints | PR #618
|
||||||
|
|
||||||
|
* Add CMake support for Linux | @imwints | PR #589
|
||||||
|
|
||||||
|
* Horizon theme | @SidVeld | PR #610
|
||||||
|
|
||||||
|
* Fix short conversion of 1000-1023 *iB | @scorpion-26 | #609
|
||||||
|
|
||||||
|
* Fix integer overflows in btop_collect.cpp | @dorrellmw | #546
|
||||||
|
|
||||||
|
* Support compiling with LLVM | @imwints | #510
|
||||||
|
|
||||||
|
* Fix getting zfs pool name with '.' char in freebsd | @jfouquart | #602
|
||||||
|
|
||||||
|
* [macos/freebsd] support gcc13 | @joske | #600
|
||||||
|
|
||||||
|
* FreeBSD swap info | @rrveex | #560
|
||||||
|
|
||||||
|
* Create adwaita.theme | @flipflop133 | #485
|
||||||
|
|
||||||
|
+ Various fixes by @imwints, @simplepad, @joske, @gwena, @cpalv, @iambeingtracked, @mattico, @NexAdn
|
||||||
|
|
||||||
|
## v1.2.13
|
||||||
|
|
||||||
|
* Makefile: VERBOSE=true flag for Makefile to display all compiler commands and fixed so already set CXXFLAGS and LDFLAGS are displayed.
|
||||||
|
|
||||||
|
* Makefile: Added autodetection for gcc12 to make compiling on macos Ventura easier.
|
||||||
|
|
||||||
|
* Changed: Reverted back to sysconf(_SC_NPROCESSORS_ONLN) for Cpu core count ant let the new dynamic update fix if cores are turned on later
|
||||||
|
|
||||||
|
* Fixed: Ignore disks that fails in statvfs64() to avoid slowdowns and possible crashes.
|
||||||
|
|
||||||
|
* Fixed: Moved up get_cpuHz() in the execution order to get better cpu clock reading.
|
||||||
|
|
||||||
|
* Added: proc tree view: if there's more than 40 width left, try to print full cmd, by @Superty
|
||||||
|
|
||||||
|
* Fixed: Show the first IP of the interface in NET box instead of the last, by @correabuscar
|
||||||
|
|
||||||
|
* Changed: Replace getnameinfo with inet_ntop [on Linux], by @correabuscar
|
||||||
|
|
||||||
|
* Fixed: Not picking up last username from /etc/passwd
|
||||||
|
|
||||||
|
* Fixed: Process nice value underflowing, issue #461
|
||||||
|
|
||||||
|
* Changed: Replace getnameinfo with inet_ntop [on FreeBSD], by @correabuscar
|
||||||
|
|
||||||
|
* Changed: Replace getnameinfo with inet_ntop [on macos], by @correabuscar
|
||||||
|
|
||||||
|
## v1.2.12
|
||||||
|
|
||||||
|
* Added: Dynamic updating of max number of CPU cores.
|
||||||
|
|
||||||
|
## v1.2.11
|
||||||
|
|
||||||
|
* Fixed: Number of cores wrongly detected for Ryzen in rare cases.
|
||||||
|
|
||||||
|
## v1.2.10
|
||||||
|
|
||||||
|
* Fixed: Process tree filtering not case insensitive
|
||||||
|
|
||||||
|
* Added: Paper theme, by @s6muel
|
||||||
|
|
||||||
|
* Fixed: Extra checks to avoid crash on trying to replace empty strings in tree mode
|
||||||
|
|
||||||
|
* Fixed: Crashing when cores are offline
|
||||||
|
|
||||||
|
* Fixed: Cpu::collect() core count counter...
|
||||||
|
|
||||||
|
* Changed: Using sysconf(_SC_NPROCESSORS_CONF) for number of cores instead of sysconf(_SC_NPROCESSORS_ONLN)
|
||||||
|
|
||||||
|
* Maintenance: Code cleanup, by @stefanos82
|
||||||
|
|
||||||
|
## v1.2.9
|
||||||
|
|
||||||
|
* Fixed: Memory values not clearing properly when not in graph mode in mem box
|
||||||
|
|
||||||
|
* Changed: kyli0x theme color update, by @kyli0x
|
||||||
|
|
||||||
|
* Added: Elementarish theme, by @dennismayr
|
||||||
|
|
||||||
|
* Added: key "?" to see help, by @mohi001
|
||||||
|
|
||||||
|
* Added: solarized_light theme, by @Fingerzam
|
||||||
|
|
||||||
|
* Changed: Made ZFS stats collection compatible with zfs_pools_only option, by @simplepad
|
||||||
|
|
||||||
|
* Changed: Rewrite of process sorting and tree generation including fixes for tree sorting and mouse support
|
||||||
|
|
||||||
|
* Added: Option to hide the small cpu graphs for processes
|
||||||
|
|
||||||
|
* Changed: Small graphs now show colors for each character
|
||||||
|
|
||||||
|
* Fixed: Getting selfpath on macos (fix for finding theme folder)
|
||||||
|
|
||||||
## v1.2.8
|
## v1.2.8
|
||||||
|
|
||||||
* Added: Support for ZFS pool io stats monitoring, by @simplepad
|
* Added: Support for ZFS pool io stats monitoring, by @simplepad
|
||||||
|
201
CMakeLists.txt
Normal file
201
CMakeLists.txt
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# CMake configuration for btop
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
# Disable in-source builds since they would override the Makefile
|
||||||
|
if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
message(FATAL_ERROR "In-source builds are not allowed")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project("btop"
|
||||||
|
VERSION 1.2.13
|
||||||
|
DESCRIPTION "A monitor of resources"
|
||||||
|
HOMEPAGE_URL "https://github.com/aristocratos/btop"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make custom modules available
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
|
||||||
|
|
||||||
|
# When the build type is not set we can't fortify
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_COLOR_DIAGNOSTICS ON)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
option(BTOP_STATIC "Link btop statically" OFF)
|
||||||
|
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_GPU "Enable GPU support" ON)
|
||||||
|
cmake_dependent_option(BTOP_RSMI_STATIC "Link statically to ROCm SMI" OFF "BTOP_GPU" OFF)
|
||||||
|
|
||||||
|
if(BTOP_STATIC)
|
||||||
|
# Set this before calling find_package
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckIncludeFileCXX)
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
|
||||||
|
check_include_file_cxx(ranges CXX_HAS_RANGES)
|
||||||
|
if(NOT CXX_HAS_RANGES)
|
||||||
|
message(FATAL_ERROR "The compiler doesn't support <ranges>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(btop
|
||||||
|
src/btop.cpp
|
||||||
|
src/btop_config.cpp
|
||||||
|
src/btop_draw.cpp
|
||||||
|
src/btop_input.cpp
|
||||||
|
src/btop_menu.cpp
|
||||||
|
src/btop_shared.cpp
|
||||||
|
src/btop_theme.cpp
|
||||||
|
src/btop_tools.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# NOTE: Checks can be simplified with CMake 3.25
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
|
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 "Linux")
|
||||||
|
target_sources(btop PRIVATE src/linux/btop_collect.cpp)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is not supported")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check for and enable LTO
|
||||||
|
check_ipo_supported(RESULT ipo_supported)
|
||||||
|
if(ipo_supported AND BTOP_LTO)
|
||||||
|
set_target_properties(btop PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: enable more warnings in coordination with upstream
|
||||||
|
target_compile_options(btop PRIVATE
|
||||||
|
-Wall -Wextra -Wpedantic
|
||||||
|
-ftree-vectorize -fstack-clash-protection
|
||||||
|
)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
target_compile_options(btop PRIVATE
|
||||||
|
-Wheader-hygiene -Wgnu -Wthread-safety
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BTOP_PEDANTIC)
|
||||||
|
target_compile_options(btop PRIVATE
|
||||||
|
-Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused
|
||||||
|
-Woverloaded-virtual -Wconversion -Wsign-conversion -Wdouble-promotion
|
||||||
|
-Wformat=2 -Wimplicit-fallthrough -Weffc++
|
||||||
|
)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
target_compile_options(btop PRIVATE
|
||||||
|
-Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference
|
||||||
|
-Wuseless-cast
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BTOP_WERROR)
|
||||||
|
target_compile_options(btop PRIVATE -Werror)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
check_cxx_compiler_flag(-fstack-protector CXX_HAS_FSTACK_PROTECTOR)
|
||||||
|
if(CXX_HAS_FSTACK_PROTECTOR)
|
||||||
|
target_compile_options(btop PRIVATE -fstack-protector)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
check_cxx_compiler_flag(-fcf-protection CXX_HAS_FCF_PROTECTION)
|
||||||
|
if(CXX_HAS_FCF_PROTECTION)
|
||||||
|
target_compile_options(btop PRIVATE -fcf-protection)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_compile_definitions(btop PRIVATE
|
||||||
|
_FILE_OFFSET_BITS=64
|
||||||
|
_GLIBCXX_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS=1
|
||||||
|
# Only has an effect with optimizations enabled
|
||||||
|
$<$<NOT:$<CONFIG:Debug>>:_FORTIFY_SOURCE=2>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enable GPU support
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND BTOP_GPU)
|
||||||
|
target_compile_definitions(btop PRIVATE GPU_SUPPORT)
|
||||||
|
|
||||||
|
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
|
||||||
|
set(_CMAKE_MODULE_PATH CMAKE_MODULE_PATH)
|
||||||
|
unset(CMAKE_MODULE_PATH)
|
||||||
|
|
||||||
|
# NOTE: This might be problematic in the future if other sub projects
|
||||||
|
# depend on this or if btop starts producing libraries
|
||||||
|
# Build a static ROCm library
|
||||||
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
add_subdirectory(lib/rocm_smi_lib EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
add_library(ROCm INTERFACE)
|
||||||
|
# Export ROCm's properties to a CMake target (which should've been done by ROCm :-/)
|
||||||
|
target_compile_definitions(ROCm INTERFACE RSMI_STATIC)
|
||||||
|
target_include_directories(ROCm INTERFACE lib/rocm_smi_lib/include)
|
||||||
|
target_link_libraries(ROCm INTERFACE rocm_smi64)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH _CMAKE_MODULE_PATH)
|
||||||
|
|
||||||
|
target_link_libraries(btop PRIVATE ROCm)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(btop SYSTEM PRIVATE include)
|
||||||
|
|
||||||
|
# mold
|
||||||
|
if(BTOP_USE_MOLD)
|
||||||
|
target_link_options(btop PRIVATE -fuse-ld=mold)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BTOP_STATIC)
|
||||||
|
target_compile_definitions(btop PRIVATE STATIC_BUILD)
|
||||||
|
target_link_options(btop PRIVATE -static LINKER:--fatal-warnings)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add libraries
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(btop PRIVATE Threads::Threads)
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
|
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,CoreFoundation)
|
||||||
|
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,IOKit)
|
||||||
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
find_package(devstat REQUIRED)
|
||||||
|
find_package(kvm REQUIRED)
|
||||||
|
target_link_libraries(btop PRIVATE devstat::devstat kvm::kvm)
|
||||||
|
if(BTOP_STATIC)
|
||||||
|
find_package(elf REQUIRED)
|
||||||
|
target_link_libraries(btop PRIVATE elf::elf)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS btop RUNTIME)
|
||||||
|
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")
|
||||||
|
|
211
Makefile
211
Makefile
@ -1,6 +1,6 @@
|
|||||||
#* Btop++ makefile v1.5
|
#* Btop++ makefile v1.6
|
||||||
|
|
||||||
BANNER = \n \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m████████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗\n \033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗╚══\033[38;5;160m██\033[38;5;239m╔══╝\033[38;5;160m██\033[38;5;239m╔═══\033[38;5;160m██\033[38;5;239m╗\033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗\n \033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║\033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██████\033[38;5;238m╗\033[38;5;124m██████\033[38;5;238m╗\n \033[38;5;88m██\033[38;5;237m╔══\033[38;5;88m██\033[38;5;237m╗ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║\033[38;5;88m██\033[38;5;237m╔═══╝ ╚═\033[38;5;88m██\033[38;5;237m╔═╝╚═\033[38;5;88m██\033[38;5;237m╔═╝\n \033[38;5;52m██████\033[38;5;236m╔╝ \033[38;5;52m██\033[38;5;236m║ ╚\033[38;5;52m██████\033[38;5;236m╔╝\033[38;5;52m██\033[38;5;236m║ ╚═╝ ╚═╝\n \033[38;5;235m╚═════╝ ╚═╝ ╚═════╝ ╚═╝ \033[1;3;38;5;240mMakefile v1.5\033[0m
|
BANNER = \n \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m████████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗\n \033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗╚══\033[38;5;160m██\033[38;5;239m╔══╝\033[38;5;160m██\033[38;5;239m╔═══\033[38;5;160m██\033[38;5;239m╗\033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗\n \033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║\033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██████\033[38;5;238m╗\033[38;5;124m██████\033[38;5;238m╗\n \033[38;5;88m██\033[38;5;237m╔══\033[38;5;88m██\033[38;5;237m╗ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║\033[38;5;88m██\033[38;5;237m╔═══╝ ╚═\033[38;5;88m██\033[38;5;237m╔═╝╚═\033[38;5;88m██\033[38;5;237m╔═╝\n \033[38;5;52m██████\033[38;5;236m╔╝ \033[38;5;52m██\033[38;5;236m║ ╚\033[38;5;52m██████\033[38;5;236m╔╝\033[38;5;52m██\033[38;5;236m║ ╚═╝ ╚═╝\n \033[38;5;235m╚═════╝ ╚═╝ ╚═════╝ ╚═╝ \033[1;3;38;5;240mMakefile v1.6\033[0m
|
||||||
|
|
||||||
override BTOP_VERSION := $(shell head -n100 src/btop.cpp 2>/dev/null | grep "Version =" | cut -f2 -d"\"" || echo " unknown")
|
override BTOP_VERSION := $(shell head -n100 src/btop.cpp 2>/dev/null | grep "Version =" | cut -f2 -d"\"" || echo " unknown")
|
||||||
override TIMESTAMP := $(shell date +%s 2>/dev/null || echo "0")
|
override TIMESTAMP := $(shell date +%s 2>/dev/null || echo "0")
|
||||||
@ -12,12 +12,12 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(QUIET),true)
|
ifneq ($(QUIET),true)
|
||||||
override PRE := info info-quiet
|
|
||||||
override QUIET := false
|
override QUIET := false
|
||||||
else
|
|
||||||
override PRE := info-quiet
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
OLDCXX := $(CXXFLAGS)
|
||||||
|
OLDLD := $(LDFLAGS)
|
||||||
|
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
#? Detect PLATFORM and ARCH from uname/gcc if not set
|
#? Detect PLATFORM and ARCH from uname/gcc if not set
|
||||||
@ -36,6 +36,70 @@ endif
|
|||||||
|
|
||||||
override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]')
|
override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
#? GPU Support
|
||||||
|
ifeq ($(PLATFORM_LC)$(ARCH),linuxx86_64)
|
||||||
|
ifneq ($(STATIC),true)
|
||||||
|
GPU_SUPPORT := true
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
ifneq ($(GPU_SUPPORT),true)
|
||||||
|
GPU_SUPPORT := false
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(GPU_SUPPORT),true)
|
||||||
|
override ADDFLAGS += -DGPU_SUPPORT
|
||||||
|
endif
|
||||||
|
|
||||||
|
#? Compiler and Linker
|
||||||
|
ifeq ($(shell $(CXX) --version | grep clang >/dev/null 2>&1; echo $$?),0)
|
||||||
|
override CXX_IS_CLANG := true
|
||||||
|
endif
|
||||||
|
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
|
||||||
|
override CXX_VERSION_MAJOR := $(shell echo $(CXX_VERSION) | cut -d '.' -f 1)
|
||||||
|
|
||||||
|
CLANG_WORKS = false
|
||||||
|
GCC_WORKS = false
|
||||||
|
|
||||||
|
#? Supported is Clang 16.0.0 and later
|
||||||
|
ifeq ($(CXX_IS_CLANG),true)
|
||||||
|
ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 16; 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
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CLANG_WORKS),false)
|
||||||
|
ifeq ($(GCC_WORKS),false)
|
||||||
|
$(error $(shell printf "\033[1;91mERROR: \033[97mCompiler too old. (Requires Clang 16.0.0, GCC 10.1.0)\033[0m"))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
#? Any flags added to TESTFLAGS must not contain whitespace for the testing to work
|
#? Any flags added to TESTFLAGS must not contain whitespace for the testing to work
|
||||||
override TESTFLAGS := -fexceptions -fstack-clash-protection -fcf-protection
|
override TESTFLAGS := -fexceptions -fstack-clash-protection -fcf-protection
|
||||||
ifneq ($(PLATFORM) $(ARCH),macos arm64)
|
ifneq ($(PLATFORM) $(ARCH),macos arm64)
|
||||||
@ -43,9 +107,17 @@ ifneq ($(PLATFORM) $(ARCH),macos arm64)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(STATIC),true)
|
ifeq ($(STATIC),true)
|
||||||
override ADDFLAGS += -static-libgcc -static-libstdc++
|
ifeq ($(CXX_IS_CLANG) $(CLANG_WORKS),true true)
|
||||||
ifneq ($(PLATFORM),macos)
|
ifeq ($(shell $(CXX) -print-target-triple | grep gnu >/dev/null; echo $$?),0)
|
||||||
|
$(error $(shell printf "\033[1;91mERROR: \033[97m$(CXX) can't statically link glibc\033[0m"))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
override ADDFLAGS += -static-libgcc -static-libstdc++
|
||||||
|
endif
|
||||||
|
ifeq ($(PLATFORM_LC),linux)
|
||||||
override ADDFLAGS += -DSTATIC_BUILD -static -Wl,--fatal-warnings
|
override ADDFLAGS += -DSTATIC_BUILD -static -Wl,--fatal-warnings
|
||||||
|
else ifeq ($(PLATFORM_LC),freebsd)
|
||||||
|
override ADDFLAGS += -DSTATIC_BUILD
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -53,29 +125,10 @@ ifeq ($(STRIP),true)
|
|||||||
override ADDFLAGS += -s
|
override ADDFLAGS += -s
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#? Compiler and Linker
|
ifeq ($(VERBOSE),true)
|
||||||
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
override VERBOSE := false
|
||||||
CXX := g++-11
|
else
|
||||||
else ifeq ($(shell command -v g++11 >/dev/null; echo $$?),0)
|
override VERBOSE := true
|
||||||
CXX := g++11
|
|
||||||
else ifeq ($(shell command -v g++ >/dev/null; echo $$?),0)
|
|
||||||
CXX := g++
|
|
||||||
endif
|
|
||||||
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
|
|
||||||
|
|
||||||
#? Try to make sure we are using GCC/G++ version 11 or later if not instructed to use g++-10
|
|
||||||
ifeq ($(CXX),g++)
|
|
||||||
ifeq ($(shell g++ --version | grep clang >/dev/null 2>&1; echo $$?),0)
|
|
||||||
V_MAJOR := 0
|
|
||||||
else
|
|
||||||
V_MAJOR := $(shell echo $(CXX_VERSION) | cut -f1 -d".")
|
|
||||||
endif
|
|
||||||
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
|
|
||||||
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
|
||||||
override CXX := g++-11
|
|
||||||
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#? Pull in platform specific source files and get thread count
|
#? Pull in platform specific source files and get thread count
|
||||||
@ -87,7 +140,10 @@ else ifeq ($(PLATFORM_LC),freebsd)
|
|||||||
PLATFORM_DIR := freebsd
|
PLATFORM_DIR := freebsd
|
||||||
THREADS := $(shell getconf NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
THREADS := $(shell getconf NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||||
SU_GROUP := wheel
|
SU_GROUP := wheel
|
||||||
override ADDFLAGS += -lstdc++ -lm -lkvm -ldevstat -Wl,-rpath=/usr/local/lib/gcc11
|
override ADDFLAGS += -lm -lkvm -ldevstat -Wl,-rpath=/usr/local/lib/gcc$(CXX_VERSION_MAJOR)
|
||||||
|
ifneq ($(STATIC),true)
|
||||||
|
override ADDFLAGS += -lstdc++
|
||||||
|
endif
|
||||||
export MAKE = gmake
|
export MAKE = gmake
|
||||||
else ifeq ($(PLATFORM_LC),macos)
|
else ifeq ($(PLATFORM_LC),macos)
|
||||||
PLATFORM_DIR := osx
|
PLATFORM_DIR := osx
|
||||||
@ -104,9 +160,16 @@ ifeq ($(THREADS),1)
|
|||||||
override THREADS := auto
|
override THREADS := auto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
#? LTO command line
|
||||||
|
ifeq ($(CLANG_WORKS),true)
|
||||||
|
LTO := thin
|
||||||
|
else
|
||||||
|
LTO := $(THREADS)
|
||||||
|
endif
|
||||||
|
|
||||||
#? The Directories, Source, Includes, Objects and Binary
|
#? The Directories, Source, Includes, Objects and Binary
|
||||||
SRCDIR := src
|
SRCDIR := src
|
||||||
INCDIR := include
|
INCDIRS := include $(wildcard lib/**/include)
|
||||||
BUILDDIR := obj
|
BUILDDIR := obj
|
||||||
TARGETDIR := bin
|
TARGETDIR := bin
|
||||||
SRCEXT := cpp
|
SRCEXT := cpp
|
||||||
@ -119,11 +182,11 @@ override GOODFLAGS := $(foreach flag,$(TESTFLAGS),$(strip $(shell echo "int main
|
|||||||
#? Flags, Libraries and Includes
|
#? Flags, Libraries and Includes
|
||||||
override REQFLAGS := -std=c++20
|
override REQFLAGS := -std=c++20
|
||||||
WARNFLAGS := -Wall -Wextra -pedantic
|
WARNFLAGS := -Wall -Wextra -pedantic
|
||||||
OPTFLAGS := -O2 -ftree-loop-vectorize -flto=$(THREADS)
|
OPTFLAGS := -O2 -ftree-vectorize -flto=$(LTO)
|
||||||
LDCXXFLAGS := -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS $(GOODFLAGS) $(ADDFLAGS)
|
LDCXXFLAGS := -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -D_FILE_OFFSET_BITS=64 $(GOODFLAGS) $(ADDFLAGS)
|
||||||
override CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
override CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||||
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||||
INC := -I$(INCDIR) -I$(SRCDIR)
|
INC := $(foreach incdir,$(INCDIRS),-isystem $(incdir)) -I$(SRCDIR)
|
||||||
SU_USER := root
|
SU_USER := root
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
@ -154,24 +217,38 @@ endif
|
|||||||
|
|
||||||
P := %%
|
P := %%
|
||||||
|
|
||||||
#? Default Make
|
ifeq ($(VERBOSE),true)
|
||||||
all: $(PRE) directories btop
|
# Doesn't work with `&>`
|
||||||
|
override SUPPRESS := > /dev/null 2> /dev/null
|
||||||
|
else
|
||||||
|
override SUPPRESS :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
#? Default Make
|
||||||
|
.ONESHELL:
|
||||||
|
all: | info rocm_smi info-quiet directories btop
|
||||||
|
|
||||||
|
ifneq ($(QUIET),true)
|
||||||
info:
|
info:
|
||||||
@printf " $(BANNER)\n"
|
@printf " $(BANNER)\n"
|
||||||
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
|
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
|
||||||
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
|
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
|
||||||
@printf "\033[1;93mCXX \033[1;93m?| \033[0m$(CXX) \033[1;93m(\033[97m$(CXX_VERSION)\033[93m)\n"
|
@printf "\033[1;95mGPU_SUPPORT \033[1;94m:| \033[0m$(GPU_SUPPORT)\n"
|
||||||
@printf "\033[1;94mTHREADS \033[1;94m:| \033[0m$(THREADS)\n"
|
@printf "\033[1;93mCXX \033[1;93m?| \033[0m$(CXX) \033[1;93m(\033[97m$(CXX_VERSION)\033[93m)\n"
|
||||||
@printf "\033[1;92mREQFLAGS \033[1;91m!| \033[0m$(REQFLAGS)\n"
|
@printf "\033[1;94mTHREADS \033[1;94m:| \033[0m$(THREADS)\n"
|
||||||
@printf "\033[1;91mWARNFLAGS \033[1;94m:| \033[0m$(WARNFLAGS)\n"
|
@printf "\033[1;92mREQFLAGS \033[1;91m!| \033[0m$(REQFLAGS)\n"
|
||||||
@printf "\033[1;94mOPTFLAGS \033[1;94m:| \033[0m$(OPTFLAGS)\n"
|
@printf "\033[1;91mWARNFLAGS \033[1;94m:| \033[0m$(WARNFLAGS)\n"
|
||||||
@printf "\033[1;93mLDCXXFLAGS \033[1;94m:| \033[0m$(LDCXXFLAGS)\n"
|
@printf "\033[1;94mOPTFLAGS \033[1;94m:| \033[0m$(OPTFLAGS)\n"
|
||||||
@printf "\033[1;95mCXXFLAGS \033[1;92m+| \033[0;37m\$$(\033[92mREQFLAGS\033[37m) \$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m)\n"
|
@printf "\033[1;93mLDCXXFLAGS \033[1;94m:| \033[0m$(LDCXXFLAGS)\n"
|
||||||
@printf "\033[1;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m)\n"
|
@printf "\033[1;95mCXXFLAGS \033[1;92m+| \033[0;37m\$$(\033[92mREQFLAGS\033[37m) \$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDCXX)\n"
|
||||||
|
@printf "\033[1;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDLD)\n"
|
||||||
|
else
|
||||||
|
info:
|
||||||
|
@true
|
||||||
|
endif
|
||||||
|
|
||||||
info-quiet:
|
|
||||||
@sleep 0.1 2>/dev/null || true
|
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"
|
@printf "\n\033[1;92mBuilding btop++ \033[91m(\033[97mv$(BTOP_VERSION)\033[91m) \033[93m$(PLATFORM) \033[96m$(ARCH)\033[0m\n"
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@ -189,18 +266,22 @@ help:
|
|||||||
|
|
||||||
#? Make the Directories
|
#? Make the Directories
|
||||||
directories:
|
directories:
|
||||||
|
@$(VERBOSE) || printf "mkdir -p $(TARGETDIR)\n"
|
||||||
@mkdir -p $(TARGETDIR)
|
@mkdir -p $(TARGETDIR)
|
||||||
|
@$(VERBOSE) || printf "mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)\n"
|
||||||
@mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)
|
@mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)
|
||||||
|
|
||||||
#? Clean only Objects
|
#? Clean only Objects
|
||||||
clean:
|
clean:
|
||||||
@printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
|
@printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
|
||||||
@rm -rf $(BUILDDIR)
|
@rm -rf $(BUILDDIR)
|
||||||
|
@cmake --build lib/rocm_smi_lib/build --target clean &> /dev/null || true
|
||||||
|
|
||||||
#? Clean Objects and Binaries
|
#? Clean Objects and Binaries
|
||||||
distclean: clean
|
distclean: clean
|
||||||
@printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
|
@printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
|
||||||
@rm -rf $(TARGETDIR)
|
@rm -rf $(TARGETDIR)
|
||||||
|
@rm -rf lib/rocm_smi_lib/build
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
|
@printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
|
||||||
@ -246,22 +327,50 @@ uninstall:
|
|||||||
#? Pull in dependency info for *existing* .o files
|
#? Pull in dependency info for *existing* .o files
|
||||||
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
|
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
|
||||||
|
|
||||||
|
#? Compile rocm_smi
|
||||||
|
ifeq ($(GPU_SUPPORT)$(RSMI_STATIC),truetrue)
|
||||||
|
ROCM_DIR ?= lib/rocm_smi_lib
|
||||||
|
ROCM_BUILD_DIR := $(ROCM_DIR)/build
|
||||||
|
ifeq ($(DEBUG),true)
|
||||||
|
BUILD_TYPE := Debug
|
||||||
|
else
|
||||||
|
BUILD_TYPE := Release
|
||||||
|
endif
|
||||||
|
.ONESHELL:
|
||||||
|
rocm_smi:
|
||||||
|
@printf "\n\033[1;92mBuilding ROCm SMI static library\033[37m...\033[0m\n"
|
||||||
|
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
||||||
|
@$(QUIET) || printf "\033[1;97mRunning CMake...\033[0m\n"
|
||||||
|
CXX=$(CXX) cmake -S $(ROCM_DIR) -B $(ROCM_BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DBUILD_SHARED_LIBS=OFF $(SUPPRESS) || { printf "\033[1;91mCMake failed, continuing build without statically linking ROCm SMI\033[37m...\033[0m\n"; exit 0; }
|
||||||
|
@$(QUIET) || printf "\n\033[1;97mBuilding and linking...\033[0m\n"
|
||||||
|
@cmake --build $(ROCM_BUILD_DIR) -j -t rocm_smi64 $(SUPPRESS) || { printf "\033[1;91mMake failed, continuing build without statically linking ROCm SMI\033[37m...\033[0m\n"; exit 0; }
|
||||||
|
@printf "\033[1;92m100$(P)\033[10D\033[5C-> \033[1;37m$(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a \033[1;93m(\033[1;97m$$(du -ah $(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a | cut -f1)iB\033[1;93m)\033[0m\n"
|
||||||
|
@printf "\033[1;92mROCm SMI build complete in \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $(TIMESTAMP) 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo "unknown")\033[92m)\033[0m\n"
|
||||||
|
@$(eval override LDFLAGS += $(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a -DRSMI_STATIC) # TODO: this seems to execute every time, no matter if the compilation failed or succeeded
|
||||||
|
@$(eval override CXXFLAGS += -DRSMI_STATIC)
|
||||||
|
else
|
||||||
|
rocm_smi:
|
||||||
|
@true
|
||||||
|
endif
|
||||||
|
|
||||||
#? Link
|
#? Link
|
||||||
.ONESHELL:
|
.ONESHELL:
|
||||||
btop: $(OBJECTS)
|
btop: $(OBJECTS) | rocm_smi directories
|
||||||
@sleep 0.2 2>/dev/null || true
|
@sleep 0.2 2>/dev/null || true
|
||||||
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
||||||
@$(QUIET) || printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
|
@$(QUIET) || printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
|
||||||
|
@$(VERBOSE) || printf "$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS)\n"
|
||||||
@$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS) || exit 1
|
@$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS) || exit 1
|
||||||
@printf "\033[1;92m100$(P) -> \033[1;37m$(TARGETDIR)/btop \033[100D\033[38C\033[1;93m(\033[1;97m$$(du -ah $(TARGETDIR)/btop | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%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"
|
@printf "\033[1;92m100$(P) -> \033[1;37m$(TARGETDIR)/btop \033[100D\033[38C\033[1;93m(\033[1;97m$$(du -ah $(TARGETDIR)/btop | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%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"
|
||||||
@printf "\n\033[1;92mBuild complete in \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $(TIMESTAMP) 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo "unknown")\033[92m)\033[0m\n"
|
@printf "\n\033[1;92mBuild complete in \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $(TIMESTAMP) 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo "unknown")\033[92m)\033[0m\n"
|
||||||
|
|
||||||
#? Compile
|
#? Compile
|
||||||
.ONESHELL:
|
.ONESHELL:
|
||||||
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | rocm_smi directories
|
||||||
@sleep 0.3 2>/dev/null || true
|
@sleep 0.3 2>/dev/null || true
|
||||||
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
||||||
@$(QUIET) || printf "\033[1;97mCompiling $<\033[0m\n"
|
@$(QUIET) || printf "\033[1;97mCompiling $<\033[0m\n"
|
||||||
|
@$(VERBOSE) || printf "$(CXX) $(CXXFLAGS) $(INC) -MMD -c -o $@ $<\n"
|
||||||
@$(CXX) $(CXXFLAGS) $(INC) -MMD -c -o $@ $< || exit 1
|
@$(CXX) $(CXXFLAGS) $(INC) -MMD -c -o $@ $< || exit 1
|
||||||
@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"
|
@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"
|
||||||
|
|
||||||
|
469
README.md
469
README.md
@ -5,7 +5,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
|
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
|
||||||
![OSX](https://img.shields.io/badge/-OSX-black?logo=apple)
|
![macOS](https://img.shields.io/badge/-OSX-black?logo=apple)
|
||||||
![FreeBSD](https://img.shields.io/badge/-FreeBSD-red?logo=freebsd)
|
![FreeBSD](https://img.shields.io/badge/-FreeBSD-red?logo=freebsd)
|
||||||
![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow)
|
![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow)
|
||||||
![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green)
|
![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green)
|
||||||
@ -15,7 +15,8 @@
|
|||||||
[![Coffee](https://img.shields.io/badge/-Buy%20me%20a%20Coffee-grey?logo=Ko-fi)](https://ko-fi.com/aristocratos)
|
[![Coffee](https://img.shields.io/badge/-Buy%20me%20a%20Coffee-grey?logo=Ko-fi)](https://ko-fi.com/aristocratos)
|
||||||
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
|
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
|
||||||
[![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 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 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)
|
||||||
|
|
||||||
## Index
|
## Index
|
||||||
|
|
||||||
@ -28,28 +29,55 @@
|
|||||||
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
|
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
|
||||||
* [Screenshots](#screenshots)
|
* [Screenshots](#screenshots)
|
||||||
* [Keybindings](#help-menu)
|
* [Keybindings](#help-menu)
|
||||||
* [Installation Linux/OSX](#installation)
|
* [Installation Linux/macOS](#installation)
|
||||||
* [Compilation Linux](#compilation-linux)
|
* [Compilation Linux](#compilation-linux)
|
||||||
* [Compilation OSX](#compilation-osx)
|
* [Compilation macOS](#compilation-macos-osx)
|
||||||
* [Compilation FreeBSD](#compilation-freebsd)
|
* [Compilation FreeBSD](#compilation-freebsd)
|
||||||
|
* [GPU compatibility](#gpu-compatibility)
|
||||||
* [Installing the snap](#installing-the-snap)
|
* [Installing the snap](#installing-the-snap)
|
||||||
* [Configurability](#configurability)
|
* [Configurability](#configurability)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
|
|
||||||
## News
|
## News
|
||||||
|
|
||||||
|
##### 25 November 2023
|
||||||
|
|
||||||
|
GPU monitoring added for Linux!
|
||||||
|
|
||||||
|
Compile from git main to try it out.
|
||||||
|
|
||||||
|
Use keys `5`, `6`, `7` and `0` to show/hide the gpu monitoring boxes. `5` = Gpu 1, `6` = Gpu 2, etc.
|
||||||
|
|
||||||
|
Gpu stats/graphs can also be displayed in the "Cpu box" (not as verbose), see the cpu options menu for info and configuration.
|
||||||
|
|
||||||
|
Note that the binaries provided on the release page (when released) and the continuous builds will not have gpu support enabled.
|
||||||
|
|
||||||
|
Because the GPU support relies on loading of dynamic gpu libraries, gpu support will not work when also static linking.
|
||||||
|
|
||||||
|
See [Compilation Linux](#compilation-linux) for more info on how to compile with gpu monitoring support.
|
||||||
|
|
||||||
|
Many thanks to [@romner-set](https://github.com/romner-set) who wrote the vast majority of the implementation for GPU support.
|
||||||
|
|
||||||
|
Big update with version bump to 1.3 coming soon.
|
||||||
|
|
||||||
|
##### 28 August 2022
|
||||||
|
|
||||||
|
[![btop4win](https://github.com/aristocratos/btop4win/raw/master/Img/logo.png)](https://github.com/aristocratos/btop4win)
|
||||||
|
|
||||||
|
First release of btop4win available at https://github.com/aristocratos/btop4win
|
||||||
|
|
||||||
##### 16 January 2022
|
##### 16 January 2022
|
||||||
|
|
||||||
Release v1.2.0 with FreeBSD support. No release binaries for FreeBSD provided as of yet.
|
Release v1.2.0 with FreeBSD support. No release binaries for FreeBSD provided as of yet.
|
||||||
|
|
||||||
Again a big thanks to [@joske](https://github.com/joske) for his porting efforts!
|
Again a big thanks to [@joske](https://github.com/joske) for his porting efforts!
|
||||||
|
|
||||||
Since compatibility with Linux, MacOS and FreeBSD are done, the focus going forward will be on new features like GPU monitoring.
|
Since compatibility with Linux, macOS and FreeBSD are done, the focus going forward will be on new features like GPU monitoring.
|
||||||
|
|
||||||
##### 13 November 2021
|
##### 13 November 2021
|
||||||
|
|
||||||
Release v1.1.0 with OSX support. Binaries in [continuous-build-macos](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml) are only x86 for now.
|
Release v1.1.0 with macOS support. Binaries in [continuous-build-macos](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml) are only x86 for now.
|
||||||
Macos binaries + installer are included for both x86 and ARM64 (Apple Silicon) in the releases.
|
macOS binaries + installer are included for both x86 and ARM64 (Apple Silicon) in the releases.
|
||||||
|
|
||||||
Big thank you to [@joske](https://github.com/joske) who wrote the vast majority of the implementation!
|
Big thank you to [@joske](https://github.com/joske) who wrote the vast majority of the implementation!
|
||||||
|
|
||||||
@ -58,12 +86,12 @@ Big thank you to [@joske](https://github.com/joske) who wrote the vast majority
|
|||||||
|
|
||||||
##### 30 October 2021
|
##### 30 October 2021
|
||||||
|
|
||||||
Work on the OSX and FreeBSD branches, both initiated and mostly worked on by [@joske](https://github.com/joske), will likely be completed in the coming weeks.
|
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.
|
||||||
The OSX branch has some memory leaks that needs to be sorted out and both have some issues with the processes cpu usage calculation and other smaller issues that needs fixing.
|
The OSX [macOS] branch has some memory leaks that needs to be sorted out and both have some issues with the processes cpu usage calculation and other smaller issues that needs fixing.
|
||||||
|
|
||||||
If you want to help out, test for bugs/fix bugs or just try out the branches:
|
If you want to help out, test for bugs/fix bugs or just try out the branches:
|
||||||
|
|
||||||
**OSX**
|
**macOS / OSX**
|
||||||
```bash
|
```bash
|
||||||
# Install and use Homebrew or MacPorts package managers for easy dependency installation
|
# Install and use Homebrew or MacPorts package managers for easy dependency installation
|
||||||
brew install coreutils make gcc@11
|
brew install coreutils make gcc@11
|
||||||
@ -82,12 +110,12 @@ git checkout freebsd
|
|||||||
gmake
|
gmake
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that GNU make (`gmake`) is recommended but not required for OSX but it is required on FreeBSD.
|
Note that GNU make (`gmake`) is recommended but not required for macOS/OSX but it is required on FreeBSD.
|
||||||
|
|
||||||
|
|
||||||
##### 6 October 2021
|
##### 6 October 2021
|
||||||
|
|
||||||
OsX development have been started by [@joske](https://github.com/joske), big thanks :)
|
macOS development have been started by [@joske](https://github.com/joske), big thanks :)
|
||||||
See branch [OSX](https://github.com/aristocratos/btop/tree/OSX) for current progress.
|
See branch [OSX](https://github.com/aristocratos/btop/tree/OSX) for current progress.
|
||||||
|
|
||||||
##### 18 September 2021
|
##### 18 September 2021
|
||||||
@ -102,7 +130,7 @@ Please report any bugs to the [Issues](https://github.com/aristocratos/btop/issu
|
|||||||
|
|
||||||
The development plan right now:
|
The development plan right now:
|
||||||
|
|
||||||
* 1.1.0 Mac OsX support
|
* 1.1.0 macOS [OSX] support
|
||||||
* 1.2.0 FreeBSD support
|
* 1.2.0 FreeBSD support
|
||||||
* 1.3.0 Support for GPU monitoring
|
* 1.3.0 Support for GPU monitoring
|
||||||
* 1.X.0 Other platforms and features...
|
* 1.X.0 Other platforms and features...
|
||||||
@ -112,7 +140,7 @@ Windows support is not in the plans as of now, but if anyone else wants to take
|
|||||||
##### 5 May 2021
|
##### 5 May 2021
|
||||||
|
|
||||||
This project is gonna take some time until it has complete feature parity with bpytop, since all system information gathering will have to be written from scratch without any external libraries.
|
This project is gonna take some time until it has complete feature parity with bpytop, since all system information gathering will have to be written from scratch without any external libraries.
|
||||||
And will need some help in the form of code contributions to get complete support for BSD and OSX.
|
And will need some help in the form of code contributions to get complete support for BSD and macOS/OSX.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -142,9 +170,9 @@ C++ version and continuation of [bashtop](https://github.com/aristocratos/bashto
|
|||||||
* Send any signal to selected process.
|
* Send any signal to selected process.
|
||||||
* UI menu for changing all config file options.
|
* UI menu for changing all config file options.
|
||||||
* Auto scaling graph for network usage.
|
* Auto scaling graph for network usage.
|
||||||
* Shows IO activity and speeds for disks
|
* Shows IO activity and speeds for disks.
|
||||||
* Battery meter
|
* Battery meter
|
||||||
* Selectable symbols for the graphs
|
* Selectable symbols for the graphs.
|
||||||
* Custom presets
|
* Custom presets
|
||||||
* And more...
|
* And more...
|
||||||
|
|
||||||
@ -152,7 +180,7 @@ C++ version and continuation of [bashtop](https://github.com/aristocratos/bashto
|
|||||||
|
|
||||||
Btop++ uses the same theme files as bpytop and bashtop (some color values missing in bashtop themes) .
|
Btop++ uses the same theme files as bpytop and bashtop (some color values missing in bashtop themes) .
|
||||||
|
|
||||||
See [themes](https://github.com/aristocratos/btop/tree/master/themes) folder for available themes.
|
See [themes](https://github.com/aristocratos/btop/tree/main/themes) folder for available themes.
|
||||||
|
|
||||||
The `make install` command places the default themes in `[$PREFIX or /usr/local]/share/btop/themes`.
|
The `make install` command places the default themes in `[$PREFIX or /usr/local]/share/btop/themes`.
|
||||||
User created themes should be placed in `$XDG_CONFIG_HOME/btop/themes` or `$HOME/.config/btop/themes`.
|
User created themes should be placed in `$XDG_CONFIG_HOME/btop/themes` or `$HOME/.config/btop/themes`.
|
||||||
@ -234,7 +262,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
|
|
||||||
* **Run install.sh or:**
|
* **Run install.sh or:**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# use "make install PREFIX=/target/dir" to set target, default: /usr/local
|
# use "make install PREFIX=/target/dir" to set target, default: /usr/local
|
||||||
# only use "sudo" when installing to a NON user owned directory
|
# only use "sudo" when installing to a NON user owned directory
|
||||||
sudo make install
|
sudo make install
|
||||||
@ -246,7 +274,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
|
|
||||||
* **Run setuid.sh or:**
|
* **Run setuid.sh or:**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# run after make install and use same PREFIX if any was used at install
|
# 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:root
|
# set SU_USER and SU_GROUP to select user and group, default is root:root
|
||||||
sudo make setuid
|
sudo make setuid
|
||||||
@ -256,7 +284,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
|
|
||||||
* **Run uninstall.sh or:**
|
* **Run uninstall.sh or:**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
sudo make uninstall
|
sudo make uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -274,6 +302,19 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
sudo zypper in btop
|
sudo zypper in btop
|
||||||
```
|
```
|
||||||
* For all other versions, see [openSUSE Software: btop](https://software.opensuse.org/package/btop)
|
* For all other versions, see [openSUSE Software: btop](https://software.opensuse.org/package/btop)
|
||||||
|
* **Fedora**
|
||||||
|
```bash
|
||||||
|
sudo dnf install btop
|
||||||
|
```
|
||||||
|
* **RHEL/AlmaLinux 8+**
|
||||||
|
```bash
|
||||||
|
sudo dnf install epel-release
|
||||||
|
sudo dnf install btop
|
||||||
|
```
|
||||||
|
* **FreeBSD**
|
||||||
|
```sh
|
||||||
|
pkg install btop
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
**Binary release on Homebrew (macOS (x86_64 & ARM64) / Linux (x86_64))**
|
**Binary release on Homebrew (macOS (x86_64 & ARM64) / Linux (x86_64))**
|
||||||
@ -285,75 +326,108 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
|
|
||||||
## Compilation Linux
|
## Compilation Linux
|
||||||
|
|
||||||
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
|
Needs GCC 10 / Clang 16 (or higher).
|
||||||
|
|
||||||
The makefile also needs GNU coreutils and `sed` (should already be installed on any modern distribution).
|
The makefile also needs GNU coreutils and `sed` (should already be installed on any modern distribution).
|
||||||
|
|
||||||
For a `cmake` based build alternative see the [fork](https://github.com/jan-guenter/btop/tree/main) by @jan-guenter
|
### GPU compatibility
|
||||||
|
|
||||||
|
Btop++ supports NVIDIA and AMD GPUs out of the box on Linux x86_64, provided you have the correct drivers and libraries.
|
||||||
|
|
||||||
|
Compatibility with Intel GPUs using generic DRM calls is planned, as is compatibility for FreeBSD and macOS.
|
||||||
|
|
||||||
|
Gpu support will not work when static linking glibc (or musl, etc.)!
|
||||||
|
|
||||||
|
For x86_64 Linux the flag `GPU_SUPPORT` is automatically set to `true`, to manually disable gpu support set the flag to false, like:
|
||||||
|
|
||||||
|
`make GPU_SUPPORT=false` (or `cmake -DBTOP_GPU=false` with CMake)
|
||||||
|
|
||||||
|
* **NVIDIA**
|
||||||
|
|
||||||
|
You must use an official NVIDIA driver, both the closed-source and [open-source](https://github.com/NVIDIA/open-gpu-kernel-modules) 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**
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>
|
||||||
|
|
||||||
|
### With Make
|
||||||
|
|
||||||
|
</summary>
|
||||||
|
|
||||||
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
|
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
|
||||||
|
|
||||||
Use gcc-10 g++-10 if gcc-11 isn't available
|
```bash
|
||||||
|
|
||||||
``` 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
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Clone repository**
|
2. **Clone repository**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
git clone https://github.com/aristocratos/btop.git
|
git clone https://github.com/aristocratos/btop.git
|
||||||
cd btop
|
cd btop
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Compile**
|
3. **Compile**
|
||||||
|
|
||||||
Append `STATIC=true` to `make` command for static compilation.
|
```bash
|
||||||
|
|
||||||
Notice! If using LDAP Authentication, usernames will show as UID number for LDAP users if compiling statically with glibc.
|
|
||||||
|
|
||||||
Append `QUIET=true` for less verbose output.
|
|
||||||
|
|
||||||
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
|
|
||||||
|
|
||||||
Append `ARCH=<architecture>` to manually set the target architecture.
|
|
||||||
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
|
|
||||||
|
|
||||||
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
|
|
||||||
|
|
||||||
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
|
|
||||||
|
|
||||||
If `g++` is linked to an older version of gcc on your system specify the correct version by appending `CXX=g++-10` or `CXX=g++-11`.
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Options for make:
|
||||||
|
|
||||||
|
| Flag | Description |
|
||||||
|
|---------------------------------|-------------------------------------------------------------------------|
|
||||||
|
| `VERBOSE=true` | To display full compiler/linker commands |
|
||||||
|
| `STATIC=true` | For static compilation |
|
||||||
|
| `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 |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
||||||
|
|
||||||
Notice! Only use "sudo" when installing to a NON user owned directory.
|
Notice! Only use "sudo" when installing to a NON user owned directory.
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
||||||
|
```
|
||||||
|
|
||||||
No need for `sudo` to enable signal sending to any process and to prevent /proc read permissions problems on some systems.
|
No need for `sudo` to enable signal sending to any process and to prevent /proc read permissions problems on some systems.
|
||||||
|
|
||||||
Run after make install and use same PREFIX if any was used at install.
|
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 `root`
|
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `root`
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo make setuid
|
|
||||||
```
|
|
||||||
|
|
||||||
* **Uninstall**
|
* **Uninstall**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
sudo make uninstall
|
sudo make uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -375,71 +449,148 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
make help
|
make help
|
||||||
```
|
```
|
||||||
|
|
||||||
## Compilation OSX
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>
|
||||||
|
|
||||||
|
### With CMake (Community maintained)
|
||||||
|
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
1. **Install build dependencies**
|
||||||
|
|
||||||
|
Requires Clang / GCC, CMake, Ninja and Git
|
||||||
|
|
||||||
|
For example, with Debian Bookworm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install cmake git g++ ninja-build
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Clone the repository**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/aristocratos/btop.git && cd btop
|
||||||
|
``````
|
||||||
|
|
||||||
|
3. **Compile**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configure
|
||||||
|
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_STATIC=<ON\|OFF>` | Enables static linking (OFF by default) |
|
||||||
|
| `-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_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) |
|
||||||
|
|
||||||
|
To force a 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>
|
||||||
|
|
||||||
|
## Compilation macOS OSX
|
||||||
|
|
||||||
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
|
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
|
||||||
|
|
||||||
|
GCC 12 needed for macOS Ventura. If you get linker errors on Ventura you'll need to upgrade your command line tools (Version 14.0) is bugged.
|
||||||
|
|
||||||
The makefile also needs GNU coreutils and `sed`.
|
The makefile also needs GNU coreutils and `sed`.
|
||||||
|
|
||||||
Install and use Homebrew or MacPorts package managers for easy dependency installation
|
Install and use Homebrew or MacPorts package managers for easy dependency installation
|
||||||
|
|
||||||
1. **Install dependencies (example for Homebrew)**
|
1. **Install dependencies (example for Homebrew)**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
brew install coreutils make gcc@11
|
brew install coreutils make gcc@12
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Clone repository**
|
2. **Clone repository**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
git clone https://github.com/aristocratos/btop.git
|
git clone https://github.com/aristocratos/btop.git
|
||||||
cd btop
|
cd btop
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Compile**
|
3. **Compile**
|
||||||
|
|
||||||
Append `STATIC=true` to `make` command for static compilation (only libgcc and libstdc++ will be static!).
|
```bash
|
||||||
|
|
||||||
Append `QUIET=true` for less verbose output.
|
|
||||||
|
|
||||||
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
|
|
||||||
|
|
||||||
Append `ARCH=<architecture>` to manually set the target architecture.
|
|
||||||
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
|
|
||||||
|
|
||||||
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
|
|
||||||
|
|
||||||
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
gmake
|
gmake
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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) |
|
||||||
|
| `ARCH=<architecture>` | To manually set the target architecture |
|
||||||
|
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
|
||||||
|
| `CXX=<compiler>` | Manualy set which compiler to use |
|
||||||
|
|
||||||
|
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
|
||||||
|
|
||||||
4. **Install**
|
4. **Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo gmake install
|
||||||
|
```
|
||||||
|
|
||||||
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
||||||
|
|
||||||
Notice! Only use "sudo" when installing to a NON user owned directory.
|
Notice! Only use "sudo" when installing to a NON user owned directory.
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo gmake install
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
||||||
|
```
|
||||||
|
|
||||||
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
|
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.
|
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`
|
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo gmake setuid
|
|
||||||
```
|
|
||||||
|
|
||||||
* **Uninstall**
|
* **Uninstall**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
sudo gmake uninstall
|
sudo gmake uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -467,63 +618,72 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
|
|
||||||
Note that GNU make (`gmake`) is required to compile on FreeBSD.
|
Note that GNU make (`gmake`) is required to compile on FreeBSD.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>
|
||||||
|
|
||||||
|
### With gmake
|
||||||
|
|
||||||
|
</summary>
|
||||||
|
|
||||||
1. **Install dependencies**
|
1. **Install dependencies**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
sudo pkg install gmake gcc11 coreutils git
|
sudo pkg install gmake gcc11 coreutils git
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Clone repository**
|
2. **Clone repository**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
git clone https://github.com/aristocratos/btop.git
|
git clone https://github.com/aristocratos/btop.git
|
||||||
cd btop
|
cd btop
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Compile**
|
3. **Compile**
|
||||||
|
|
||||||
Append `STATIC=true` to `make` command for static compilation.
|
```bash
|
||||||
|
|
||||||
Append `QUIET=true` for less verbose output.
|
|
||||||
|
|
||||||
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
|
|
||||||
|
|
||||||
Append `ARCH=<architecture>` to manually set the target architecture.
|
|
||||||
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
|
|
||||||
|
|
||||||
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
|
|
||||||
|
|
||||||
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
gmake
|
gmake
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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) |
|
||||||
|
| `ARCH=<architecture>` | To manually set the target architecture |
|
||||||
|
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
|
||||||
|
| `CXX=<compiler>` | Manualy set which compiler to use |
|
||||||
|
|
||||||
|
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
|
||||||
|
|
||||||
4. **Install**
|
4. **Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo gmake install
|
||||||
|
```
|
||||||
|
|
||||||
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
|
||||||
|
|
||||||
Notice! Only use "sudo" when installing to a NON user owned directory.
|
Notice! Only use "sudo" when installing to a NON user owned directory.
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo gmake install
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
||||||
|
```
|
||||||
|
|
||||||
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
|
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.
|
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`
|
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
|
||||||
|
|
||||||
``` bash
|
|
||||||
sudo gmake setuid
|
|
||||||
```
|
|
||||||
|
|
||||||
* **Uninstall**
|
* **Uninstall**
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
sudo gmake uninstall
|
sudo gmake uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -545,6 +705,96 @@ Also needs a UTF8 locale and a font that covers:
|
|||||||
gmake help
|
gmake help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>
|
||||||
|
|
||||||
|
### With CMake (Community maintained)
|
||||||
|
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
1. **Install build dependencies**
|
||||||
|
|
||||||
|
Requires Clang / GCC, CMake, Ninja 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
|
||||||
|
```
|
||||||
|
|
||||||
|
FreeBSD 13:
|
||||||
|
```bash
|
||||||
|
pkg install cmake gcc13 ninja
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Clone the repository**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/aristocratos/btop.git && cd btop
|
||||||
|
``````
|
||||||
|
|
||||||
|
3. **Compile**
|
||||||
|
|
||||||
|
FreeBSD 14 and later:
|
||||||
|
```bash
|
||||||
|
# Configure
|
||||||
|
cmake -B build -G Ninja
|
||||||
|
# Build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
FreeBSD 13:
|
||||||
|
```bash
|
||||||
|
# Configure
|
||||||
|
CXX=g++13 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_STATIC=<ON\|OFF>` | Enables static linking (OFF by default) |
|
||||||
|
| `-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) |
|
||||||
|
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
|
||||||
|
|
||||||
|
_**Note:** Static linking does not work with GCC._
|
||||||
|
|
||||||
|
To force a 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
|
## Installing the snap
|
||||||
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
|
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
|
||||||
|
|
||||||
@ -599,7 +849,7 @@ force_tty = False
|
|||||||
|
|
||||||
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
|
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
|
||||||
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
|
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
|
||||||
#* Use withespace " " as separator between different presets.
|
#* Use whitespace " " as separator between different presets.
|
||||||
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
|
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
|
||||||
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
|
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
|
||||||
|
|
||||||
@ -629,7 +879,7 @@ graph_symbol_net = "default"
|
|||||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||||
graph_symbol_proc = "default"
|
graph_symbol_proc = "default"
|
||||||
|
|
||||||
#* Manually set which boxes to show. Available values are "cpu mem net proc", separate values with whitespace.
|
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
|
||||||
shown_boxes = "proc cpu mem net"
|
shown_boxes = "proc cpu mem net"
|
||||||
|
|
||||||
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
|
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
|
||||||
@ -727,6 +977,9 @@ mem_graphs = True
|
|||||||
#* Show mem box below net box instead of above.
|
#* Show mem box below net box instead of above.
|
||||||
mem_below_net = False
|
mem_below_net = False
|
||||||
|
|
||||||
|
#* Count ZFS ARC in cached and available memory.
|
||||||
|
zfs_arc_cached = True
|
||||||
|
|
||||||
#* If swap memory should be shown in memory box.
|
#* If swap memory should be shown in memory box.
|
||||||
show_swap = True
|
show_swap = True
|
||||||
|
|
||||||
|
23
cmake/Modules/Finddevstat.cmake
Normal file
23
cmake/Modules/Finddevstat.cmake
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Find devstat, the Device Statistics Library
|
||||||
|
#
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
find_path(devstat_INCLUDE_DIR NAMES devstat.h)
|
||||||
|
find_library(devstat_LIBRARY NAMES devstat)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(devstat REQUIRED_VARS devstat_LIBRARY devstat_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if(devstat_FOUND AND NOT TARGET devstat::devstat)
|
||||||
|
add_library(devstat::devstat UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(devstat::devstat PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${devstat_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${devstat_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(devstat_INCLUDE_DIR devstat_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
23
cmake/Modules/Findelf.cmake
Normal file
23
cmake/Modules/Findelf.cmake
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Find libelf, the ELF Access Library
|
||||||
|
#
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
find_path(elf_INCLUDE_DIR NAMES libelf.h)
|
||||||
|
find_library(elf_LIBRARY NAMES elf)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(elf REQUIRED_VARS elf_LIBRARY elf_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if(elf_FOUND AND NOT TARGET elf::elf)
|
||||||
|
add_library(elf::elf UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(elf::elf PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${elf_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${elf_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(elf_INCLUDE_DIR elf_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
23
cmake/Modules/Findkvm.cmake
Normal file
23
cmake/Modules/Findkvm.cmake
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Find libkvm, the Kernel Data Access Library
|
||||||
|
#
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
find_path(kvm_INCLUDE_DIR NAMES kvm.h)
|
||||||
|
find_library(kvm_LIBRARY NAMES kvm)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(kvm REQUIRED_VARS kvm_LIBRARY kvm_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if(kvm_FOUND AND NOT TARGET kvm::kvm)
|
||||||
|
add_library(kvm::kvm UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(kvm::kvm PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${kvm_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${kvm_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(kvm_INCLUDE_DIR kvm_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
27
include/fmt/LICENSE.rst
Normal file
27
include/fmt/LICENSE.rst
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
--- Optional exception to the license ---
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into a machine-executable object form of such
|
||||||
|
source code, you may redistribute such embedded portions in such object form
|
||||||
|
without including the above copyright and permission notices.
|
234
include/fmt/args.h
Normal file
234
include/fmt/args.h
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
// Formatting library for C++ - dynamic format arguments
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_ARGS_H_
|
||||||
|
#define FMT_ARGS_H_
|
||||||
|
|
||||||
|
#include <functional> // std::reference_wrapper
|
||||||
|
#include <memory> // std::unique_ptr
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> const T& unwrap(const T& v) { return v; }
|
||||||
|
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
||||||
|
return static_cast<const T&>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
class dynamic_arg_list {
|
||||||
|
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||||
|
// templates it doesn't complain about inability to deduce single translation
|
||||||
|
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||||
|
template <typename = void> struct node {
|
||||||
|
virtual ~node() = default;
|
||||||
|
std::unique_ptr<node<>> next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct typed_node : node<> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||||
|
: value(arg.data(), arg.size()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<node<>> head_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||||
|
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||||
|
auto& value = new_node->value;
|
||||||
|
new_node->next = std::move(head_);
|
||||||
|
head_ = std::move(new_node);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A dynamic version of `fmt::format_arg_store`.
|
||||||
|
It's equipped with a storage to potentially temporary objects which lifetimes
|
||||||
|
could be shorter than the format arguments object.
|
||||||
|
|
||||||
|
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
||||||
|
into type-erased formatting functions such as `~fmt::vformat`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Context>
|
||||||
|
class dynamic_format_arg_store
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
// Workaround a GCC template argument substitution bug.
|
||||||
|
: public basic_format_args<Context>
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
template <typename T> struct need_copy {
|
||||||
|
static constexpr detail::type mapped_type =
|
||||||
|
detail::mapped_type_constant<T, Context>::value;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
value = !(detail::is_reference_wrapper<T>::value ||
|
||||||
|
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||||
|
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||||
|
(mapped_type != detail::type::cstring_type &&
|
||||||
|
mapped_type != detail::type::string_type &&
|
||||||
|
mapped_type != detail::type::custom_type))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using stored_type = conditional_t<
|
||||||
|
std::is_convertible<T, std::basic_string<char_type>>::value &&
|
||||||
|
!detail::is_reference_wrapper<T>::value,
|
||||||
|
std::basic_string<char_type>, T>;
|
||||||
|
|
||||||
|
// Storage of basic_format_arg must be contiguous.
|
||||||
|
std::vector<basic_format_arg<Context>> data_;
|
||||||
|
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||||
|
|
||||||
|
// Storage of arguments not fitting into basic_format_arg must grow
|
||||||
|
// without relocation because items in data_ refer to it.
|
||||||
|
detail::dynamic_arg_list dynamic_args_;
|
||||||
|
|
||||||
|
friend class basic_format_args<Context>;
|
||||||
|
|
||||||
|
unsigned long long get_types() const {
|
||||||
|
return detail::is_unpacked_bit | data_.size() |
|
||||||
|
(named_info_.empty()
|
||||||
|
? 0ULL
|
||||||
|
: static_cast<unsigned long long>(detail::has_named_args_bit));
|
||||||
|
}
|
||||||
|
|
||||||
|
const basic_format_arg<Context>* data() const {
|
||||||
|
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void emplace_arg(const T& arg) {
|
||||||
|
data_.emplace_back(detail::make_arg<Context>(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||||
|
if (named_info_.empty()) {
|
||||||
|
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
|
||||||
|
data_.insert(data_.begin(), {zero_ptr, 0});
|
||||||
|
}
|
||||||
|
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
|
||||||
|
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||||
|
data->pop_back();
|
||||||
|
};
|
||||||
|
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||||
|
guard{&data_, pop_one};
|
||||||
|
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||||
|
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
|
||||||
|
guard.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr dynamic_format_arg_store() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Adds an argument into the dynamic store for later passing to a formatting
|
||||||
|
function.
|
||||||
|
|
||||||
|
Note that custom types and string types (but not string views) are copied
|
||||||
|
into the store dynamically allocating memory if necessary.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
store.push_back(42);
|
||||||
|
store.push_back("abc");
|
||||||
|
store.push_back(1.5f);
|
||||||
|
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T> void push_back(const T& arg) {
|
||||||
|
if (detail::const_check(need_copy<T>::value))
|
||||||
|
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||||
|
else
|
||||||
|
emplace_arg(detail::unwrap(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Adds a reference to the argument into the dynamic store for later passing to
|
||||||
|
a formatting function.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
char band[] = "Rolling Stones";
|
||||||
|
store.push_back(std::cref(band));
|
||||||
|
band[9] = 'c'; // Changing str affects the output.
|
||||||
|
std::string result = fmt::vformat("{}", store);
|
||||||
|
// result == "Rolling Scones"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||||
|
static_assert(
|
||||||
|
need_copy<T>::value,
|
||||||
|
"objects of built-in types and string views are always copied");
|
||||||
|
emplace_arg(arg.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds named argument into the dynamic store for later passing to a formatting
|
||||||
|
function. ``std::reference_wrapper`` is supported to avoid copying of the
|
||||||
|
argument. The name is always copied into the store.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||||
|
const char_type* arg_name =
|
||||||
|
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||||
|
if (detail::const_check(need_copy<T>::value)) {
|
||||||
|
emplace_arg(
|
||||||
|
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
|
||||||
|
} else {
|
||||||
|
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Erase all elements from the store */
|
||||||
|
void clear() {
|
||||||
|
data_.clear();
|
||||||
|
named_info_.clear();
|
||||||
|
dynamic_args_ = detail::dynamic_arg_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Reserves space to store at least *new_cap* arguments including
|
||||||
|
*new_cap_named* named arguments.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||||
|
FMT_ASSERT(new_cap >= new_cap_named,
|
||||||
|
"Set of arguments includes set of named arguments");
|
||||||
|
data_.reserve(new_cap);
|
||||||
|
named_info_.reserve(new_cap_named);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_ARGS_H_
|
2268
include/fmt/chrono.h
Normal file
2268
include/fmt/chrono.h
Normal file
File diff suppressed because it is too large
Load Diff
633
include/fmt/color.h
Normal file
633
include/fmt/color.h
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
faint = 1 << 1,
|
||||||
|
italic = 1 << 2,
|
||||||
|
underline = 1 << 3,
|
||||||
|
blink = 1 << 4,
|
||||||
|
reverse = 1 << 5,
|
||||||
|
conceal = 1 << 6,
|
||||||
|
strikethrough = 1 << 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||||
|
g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||||
|
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
|
||||||
|
: is_rgb(), value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
/** A text style consisting of foreground and background colors and emphasis. */
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||||
|
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||||
|
const text_style& rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const noexcept {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const noexcept {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const noexcept {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
|
||||||
|
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR detail::color_type get_background() const noexcept {
|
||||||
|
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
|
||||||
|
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
detail::color_type text_color) noexcept
|
||||||
|
: set_foreground_color(), set_background_color(), ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
|
||||||
|
|
||||||
|
detail::color_type foreground_color;
|
||||||
|
detail::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Creates a text style from the foreground (text) color. */
|
||||||
|
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
|
||||||
|
return text_style(true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a text style from the background color. */
|
||||||
|
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
|
||||||
|
return text_style(false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char> struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||||
|
const char* esc) noexcept {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == string_view("\x1b[48;2;");
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background) value += 10u;
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
||||||
|
uint8_t em_codes[num_emphases] = {};
|
||||||
|
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||||
|
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||||
|
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||||
|
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||||
|
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||||
|
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||||
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||||
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (size_t i = 0; i < num_emphases; ++i) {
|
||||||
|
if (!em_codes[i]) continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
|
||||||
|
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
|
||||||
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr size_t num_emphases = 8;
|
||||||
|
Char buffer[7u + 3u * num_emphases + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
|
char delimiter) noexcept {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
|
||||||
|
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
|
detail::color_type foreground) noexcept {
|
||||||
|
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
|
detail::color_type background) noexcept {
|
||||||
|
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||||
|
auto reset_color = string_view("\x1b[0m");
|
||||||
|
buffer.append(reset_color.begin(), reset_color.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> struct styled_arg {
|
||||||
|
const T& value;
|
||||||
|
text_style style;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||||
|
buf.append(emphasis.begin(), emphasis.end());
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||||
|
buf.append(foreground.begin(), foreground.end());
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||||
|
buf.append(background.begin(), background.end());
|
||||||
|
}
|
||||||
|
detail::vformat_to(buf, format_str, args, {});
|
||||||
|
if (has_style) detail::reset_color<Char>(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
|
||||||
|
format_args args) {
|
||||||
|
// Legacy wide streams are not supported.
|
||||||
|
auto buf = memory_buffer();
|
||||||
|
detail::vformat_to(buf, ts, fmt, args);
|
||||||
|
if (detail::is_utf8()) {
|
||||||
|
detail::print(f, string_view(buf.begin(), buf.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf.push_back('\0');
|
||||||
|
int result = std::fputs(buf.data(), f);
|
||||||
|
if (result < 0)
|
||||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
vprint(f, ts, format_str,
|
||||||
|
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
|
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const text_style& ts, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
|
||||||
|
return fmt::to_string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"The answer is {}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
return fmt::vformat(ts, detail::to_string_view(format_str),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string with the given text_style and writes the output to ``out``.
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename Char,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
OutputIt vformat_to(
|
||||||
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
|
detail::vformat_to(buf, ts, format_str, args);
|
||||||
|
return detail::get_iterator(buf, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments with the given text_style, writes the result to the output
|
||||||
|
iterator ``out`` and returns the iterator past the end of the output range.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out),
|
||||||
|
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||||
|
detail::is_string<S>::value>
|
||||||
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||||
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
return vformat_to(out, ts, detail::to_string_view(format_str),
|
||||||
|
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
const auto& ts = arg.style;
|
||||||
|
const auto& value = arg.value;
|
||||||
|
auto out = ctx.out();
|
||||||
|
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||||
|
out = std::copy(emphasis.begin(), emphasis.end(), out);
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
auto foreground =
|
||||||
|
detail::make_foreground_color<Char>(ts.get_foreground());
|
||||||
|
out = std::copy(foreground.begin(), foreground.end(), out);
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
auto background =
|
||||||
|
detail::make_background_color<Char>(ts.get_background());
|
||||||
|
out = std::copy(background.begin(), background.end(), out);
|
||||||
|
}
|
||||||
|
out = formatter<T, Char>::format(value, ctx);
|
||||||
|
if (has_style) {
|
||||||
|
auto reset_color = string_view("\x1b[0m");
|
||||||
|
out = std::copy(reset_color.begin(), reset_color.end(), out);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an argument that will be formatted using ANSI escape sequences,
|
||||||
|
to be used in a formatting function.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("Elapsed time: {0:.2f} seconds",
|
||||||
|
fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
||||||
|
fmt::bg(fmt::color::blue)));
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
|
||||||
|
-> detail::styled_arg<remove_cvref_t<T>> {
|
||||||
|
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
605
include/fmt/compile.h
Normal file
605
include/fmt/compile.h
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
// Formatting library for C++ - experimental format string compilation
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COMPILE_H_
|
||||||
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Char, typename InputIt>
|
||||||
|
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||||
|
counting_iterator it) {
|
||||||
|
return it + (end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> class truncating_iterator_base {
|
||||||
|
protected:
|
||||||
|
OutputIt out_;
|
||||||
|
size_t limit_;
|
||||||
|
size_t count_ = 0;
|
||||||
|
|
||||||
|
truncating_iterator_base() : out_(), limit_(0) {}
|
||||||
|
|
||||||
|
truncating_iterator_base(OutputIt out, size_t limit)
|
||||||
|
: out_(out), limit_(limit) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::output_iterator_tag;
|
||||||
|
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
|
||||||
|
|
||||||
|
OutputIt base() const { return out_; }
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that truncates the output and counts the number of objects
|
||||||
|
// written to it.
|
||||||
|
template <typename OutputIt,
|
||||||
|
typename Enable = typename std::is_void<
|
||||||
|
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||||
|
class truncating_iterator;
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::false_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||||
|
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() {
|
||||||
|
if (this->count_++ < this->limit_) ++this->out_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator operator++(int) {
|
||||||
|
auto it = *this;
|
||||||
|
++*this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator*() const {
|
||||||
|
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::true_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
public:
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
template <typename T> truncating_iterator& operator=(T val) {
|
||||||
|
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() { return *this; }
|
||||||
|
truncating_iterator& operator++(int) { return *this; }
|
||||||
|
truncating_iterator& operator*() { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
|
class compiled_string {};
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Converts a string literal *s* into a format string that will be parsed at
|
||||||
|
compile time and converted into efficient formatting code. Requires C++17
|
||||||
|
``constexpr if`` compiler support.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
// Converts 42 into std::string using the most efficient method and no
|
||||||
|
// runtime format string processing.
|
||||||
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
# define FMT_COMPILE(s) \
|
||||||
|
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||||
|
#else
|
||||||
|
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
|
template <typename Char, size_t N,
|
||||||
|
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||||
|
struct udl_compiled_string : compiled_string {
|
||||||
|
using char_type = Char;
|
||||||
|
explicit constexpr operator basic_string_view<char_type>() const {
|
||||||
|
return {Str.data, N - 1};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, typename... Tail>
|
||||||
|
const T& first(const T& value, const Tail&...) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
|
template <int N, typename T, typename... Args>
|
||||||
|
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
|
[[maybe_unused]] const Args&... rest) {
|
||||||
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
|
if constexpr (N == 0)
|
||||||
|
return first;
|
||||||
|
else
|
||||||
|
return detail::get<N - 1>(rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename... Args>
|
||||||
|
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||||
|
type_list<Args...>) {
|
||||||
|
return get_arg_index_by_name<Args...>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N, typename> struct get_type_impl;
|
||||||
|
|
||||||
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
|
using type =
|
||||||
|
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int N, typename T>
|
||||||
|
using get_type = typename get_type_impl<N, T>::type;
|
||||||
|
|
||||||
|
template <typename T> struct is_compiled_format : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Char> struct text {
|
||||||
|
basic_string_view<Char> data;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
return write<Char>(out, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||||
|
size_t size) {
|
||||||
|
return {{&s[pos], size}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct code_unit {
|
||||||
|
Char value;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
return write<Char>(out, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This ensures that the argument type is convertible to `const T&`.
|
||||||
|
template <typename T, int N, typename... Args>
|
||||||
|
constexpr const T& get_arg_checked(const Args&... args) {
|
||||||
|
const auto& arg = detail::get<N>(args...);
|
||||||
|
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||||
|
return arg.value;
|
||||||
|
} else {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
// A replacement field that refers to argument N.
|
||||||
|
template <typename Char, typename T, int N> struct field {
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename T, int N>
|
||||||
|
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
// A replacement field that refers to argument with name.
|
||||||
|
template <typename Char> struct runtime_named_field {
|
||||||
|
using char_type = Char;
|
||||||
|
basic_string_view<Char> name;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename T>
|
||||||
|
constexpr static bool try_format_argument(
|
||||||
|
OutputIt& out,
|
||||||
|
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||||
|
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
||||||
|
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||||
|
if (arg_name == arg.name) {
|
||||||
|
out = write<Char>(out, arg.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
bool found = (try_format_argument(out, name, args) || ...);
|
||||||
|
if (!found) {
|
||||||
|
FMT_THROW(format_error("argument with specified name is not found"));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
// A replacement field that refers to argument N and has format specifiers.
|
||||||
|
template <typename Char, typename T, int N> struct spec_field {
|
||||||
|
using char_type = Char;
|
||||||
|
formatter<T, Char> fmt;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||||
|
const Args&... args) const {
|
||||||
|
const auto& vargs =
|
||||||
|
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
|
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename T, int N>
|
||||||
|
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename L, typename R> struct concat {
|
||||||
|
L lhs;
|
||||||
|
R rhs;
|
||||||
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
out = lhs.format(out, args...);
|
||||||
|
return rhs.format(out, args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||||
|
return {lhs, rhs};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct unknown_format {};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||||
|
for (size_t size = str.size(); pos != size; ++pos) {
|
||||||
|
if (str[pos] == '{' || str[pos] == '}') break;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
|
constexpr auto compile_format_string(S format_str);
|
||||||
|
|
||||||
|
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||||
|
constexpr auto parse_tail(T head, S format_str) {
|
||||||
|
if constexpr (POS !=
|
||||||
|
basic_string_view<typename S::char_type>(format_str).size()) {
|
||||||
|
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||||
|
unknown_format>())
|
||||||
|
return tail;
|
||||||
|
else
|
||||||
|
return make_concat(head, tail);
|
||||||
|
} else {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
|
formatter<T, Char> fmt;
|
||||||
|
size_t end;
|
||||||
|
int next_arg_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { manual_indexing_id = -1 };
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
|
size_t pos, int next_arg_id) {
|
||||||
|
str.remove_prefix(pos);
|
||||||
|
auto ctx =
|
||||||
|
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
|
||||||
|
auto f = formatter<T, Char>();
|
||||||
|
auto end = f.parse(ctx);
|
||||||
|
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
|
||||||
|
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct arg_id_handler {
|
||||||
|
arg_ref<Char> arg_id;
|
||||||
|
|
||||||
|
constexpr int on_auto() {
|
||||||
|
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
constexpr int on_index(int id) {
|
||||||
|
arg_id = arg_ref<Char>(id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
constexpr int on_name(basic_string_view<Char> id) {
|
||||||
|
arg_id = arg_ref<Char>(id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char> struct parse_arg_id_result {
|
||||||
|
arg_ref<Char> arg_id;
|
||||||
|
const Char* arg_id_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int ID, typename Char>
|
||||||
|
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||||
|
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
|
||||||
|
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||||
|
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct field_type {
|
||||||
|
using type = remove_cvref_t<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||||
|
using type = remove_cvref_t<decltype(T::value)>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||||
|
typename S>
|
||||||
|
constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||||
|
using char_type = typename S::char_type;
|
||||||
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
|
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||||
|
if constexpr (c == '}') {
|
||||||
|
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||||
|
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (c != ':') {
|
||||||
|
FMT_THROW(format_error("expected ':'"));
|
||||||
|
} else {
|
||||||
|
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||||
|
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||||
|
if constexpr (result.end >= str.size() || str[result.end] != '}') {
|
||||||
|
FMT_THROW(format_error("expected '}'"));
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, result.end + 1, result.next_arg_id>(
|
||||||
|
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||||
|
result.fmt},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
|
// or unknown_format() on unrecognized input.
|
||||||
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
|
constexpr auto compile_format_string(S format_str) {
|
||||||
|
using char_type = typename S::char_type;
|
||||||
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
|
if constexpr (str[POS] == '{') {
|
||||||
|
if constexpr (POS + 1 == str.size())
|
||||||
|
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||||
|
if constexpr (str[POS + 1] == '{') {
|
||||||
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
|
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||||
|
static_assert(ID != manual_indexing_id,
|
||||||
|
"cannot switch from manual to automatic argument indexing");
|
||||||
|
constexpr auto next_id =
|
||||||
|
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||||
|
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||||
|
POS + 1, ID, next_id>(
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
constexpr auto arg_id_result =
|
||||||
|
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||||
|
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||||
|
constexpr char_type c =
|
||||||
|
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||||
|
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||||
|
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
|
||||||
|
static_assert(
|
||||||
|
ID == manual_indexing_id || ID == 0,
|
||||||
|
"cannot switch from automatic to manual argument indexing");
|
||||||
|
constexpr auto arg_index = arg_id_result.arg_id.val.index;
|
||||||
|
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||||
|
Args, arg_id_end_pos,
|
||||||
|
arg_index, manual_indexing_id>(
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
|
||||||
|
constexpr auto arg_index =
|
||||||
|
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
|
||||||
|
if constexpr (arg_index >= 0) {
|
||||||
|
constexpr auto next_id =
|
||||||
|
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||||
|
return parse_replacement_field_then_tail<
|
||||||
|
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||||
|
arg_index, next_id>(format_str);
|
||||||
|
} else if constexpr (c == '}') {
|
||||||
|
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||||
|
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (c == ':') {
|
||||||
|
return unknown_format(); // no type info for specs parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if constexpr (str[POS] == '}') {
|
||||||
|
if constexpr (POS + 1 == str.size())
|
||||||
|
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||||
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
|
} else {
|
||||||
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
|
if constexpr (end - POS > 1) {
|
||||||
|
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
constexpr auto compile(S format_str) {
|
||||||
|
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||||
|
if constexpr (str.size() == 0) {
|
||||||
|
return detail::make_text(str, 0, 0);
|
||||||
|
} else {
|
||||||
|
constexpr auto result =
|
||||||
|
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||||
|
format_str);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
auto s = std::basic_string<Char>();
|
||||||
|
cf.format(std::back_inserter(s), args...);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
|
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
return cf.format(out, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
|
Args&&... args) {
|
||||||
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
|
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||||
|
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||||
|
const auto& first = detail::first(args...);
|
||||||
|
if constexpr (detail::is_named_arg<
|
||||||
|
remove_cvref_t<decltype(first)>>::value) {
|
||||||
|
return fmt::to_string(first.value);
|
||||||
|
} else {
|
||||||
|
return fmt::to_string(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
|
detail::unknown_format>()) {
|
||||||
|
return fmt::format(
|
||||||
|
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
return fmt::format(compiled, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
|
detail::unknown_format>()) {
|
||||||
|
return fmt::format_to(
|
||||||
|
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
|
const S& format_str, Args&&... args) {
|
||||||
|
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
|
||||||
|
format_str, std::forward<Args>(args)...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
return fmt::format_to(detail::counting_iterator(), format_str, args...)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
fmt::format_to(std::back_inserter(buffer), format_str, args...);
|
||||||
|
detail::print(f, {buffer.data(), buffer.size()});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
void print(const S& format_str, const Args&... args) {
|
||||||
|
print(stdout, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
|
inline namespace literals {
|
||||||
|
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
|
||||||
|
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||||
|
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||||
|
Str>();
|
||||||
|
}
|
||||||
|
} // namespace literals
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COMPILE_H_
|
2905
include/fmt/core.h
Normal file
2905
include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load Diff
1662
include/fmt/format-inl.h
Normal file
1662
include/fmt/format-inl.h
Normal file
File diff suppressed because it is too large
Load Diff
4731
include/fmt/format.h
Normal file
4731
include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load Diff
451
include/fmt/os.h
Normal file
451
include/fmt/os.h
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
// Formatting library for C++ - optional OS-specific functionality
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OS_H_
|
||||||
|
#define FMT_OS_H_
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <system_error> // std::system_error
|
||||||
|
|
||||||
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FCNTL
|
||||||
|
// UWP doesn't provide _pipe.
|
||||||
|
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
|
# include <winapifamily.h>
|
||||||
|
# endif
|
||||||
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
|
(!defined(WINAPI_FAMILY) || \
|
||||||
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
|
# include <fcntl.h> // for O_RDONLY
|
||||||
|
# define FMT_USE_FCNTL 1
|
||||||
|
# else
|
||||||
|
# define FMT_USE_FCNTL 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX
|
||||||
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX(call) _##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX(call) call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
|
#ifdef FMT_SYSTEM
|
||||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
|
#else
|
||||||
|
# define FMT_SYSTEM(call) ::call
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Retries the expression while it evaluates to error_result and errno
|
||||||
|
// equals to EINTR.
|
||||||
|
#ifndef _WIN32
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||||
|
do { \
|
||||||
|
(result) = (expression); \
|
||||||
|
} while ((result) == (error_result) && errno == EINTR)
|
||||||
|
#else
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A reference to a null-terminated string. It can be constructed from a C
|
||||||
|
string or ``std::string``.
|
||||||
|
|
||||||
|
You can use one of the following type aliases for common character types:
|
||||||
|
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| Type | Definition |
|
||||||
|
+===============+=============================+
|
||||||
|
| cstring_view | basic_cstring_view<char> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
|
||||||
|
This class is most useful as a parameter type to allow passing
|
||||||
|
different types of strings to a function, for example::
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string format(cstring_view format_str, const Args & ... args);
|
||||||
|
|
||||||
|
format("{}", 42);
|
||||||
|
format(std::string("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Char> class basic_cstring_view {
|
||||||
|
private:
|
||||||
|
const Char* data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructs a string reference object from a C string. */
|
||||||
|
basic_cstring_view(const Char* s) : data_(s) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a string reference from an ``std::string`` object.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||||
|
|
||||||
|
/** Returns the pointer to a C string. */
|
||||||
|
const Char* c_str() const { return data_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using cstring_view = basic_cstring_view<char>;
|
||||||
|
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
FMT_API const std::error_category& system_category() noexcept;
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||||
|
const char* message) noexcept;
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||||
|
format_args args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a :class:`std::system_error` object with the description
|
||||||
|
of the form
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
*<message>*: *<system-message>*
|
||||||
|
|
||||||
|
where *<message>* is the formatted message and *<system-message>* is the
|
||||||
|
system message corresponding to the error code.
|
||||||
|
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||||
|
If *error_code* is not a valid error code such as -1, the system message
|
||||||
|
will look like "error -1".
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
// This throws a system_error with the description
|
||||||
|
// cannot open file 'madeup': The system cannot find the file specified.
|
||||||
|
// or similar (system message may vary).
|
||||||
|
const char *filename = "madeup";
|
||||||
|
LPOFSTRUCT of = LPOFSTRUCT();
|
||||||
|
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||||
|
if (file == HFILE_ERROR) {
|
||||||
|
throw fmt::windows_error(GetLastError(),
|
||||||
|
"cannot open file '{}'", filename);
|
||||||
|
}
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
std::system_error windows_error(int error_code, string_view message,
|
||||||
|
const Args&... args) {
|
||||||
|
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reports a Windows error without throwing an exception.
|
||||||
|
// Can be used to report errors from destructors.
|
||||||
|
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
|
||||||
|
#else
|
||||||
|
inline const std::error_category& system_category() noexcept {
|
||||||
|
return std::system_category();
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// std::system is not available on some platforms such as iOS (#2248).
|
||||||
|
#ifdef __OSX__
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
void say(const S& format_str, Args&&... args) {
|
||||||
|
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A buffered file.
|
||||||
|
class buffered_file {
|
||||||
|
private:
|
||||||
|
FILE* file_;
|
||||||
|
|
||||||
|
friend class file;
|
||||||
|
|
||||||
|
explicit buffered_file(FILE* f) : file_(f) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
buffered_file(const buffered_file&) = delete;
|
||||||
|
void operator=(const buffered_file&) = delete;
|
||||||
|
|
||||||
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
|
buffered_file() noexcept : file_(nullptr) {}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~buffered_file() noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
||||||
|
other.file_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file& operator=(buffered_file&& other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens a file.
|
||||||
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the pointer to a FILE object representing this file.
|
||||||
|
FILE* get() const noexcept { return file_; }
|
||||||
|
|
||||||
|
FMT_API int descriptor() const;
|
||||||
|
|
||||||
|
void vprint(string_view format_str, format_args args) {
|
||||||
|
fmt::vprint(file_, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(string_view format_str, const Args&... args) {
|
||||||
|
vprint(format_str, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if FMT_USE_FCNTL
|
||||||
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
|
// Methods that are not declared with noexcept may throw
|
||||||
|
// fmt::system_error in case of failure. Note that some errors such as
|
||||||
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
|
// than an exception. You can get standard behavior by overriding the
|
||||||
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
|
class FMT_API file {
|
||||||
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
|
// Constructs a file object with a given descriptor.
|
||||||
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
enum {
|
||||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
|
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||||
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||||
|
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
|
||||||
|
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
file() noexcept : fd_(-1) {}
|
||||||
|
|
||||||
|
// Opens a file and constructs a file object representing this file.
|
||||||
|
file(cstring_view path, int oflag);
|
||||||
|
|
||||||
|
public:
|
||||||
|
file(const file&) = delete;
|
||||||
|
void operator=(const file&) = delete;
|
||||||
|
|
||||||
|
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
||||||
|
|
||||||
|
// Move assignment is not noexcept because close may throw.
|
||||||
|
file& operator=(file&& other) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
~file() noexcept;
|
||||||
|
|
||||||
|
// Returns the file descriptor.
|
||||||
|
int descriptor() const noexcept { return fd_; }
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// Returns the file size. The size has signed type for consistency with
|
||||||
|
// stat::st_size.
|
||||||
|
long long size() const;
|
||||||
|
|
||||||
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
|
size_t read(void* buffer, size_t count);
|
||||||
|
|
||||||
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
|
size_t write(const void* buffer, size_t count);
|
||||||
|
|
||||||
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
|
// the duplicate as a file object.
|
||||||
|
static file dup(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
void dup2(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
void dup2(int fd, std::error_code& ec) noexcept;
|
||||||
|
|
||||||
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
|
// and writing respectively.
|
||||||
|
static void pipe(file& read_end, file& write_end);
|
||||||
|
|
||||||
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
|
// this file object from the file.
|
||||||
|
buffered_file fdopen(const char* mode);
|
||||||
|
|
||||||
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Opens a file and constructs a file object representing this file by
|
||||||
|
// wcstring_view filename. Windows only.
|
||||||
|
static file open_windows_file(wcstring_view path, int oflag);
|
||||||
|
# endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the memory page size.
|
||||||
|
long getpagesize();
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
struct buffer_size {
|
||||||
|
buffer_size() = default;
|
||||||
|
size_t value = 0;
|
||||||
|
buffer_size operator=(size_t val) const {
|
||||||
|
auto bs = buffer_size();
|
||||||
|
bs.value = val;
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ostream_params {
|
||||||
|
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||||
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
|
ostream_params() {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||||
|
oflag = new_oflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, detail::buffer_size bs)
|
||||||
|
: ostream_params(params...) {
|
||||||
|
this->buffer_size = bs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intel has a bug that results in failure to deduce a constructor
|
||||||
|
// for empty parameter packs.
|
||||||
|
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||||
|
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||||
|
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||||
|
# endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class file_buffer final : public buffer<char> {
|
||||||
|
file file_;
|
||||||
|
|
||||||
|
FMT_API void grow(size_t) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_API file_buffer(cstring_view path, const ostream_params& params);
|
||||||
|
FMT_API file_buffer(file_buffer&& other);
|
||||||
|
FMT_API ~file_buffer();
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
if (size() == 0) return;
|
||||||
|
file_.write(data(), size() * sizeof(data()[0]));
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
flush();
|
||||||
|
file_.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
// Added {} below to work around default constructor error known to
|
||||||
|
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||||
|
constexpr detail::buffer_size buffer_size{};
|
||||||
|
|
||||||
|
/** A fast output stream which is not thread-safe. */
|
||||||
|
class FMT_API ostream {
|
||||||
|
private:
|
||||||
|
FMT_MSC_WARNING(suppress : 4251)
|
||||||
|
detail::file_buffer buffer_;
|
||||||
|
|
||||||
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
|
: buffer_(path, params) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
|
||||||
|
|
||||||
|
~ostream();
|
||||||
|
|
||||||
|
void flush() { buffer_.flush(); }
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
|
void close() { buffer_.close(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||||
|
output to the file.
|
||||||
|
*/
|
||||||
|
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||||
|
vformat_to(detail::buffer_appender<char>(buffer_), fmt,
|
||||||
|
fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Opens a file for writing. Supported parameters passed in *params*:
|
||||||
|
|
||||||
|
* ``<integer>``: Flags passed to `open
|
||||||
|
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
|
||||||
|
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
|
||||||
|
* ``buffer_size=<integer>``: Output buffer size
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
auto out = fmt::output_file("guide.txt");
|
||||||
|
out.print("Don't {}", "Panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline ostream output_file(cstring_view path, T... params) {
|
||||||
|
return {path, detail::ostream_params(params...)};
|
||||||
|
}
|
||||||
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OS_H_
|
209
include/fmt/ostream.h
Normal file
209
include/fmt/ostream.h
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include <fstream> // std::filebuf
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__GLIBCXX__)
|
||||||
|
# include <ext/stdio_filebuf.h>
|
||||||
|
# include <ext/stdio_sync_filebuf.h>
|
||||||
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
# include <__std_stream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Generate a unique explicit instantion in every translation unit using a tag
|
||||||
|
// type in an anonymous namespace.
|
||||||
|
namespace {
|
||||||
|
struct file_access_tag {};
|
||||||
|
} // namespace
|
||||||
|
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
|
||||||
|
class file_access {
|
||||||
|
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if FMT_MSC_VERSION
|
||||||
|
template class file_access<file_access_tag, std::filebuf,
|
||||||
|
&std::filebuf::_Myfile>;
|
||||||
|
auto get_file(std::filebuf&) -> FILE*;
|
||||||
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
template class file_access<file_access_tag, std::__stdoutbuf<char>,
|
||||||
|
&std::__stdoutbuf<char>::__file_>;
|
||||||
|
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
|
||||||
|
#if FMT_MSC_VERSION
|
||||||
|
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
||||||
|
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||||
|
#elif defined(_WIN32) && defined(__GLIBCXX__)
|
||||||
|
auto* rdbuf = os.rdbuf();
|
||||||
|
FILE* c_file;
|
||||||
|
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
|
||||||
|
c_file = sfbuf->file();
|
||||||
|
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
|
||||||
|
c_file = fbuf->file();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
if (c_file) return write_console(c_file, data);
|
||||||
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
|
||||||
|
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||||
|
#else
|
||||||
|
ignore_unused(os, data);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool write_ostream_unicode(std::wostream&,
|
||||||
|
fmt::basic_string_view<wchar_t>) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
// It is a separate function rather than a part of vprint to simplify testing.
|
||||||
|
template <typename Char>
|
||||||
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
|
const Char* buf_data = buf.data();
|
||||||
|
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||||
|
unsigned_streamsize size = buf.size();
|
||||||
|
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||||
|
do {
|
||||||
|
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||||
|
buf_data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(buffer<Char>& buf, const T& value,
|
||||||
|
locale_ref loc = locale_ref()) {
|
||||||
|
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||||
|
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
|
#endif
|
||||||
|
output << value;
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> struct streamed_view { const T& value; };
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename Char>
|
||||||
|
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||||
|
void set_debug_format() = delete;
|
||||||
|
|
||||||
|
template <typename T, typename OutputIt>
|
||||||
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
||||||
|
-> OutputIt {
|
||||||
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
|
detail::format_value(buffer, value, ctx.locale());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(
|
||||||
|
{buffer.data(), buffer.size()}, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ostream_formatter = basic_ostream_formatter<char>;
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<detail::streamed_view<T>, Char>
|
||||||
|
: basic_ostream_formatter<Char> {
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(detail::streamed_view<T> view,
|
||||||
|
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||||
|
return basic_ostream_formatter<Char>::format(view.value, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns a view that formats `value` via an ostream ``operator<<``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("Current thread id: {}\n",
|
||||||
|
fmt::streamed(std::this_thread::get_id()));
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
auto streamed(const T& value) -> detail::streamed_view<T> {
|
||||||
|
return {value};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline void vprint_directly(std::ostream& os, string_view format_str,
|
||||||
|
format_args args) {
|
||||||
|
auto buffer = memory_buffer();
|
||||||
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
detail::write_buffer(os, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_EXPORT template <typename Char>
|
||||||
|
void vprint(std::basic_ostream<Char>& os,
|
||||||
|
basic_string_view<type_identity_t<Char>> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
|
||||||
|
detail::write_buffer(os, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
FMT_EXPORT template <typename... T>
|
||||||
|
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||||
|
const auto& vargs = fmt::make_format_args(args...);
|
||||||
|
if (detail::is_utf8())
|
||||||
|
vprint(os, fmt, vargs);
|
||||||
|
else
|
||||||
|
detail::vprint_directly(os, fmt, vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename... Args>
|
||||||
|
void print(std::wostream& os,
|
||||||
|
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||||
|
Args&&... args) {
|
||||||
|
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_EXPORT template <typename... T>
|
||||||
|
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||||
|
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename... Args>
|
||||||
|
void println(std::wostream& os,
|
||||||
|
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||||
|
Args&&... args) {
|
||||||
|
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
667
include/fmt/printf.h
Normal file
667
include/fmt/printf.h
Normal file
@ -0,0 +1,667 @@
|
|||||||
|
// Formatting library for C++ - legacy printf implementation
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_PRINTF_H_
|
||||||
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
|
#include <algorithm> // std::max
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
||||||
|
|
||||||
|
template <typename Char> class basic_printf_context {
|
||||||
|
private:
|
||||||
|
detail::buffer_appender<Char> out_;
|
||||||
|
basic_format_args<basic_printf_context> args_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using parse_context_type = basic_format_parse_context<Char>;
|
||||||
|
template <typename T> using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments are
|
||||||
|
stored in the context object so make sure they have appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(detail::buffer_appender<Char> out,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: out_(out), args_(args) {}
|
||||||
|
|
||||||
|
auto out() -> detail::buffer_appender<Char> { return out_; }
|
||||||
|
void advance_to(detail::buffer_appender<Char>) {}
|
||||||
|
|
||||||
|
auto locale() -> detail::locale_ref { return {}; }
|
||||||
|
|
||||||
|
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
|
||||||
|
return args_.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
detail::error_handler().on_error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned> struct int_checker {
|
||||||
|
template <typename T> static auto fits_in_int(T value) -> bool {
|
||||||
|
unsigned max = max_value<int>();
|
||||||
|
return value <= max;
|
||||||
|
}
|
||||||
|
static auto fits_in_int(bool) -> bool { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct int_checker<true> {
|
||||||
|
template <typename T> static auto fits_in_int(T value) -> bool {
|
||||||
|
return value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= max_value<int>();
|
||||||
|
}
|
||||||
|
static auto fits_in_int(int) -> bool { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct printf_precision_handler {
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
auto operator()(T value) -> int {
|
||||||
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
|
throw_format_error("number is too big");
|
||||||
|
return (std::max)(static_cast<int>(value), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
auto operator()(T) -> int {
|
||||||
|
throw_format_error("precision is not integer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that returns true iff arg is a zero integer.
|
||||||
|
struct is_zero_int {
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
auto operator()(T value) -> bool {
|
||||||
|
return value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
auto operator()(T) -> bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||||
|
|
||||||
|
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||||
|
|
||||||
|
template <typename T, typename Context> class arg_converter {
|
||||||
|
private:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
basic_format_arg<Context>& arg_;
|
||||||
|
char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||||
|
: arg_(arg), type_(type) {}
|
||||||
|
|
||||||
|
void operator()(bool value) {
|
||||||
|
if (type_ != 's') operator()<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||||
|
void operator()(U value) {
|
||||||
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||||
|
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||||
|
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||||
|
// Extra casts are used to silence warnings.
|
||||||
|
if (is_signed) {
|
||||||
|
auto n = static_cast<int>(static_cast<target_type>(value));
|
||||||
|
arg_ = detail::make_arg<Context>(n);
|
||||||
|
} else {
|
||||||
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||||
|
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
|
||||||
|
arg_ = detail::make_arg<Context>(n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_signed) {
|
||||||
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
|
// but we don't have to do the same because it's a UB.
|
||||||
|
auto n = static_cast<long long>(value);
|
||||||
|
arg_ = detail::make_arg<Context>(n);
|
||||||
|
} else {
|
||||||
|
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
|
||||||
|
arg_ = detail::make_arg<Context>(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||||
|
void operator()(U) {} // No conversion needed for non-integral types.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts an integer argument to T for printf, if T is an integral type.
|
||||||
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||||
|
// unsigned).
|
||||||
|
template <typename T, typename Context, typename Char>
|
||||||
|
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||||
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context> class char_converter {
|
||||||
|
private:
|
||||||
|
basic_format_arg<Context>& arg_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
void operator()(T value) {
|
||||||
|
auto c = static_cast<typename Context::char_type>(value);
|
||||||
|
arg_ = detail::make_arg<Context>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
void operator()(T) {} // No conversion needed for non-integral types.
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that return a pointer to a C string if argument is a
|
||||||
|
// string or null otherwise.
|
||||||
|
template <typename Char> struct get_cstring {
|
||||||
|
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
|
||||||
|
auto operator()(const Char* s) -> const Char* { return s; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
|
// left alignment if it is negative.
|
||||||
|
template <typename Char> class printf_width_handler {
|
||||||
|
private:
|
||||||
|
format_specs<Char>& specs_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
auto operator()(T value) -> unsigned {
|
||||||
|
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||||
|
if (detail::is_negative(value)) {
|
||||||
|
specs_.align = align::left;
|
||||||
|
width = 0 - width;
|
||||||
|
}
|
||||||
|
unsigned int_max = max_value<int>();
|
||||||
|
if (width > int_max) throw_format_error("number is too big");
|
||||||
|
return static_cast<unsigned>(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
auto operator()(T) -> unsigned {
|
||||||
|
throw_format_error("width is not integer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Workaround for a bug with the XL compiler when initializing
|
||||||
|
// printf_arg_formatter's base class.
|
||||||
|
template <typename Char>
|
||||||
|
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
|
||||||
|
-> arg_formatter<Char> {
|
||||||
|
return {iter, s, locale_ref()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ``printf`` argument formatter.
|
||||||
|
template <typename Char>
|
||||||
|
class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
|
private:
|
||||||
|
using base = arg_formatter<Char>;
|
||||||
|
using context_type = basic_printf_context<Char>;
|
||||||
|
|
||||||
|
context_type& context_;
|
||||||
|
|
||||||
|
void write_null_pointer(bool is_string = false) {
|
||||||
|
auto s = this->specs;
|
||||||
|
s.type = presentation_type::none;
|
||||||
|
write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
|
||||||
|
context_type& ctx)
|
||||||
|
: base(make_arg_formatter(iter, s)), context_(ctx) {}
|
||||||
|
|
||||||
|
void operator()(monostate value) { base::operator()(value); }
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||||
|
void operator()(T value) {
|
||||||
|
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||||
|
// std::is_same instead.
|
||||||
|
if (!std::is_same<T, Char>::value) {
|
||||||
|
base::operator()(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_specs<Char> fmt_specs = this->specs;
|
||||||
|
if (fmt_specs.type != presentation_type::none &&
|
||||||
|
fmt_specs.type != presentation_type::chr) {
|
||||||
|
return (*this)(static_cast<int>(value));
|
||||||
|
}
|
||||||
|
fmt_specs.sign = sign::none;
|
||||||
|
fmt_specs.alt = false;
|
||||||
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||||
|
// align::numeric needs to be overwritten here since the '0' flag is
|
||||||
|
// ignored for non-numeric types
|
||||||
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||||
|
fmt_specs.align = align::right;
|
||||||
|
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
void operator()(T value) {
|
||||||
|
base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
void operator()(const char* value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else
|
||||||
|
write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated wide C string. */
|
||||||
|
void operator()(const wchar_t* value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else
|
||||||
|
write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(basic_string_view<Char> value) { base::operator()(value); }
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
void operator()(const void* value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else
|
||||||
|
write_null_pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
void operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
|
auto parse_ctx = basic_format_parse_context<Char>({});
|
||||||
|
handle.format(parse_ctx, context_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
switch (*it) {
|
||||||
|
case '-':
|
||||||
|
specs.align = align::left;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
specs.sign = sign::plus;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
specs.fill[0] = '0';
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
if (specs.sign != sign::plus) specs.sign = sign::space;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
specs.alt = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename GetArg>
|
||||||
|
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
|
||||||
|
GetArg get_arg) -> int {
|
||||||
|
int arg_index = -1;
|
||||||
|
Char c = *it;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
|
// preceded with '0' flag(s).
|
||||||
|
int value = parse_nonnegative_int(it, end, -1);
|
||||||
|
if (it != end && *it == '$') { // value is an argument index
|
||||||
|
++it;
|
||||||
|
arg_index = value != -1 ? value : max_value<int>();
|
||||||
|
} else {
|
||||||
|
if (c == '0') specs.fill[0] = '0';
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width and don't need to
|
||||||
|
// parse it or flags again, so return now.
|
||||||
|
if (value == -1) throw_format_error("number is too big");
|
||||||
|
specs.width = value;
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_flags(specs, it, end);
|
||||||
|
// Parse width.
|
||||||
|
if (it != end) {
|
||||||
|
if (*it >= '0' && *it <= '9') {
|
||||||
|
specs.width = parse_nonnegative_int(it, end, -1);
|
||||||
|
if (specs.width == -1) throw_format_error("number is too big");
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
specs.width = static_cast<int>(visit_format_arg(
|
||||||
|
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto parse_printf_presentation_type(char c, type t)
|
||||||
|
-> presentation_type {
|
||||||
|
using pt = presentation_type;
|
||||||
|
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
return in(t, integral_set) ? pt::dec : pt::none;
|
||||||
|
case 'o':
|
||||||
|
return in(t, integral_set) ? pt::oct : pt::none;
|
||||||
|
case 'x':
|
||||||
|
return in(t, integral_set) ? pt::hex_lower : pt::none;
|
||||||
|
case 'X':
|
||||||
|
return in(t, integral_set) ? pt::hex_upper : pt::none;
|
||||||
|
case 'a':
|
||||||
|
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
|
||||||
|
case 'A':
|
||||||
|
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
|
||||||
|
case 'e':
|
||||||
|
return in(t, float_set) ? pt::exp_lower : pt::none;
|
||||||
|
case 'E':
|
||||||
|
return in(t, float_set) ? pt::exp_upper : pt::none;
|
||||||
|
case 'f':
|
||||||
|
return in(t, float_set) ? pt::fixed_lower : pt::none;
|
||||||
|
case 'F':
|
||||||
|
return in(t, float_set) ? pt::fixed_upper : pt::none;
|
||||||
|
case 'g':
|
||||||
|
return in(t, float_set) ? pt::general_lower : pt::none;
|
||||||
|
case 'G':
|
||||||
|
return in(t, float_set) ? pt::general_upper : pt::none;
|
||||||
|
case 'c':
|
||||||
|
return in(t, integral_set) ? pt::chr : pt::none;
|
||||||
|
case 's':
|
||||||
|
return in(t, string_set | cstring_set) ? pt::string : pt::none;
|
||||||
|
case 'p':
|
||||||
|
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
|
||||||
|
default:
|
||||||
|
return pt::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
using iterator = buffer_appender<Char>;
|
||||||
|
auto out = iterator(buf);
|
||||||
|
auto context = basic_printf_context<Char>(out, args);
|
||||||
|
auto parse_ctx = basic_format_parse_context<Char>(format);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||||
|
// argument.
|
||||||
|
auto get_arg = [&](int arg_index) {
|
||||||
|
if (arg_index < 0)
|
||||||
|
arg_index = parse_ctx.next_arg_id();
|
||||||
|
else
|
||||||
|
parse_ctx.check_arg_id(--arg_index);
|
||||||
|
return detail::get_arg(context, arg_index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Char* start = parse_ctx.begin();
|
||||||
|
const Char* end = parse_ctx.end();
|
||||||
|
auto it = start;
|
||||||
|
while (it != end) {
|
||||||
|
if (!find<false, Char>(it, end, '%', it)) {
|
||||||
|
it = end; // find leaves it == nullptr if it doesn't find '%'.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Char c = *it++;
|
||||||
|
if (it != end && *it == c) {
|
||||||
|
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||||
|
start = ++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
|
||||||
|
|
||||||
|
auto specs = format_specs<Char>();
|
||||||
|
specs.align = align::right;
|
||||||
|
|
||||||
|
// Parse argument index, flags and width.
|
||||||
|
int arg_index = parse_header(it, end, specs, get_arg);
|
||||||
|
if (arg_index == 0) throw_format_error("argument not found");
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (it != end && *it == '.') {
|
||||||
|
++it;
|
||||||
|
c = it != end ? *it : 0;
|
||||||
|
if ('0' <= c && c <= '9') {
|
||||||
|
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||||
|
} else if (c == '*') {
|
||||||
|
++it;
|
||||||
|
specs.precision = static_cast<int>(
|
||||||
|
visit_format_arg(printf_precision_handler(), get_arg(-1)));
|
||||||
|
} else {
|
||||||
|
specs.precision = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto arg = get_arg(arg_index);
|
||||||
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||||
|
// specified, the '0' flag is ignored
|
||||||
|
if (specs.precision >= 0 && arg.is_integral()) {
|
||||||
|
// Ignore '0' for non-numeric types or if '-' present.
|
||||||
|
specs.fill[0] = ' ';
|
||||||
|
}
|
||||||
|
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
|
||||||
|
auto str = visit_format_arg(get_cstring<Char>(), arg);
|
||||||
|
auto str_end = str + specs.precision;
|
||||||
|
auto nul = std::find(str, str_end, Char());
|
||||||
|
auto sv = basic_string_view<Char>(
|
||||||
|
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
||||||
|
arg = make_arg<basic_printf_context<Char>>(sv);
|
||||||
|
}
|
||||||
|
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
|
||||||
|
if (specs.fill[0] == '0') {
|
||||||
|
if (arg.is_arithmetic() && specs.align != align::left)
|
||||||
|
specs.align = align::numeric;
|
||||||
|
else
|
||||||
|
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||||
|
// flag is also present.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse length and convert the argument to the required type.
|
||||||
|
c = it != end ? *it++ : 0;
|
||||||
|
Char t = it != end ? *it : 0;
|
||||||
|
switch (c) {
|
||||||
|
case 'h':
|
||||||
|
if (t == 'h') {
|
||||||
|
++it;
|
||||||
|
t = it != end ? *it : 0;
|
||||||
|
convert_arg<signed char>(arg, t);
|
||||||
|
} else {
|
||||||
|
convert_arg<short>(arg, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (t == 'l') {
|
||||||
|
++it;
|
||||||
|
t = it != end ? *it : 0;
|
||||||
|
convert_arg<long long>(arg, t);
|
||||||
|
} else {
|
||||||
|
convert_arg<long>(arg, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
convert_arg<intmax_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
convert_arg<size_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
convert_arg<std::ptrdiff_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
// printf produces garbage when 'L' is omitted for long double, no
|
||||||
|
// need to do the same.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
convert_arg<void>(arg, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (it == end) throw_format_error("invalid format string");
|
||||||
|
char type = static_cast<char>(*it++);
|
||||||
|
if (arg.is_integral()) {
|
||||||
|
// Normalize type.
|
||||||
|
switch (type) {
|
||||||
|
case 'i':
|
||||||
|
case 'u':
|
||||||
|
type = 'd';
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specs.type = parse_printf_presentation_type(type, arg.type());
|
||||||
|
if (specs.type == presentation_type::none)
|
||||||
|
throw_format_error("invalid format specifier");
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
|
||||||
|
}
|
||||||
|
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||||
|
}
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
using printf_context = basic_printf_context<char>;
|
||||||
|
using wprintf_context = basic_printf_context<wchar_t>;
|
||||||
|
|
||||||
|
using printf_args = basic_format_args<printf_context>;
|
||||||
|
using wprintf_args = basic_format_args<wprintf_context>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline auto make_printf_args(const T&... args)
|
||||||
|
-> format_arg_store<printf_context, T...> {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
template <typename... T>
|
||||||
|
inline auto make_wprintf_args(const T&... args)
|
||||||
|
-> format_arg_store<wprintf_context, T...> {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline auto vsprintf(
|
||||||
|
basic_string_view<Char> fmt,
|
||||||
|
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
auto buf = basic_memory_buffer<Char>();
|
||||||
|
detail::vprintf(buf, fmt, args);
|
||||||
|
return to_string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... T,
|
||||||
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
|
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||||
|
return vsprintf(detail::to_string_view(fmt),
|
||||||
|
fmt::make_format_args<basic_printf_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline auto vfprintf(
|
||||||
|
std::FILE* f, basic_string_view<Char> fmt,
|
||||||
|
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
|
auto buf = basic_memory_buffer<Char>();
|
||||||
|
detail::vprintf(buf, fmt, args);
|
||||||
|
size_t size = buf.size();
|
||||||
|
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
|
||||||
|
? -1
|
||||||
|
: static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the file *f*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... T, typename Char = char_t<S>>
|
||||||
|
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||||
|
return vfprintf(f, detail::to_string_view(fmt),
|
||||||
|
fmt::make_format_args<basic_printf_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_DEPRECATED inline auto vprintf(
|
||||||
|
basic_string_view<Char> fmt,
|
||||||
|
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
|
return vfprintf(stdout, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline auto printf(string_view fmt, const T&... args) -> int {
|
||||||
|
return vfprintf(stdout, fmt, make_printf_args(args...));
|
||||||
|
}
|
||||||
|
template <typename... T>
|
||||||
|
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
|
||||||
|
const T&... args) -> int {
|
||||||
|
return vfprintf(stdout, fmt, make_wprintf_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
732
include/fmt/ranges.h
Normal file
732
include/fmt/ranges.h
Normal file
@ -0,0 +1,732 @@
|
|||||||
|
// Formatting library for C++ - experimental range support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#ifndef FMT_RANGES_H_
|
||||||
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Range, typename OutputIt>
|
||||||
|
auto copy(const Range& range, OutputIt out) -> OutputIt {
|
||||||
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
|
*out++ = *it;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto copy(const char* str, OutputIt out) -> OutputIt {
|
||||||
|
while (*str) *out++ = *str++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
|
||||||
|
*out++ = ch;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
|
||||||
|
*out++ = ch;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||||
|
template <typename T> class is_std_string_like {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U* p)
|
||||||
|
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const bool value =
|
||||||
|
is_string<T>::value ||
|
||||||
|
std::is_convertible<T, std_string_view<char>>::value ||
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> class is_map {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
|
||||||
|
static constexpr const bool value = false;
|
||||||
|
#else
|
||||||
|
static constexpr const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class is_set {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
|
||||||
|
static constexpr const bool value = false;
|
||||||
|
#else
|
||||||
|
static constexpr const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
|
||||||
|
|
||||||
|
# define FMT_DECLTYPE_RETURN(val) \
|
||||||
|
->decltype(val) { return val; } \
|
||||||
|
static_assert( \
|
||||||
|
true, "") // This makes it so that a semicolon is required after the
|
||||||
|
// macro, which helps clang-format handle the formatting.
|
||||||
|
|
||||||
|
// C array overload
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
auto range_end(const T (&arr)[N]) -> const T* {
|
||||||
|
return arr + N;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_member_fn_begin_end_t : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||||
|
decltype(std::declval<T>().end())>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
// Member function overload
|
||||||
|
template <typename T>
|
||||||
|
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
|
||||||
|
template <typename T>
|
||||||
|
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
|
||||||
|
|
||||||
|
// ADL overload. Only participates in overload resolution if member functions
|
||||||
|
// are not found.
|
||||||
|
template <typename T>
|
||||||
|
auto range_begin(T&& rng)
|
||||||
|
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||||
|
decltype(begin(static_cast<T&&>(rng)))> {
|
||||||
|
return begin(static_cast<T&&>(rng));
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||||
|
decltype(end(static_cast<T&&>(rng)))> {
|
||||||
|
return end(static_cast<T&&>(rng));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_const_begin_end : std::false_type {};
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_mutable_begin_end : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_const_begin_end<
|
||||||
|
T,
|
||||||
|
void_t<
|
||||||
|
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||||
|
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_mutable_begin_end<
|
||||||
|
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||||
|
decltype(detail::range_end(std::declval<T>())),
|
||||||
|
// the extra int here is because older versions of MSVC don't
|
||||||
|
// SFINAE properly unless there are distinct types
|
||||||
|
int>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<T, void>
|
||||||
|
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||||
|
has_mutable_begin_end<T>::value)> {};
|
||||||
|
# undef FMT_DECLTYPE_RETURN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// tuple_size and tuple_element check.
|
||||||
|
template <typename T> class is_tuple_like_ {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for integer_sequence
|
||||||
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
|
||||||
|
template <typename T, T... N>
|
||||||
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
|
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||||
|
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||||
|
#else
|
||||||
|
template <typename T, T... N> struct integer_sequence {
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||||
|
|
||||||
|
template <typename T, size_t N, T... Ns>
|
||||||
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
|
template <typename T, T... Ns>
|
||||||
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
|
||||||
|
|
||||||
|
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
||||||
|
class is_tuple_formattable_ {
|
||||||
|
public:
|
||||||
|
static constexpr const bool value = false;
|
||||||
|
};
|
||||||
|
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||||
|
template <std::size_t... Is>
|
||||||
|
static std::true_type check2(index_sequence<Is...>,
|
||||||
|
integer_sequence<bool, (Is == Is)...>);
|
||||||
|
static std::false_type check2(...);
|
||||||
|
template <std::size_t... Is>
|
||||||
|
static decltype(check2(
|
||||||
|
index_sequence<Is...>{},
|
||||||
|
integer_sequence<
|
||||||
|
bool, (is_formattable<typename std::tuple_element<Is, T>::type,
|
||||||
|
C>::value)...>{})) check(index_sequence<Is...>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const bool value =
|
||||||
|
decltype(check(tuple_index_sequence<T>{}))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tuple, typename F, size_t... Is>
|
||||||
|
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
|
||||||
|
using std::get;
|
||||||
|
// Using a free function get<Is>(Tuple) now.
|
||||||
|
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
|
||||||
|
ignore_unused(unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tuple, typename F>
|
||||||
|
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
|
||||||
|
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
|
||||||
|
std::forward<Tuple>(t), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
|
||||||
|
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
|
||||||
|
using std::get;
|
||||||
|
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
|
||||||
|
ignore_unused(unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tuple1, typename Tuple2, typename F>
|
||||||
|
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
|
||||||
|
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
|
||||||
|
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
|
||||||
|
std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tuple {
|
||||||
|
// Workaround a bug in MSVC 2019 (v140).
|
||||||
|
template <typename Char, typename... T>
|
||||||
|
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
|
||||||
|
|
||||||
|
using std::get;
|
||||||
|
template <typename Tuple, typename Char, std::size_t... Is>
|
||||||
|
auto get_formatters(index_sequence<Is...>)
|
||||||
|
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
|
||||||
|
} // namespace tuple
|
||||||
|
|
||||||
|
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
|
||||||
|
// Older MSVC doesn't get the reference type correctly for arrays.
|
||||||
|
template <typename R> struct range_reference_type_impl {
|
||||||
|
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
|
||||||
|
using type = T&;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using range_reference_type = typename range_reference_type_impl<T>::type;
|
||||||
|
#else
|
||||||
|
template <typename Range>
|
||||||
|
using range_reference_type =
|
||||||
|
decltype(*detail::range_begin(std::declval<Range&>()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We don't use the Range's value_type for anything, but we do need the Range's
|
||||||
|
// reference type, with cv-ref stripped.
|
||||||
|
template <typename Range>
|
||||||
|
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||||
|
|
||||||
|
template <typename Formatter>
|
||||||
|
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
|
||||||
|
-> decltype(f.set_debug_format(set)) {
|
||||||
|
f.set_debug_format(set);
|
||||||
|
}
|
||||||
|
template <typename Formatter>
|
||||||
|
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
|
||||||
|
|
||||||
|
// These are not generic lambdas for compatibility with C++11.
|
||||||
|
template <typename ParseContext> struct parse_empty_specs {
|
||||||
|
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
|
||||||
|
f.parse(ctx);
|
||||||
|
detail::maybe_set_debug_format(f, true);
|
||||||
|
}
|
||||||
|
ParseContext& ctx;
|
||||||
|
};
|
||||||
|
template <typename FormatContext> struct format_tuple_element {
|
||||||
|
using char_type = typename FormatContext::char_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const formatter<T, char_type>& f, const T& v) {
|
||||||
|
if (i > 0)
|
||||||
|
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
|
||||||
|
ctx.advance_to(f.format(v, ctx));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
FormatContext& ctx;
|
||||||
|
basic_string_view<char_type> separator;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T> struct is_tuple_like {
|
||||||
|
static constexpr const bool value =
|
||||||
|
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename C> struct is_tuple_formattable {
|
||||||
|
static constexpr const bool value =
|
||||||
|
detail::is_tuple_formattable_<T, C>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tuple, typename Char>
|
||||||
|
struct formatter<Tuple, Char,
|
||||||
|
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
|
||||||
|
fmt::is_tuple_formattable<Tuple, Char>::value>> {
|
||||||
|
private:
|
||||||
|
decltype(detail::tuple::get_formatters<Tuple, Char>(
|
||||||
|
detail::tuple_index_sequence<Tuple>())) formatters_;
|
||||||
|
|
||||||
|
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||||
|
basic_string_view<Char> opening_bracket_ =
|
||||||
|
detail::string_literal<Char, '('>{};
|
||||||
|
basic_string_view<Char> closing_bracket_ =
|
||||||
|
detail::string_literal<Char, ')'>{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR formatter() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||||
|
separator_ = sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||||
|
basic_string_view<Char> close) {
|
||||||
|
opening_bracket_ = open;
|
||||||
|
closing_bracket_ = close;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it != '}')
|
||||||
|
FMT_THROW(format_error("invalid format specifier"));
|
||||||
|
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Tuple& value, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
|
||||||
|
detail::for_each2(
|
||||||
|
formatters_, value,
|
||||||
|
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
|
||||||
|
return detail::copy_str<Char>(closing_bracket_, ctx.out());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char> struct is_range {
|
||||||
|
static constexpr const bool value =
|
||||||
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
|
!std::is_convertible<T, detail::std_string_view<Char>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename Context> struct range_mapper {
|
||||||
|
using mapper = arg_mapper<Context>;
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||||
|
static auto map(T&& value) -> T&& {
|
||||||
|
return static_cast<T&&>(value);
|
||||||
|
}
|
||||||
|
template <typename T,
|
||||||
|
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||||
|
static auto map(T&& value)
|
||||||
|
-> decltype(mapper().map(static_cast<T&&>(value))) {
|
||||||
|
return mapper().map(static_cast<T&&>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Element>
|
||||||
|
using range_formatter_type =
|
||||||
|
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
|
||||||
|
std::declval<Element>()))>,
|
||||||
|
Char>;
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
using maybe_const_range =
|
||||||
|
conditional_t<has_const_begin_end<R>::value, const R, R>;
|
||||||
|
|
||||||
|
// Workaround a bug in MSVC 2015 and earlier.
|
||||||
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||||
|
template <typename R, typename Char>
|
||||||
|
struct is_formattable_delayed
|
||||||
|
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
|
||||||
|
#endif
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename Char, typename Enable = void>
|
||||||
|
struct range_formatter;
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct range_formatter<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
|
||||||
|
is_formattable<T, Char>>::value>> {
|
||||||
|
private:
|
||||||
|
detail::range_formatter_type<Char, T> underlying_;
|
||||||
|
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||||
|
basic_string_view<Char> opening_bracket_ =
|
||||||
|
detail::string_literal<Char, '['>{};
|
||||||
|
basic_string_view<Char> closing_bracket_ =
|
||||||
|
detail::string_literal<Char, ']'>{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR range_formatter() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
|
||||||
|
return underlying_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||||
|
separator_ = sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||||
|
basic_string_view<Char> close) {
|
||||||
|
opening_bracket_ = open;
|
||||||
|
closing_bracket_ = close;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
auto end = ctx.end();
|
||||||
|
|
||||||
|
if (it != end && *it == 'n') {
|
||||||
|
set_brackets({}, {});
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it != end && *it != '}') {
|
||||||
|
if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
|
||||||
|
++it;
|
||||||
|
} else {
|
||||||
|
detail::maybe_set_debug_format(underlying_, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.advance_to(it);
|
||||||
|
return underlying_.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename FormatContext>
|
||||||
|
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
|
detail::range_mapper<buffer_context<Char>> mapper;
|
||||||
|
auto out = ctx.out();
|
||||||
|
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||||
|
int i = 0;
|
||||||
|
auto it = detail::range_begin(range);
|
||||||
|
auto end = detail::range_end(range);
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
if (i > 0) out = detail::copy_str<Char>(separator_, out);
|
||||||
|
ctx.advance_to(out);
|
||||||
|
out = underlying_.format(mapper.map(*it), ctx);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class range_format { disabled, map, set, sequence, string, debug_string };
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T>
|
||||||
|
struct range_format_kind_
|
||||||
|
: std::integral_constant<range_format,
|
||||||
|
std::is_same<uncvref_type<T>, T>::value
|
||||||
|
? range_format::disabled
|
||||||
|
: is_map<T>::value ? range_format::map
|
||||||
|
: is_set<T>::value ? range_format::set
|
||||||
|
: range_format::sequence> {};
|
||||||
|
|
||||||
|
template <range_format K, typename R, typename Char, typename Enable = void>
|
||||||
|
struct range_default_formatter;
|
||||||
|
|
||||||
|
template <range_format K>
|
||||||
|
using range_format_constant = std::integral_constant<range_format, K>;
|
||||||
|
|
||||||
|
template <range_format K, typename R, typename Char>
|
||||||
|
struct range_default_formatter<
|
||||||
|
K, R, Char,
|
||||||
|
enable_if_t<(K == range_format::sequence || K == range_format::map ||
|
||||||
|
K == range_format::set)>> {
|
||||||
|
using range_type = detail::maybe_const_range<R>;
|
||||||
|
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
|
||||||
|
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||||
|
detail::string_literal<Char, '}'>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
|
||||||
|
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||||
|
detail::string_literal<Char, '}'>{});
|
||||||
|
underlying_.underlying().set_brackets({}, {});
|
||||||
|
underlying_.underlying().set_separator(
|
||||||
|
detail::string_literal<Char, ':', ' '>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return underlying_.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(range_type& range, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
return underlying_.format(range, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename Char, typename Enable = void>
|
||||||
|
struct range_format_kind
|
||||||
|
: conditional_t<
|
||||||
|
is_range<T, Char>::value, detail::range_format_kind_<T>,
|
||||||
|
std::integral_constant<range_format, range_format::disabled>> {};
|
||||||
|
|
||||||
|
template <typename R, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
R, Char,
|
||||||
|
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
|
||||||
|
range_format::disabled>
|
||||||
|
// Workaround a bug in MSVC 2015 and earlier.
|
||||||
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||||
|
,
|
||||||
|
detail::is_formattable_delayed<R, Char>
|
||||||
|
#endif
|
||||||
|
>::value>>
|
||||||
|
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
|
||||||
|
Char> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
|
const std::tuple<T...>& tuple;
|
||||||
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
|
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||||
|
: tuple(t), sep{s} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||||
|
// support in tuple_join. It is disabled by default because of issues with
|
||||||
|
// the dynamic width and precision.
|
||||||
|
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename Char, typename... T>
|
||||||
|
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const tuple_join_view<Char, T...>& value,
|
||||||
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
|
return do_format(value, ctx,
|
||||||
|
std::integral_constant<size_t, sizeof...(T)>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
|
std::integral_constant<size_t, 0>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext, size_t N>
|
||||||
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
|
std::integral_constant<size_t, N>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto end = ctx.begin();
|
||||||
|
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||||
|
if (N > 1) {
|
||||||
|
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||||
|
if (end != end1)
|
||||||
|
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||||
|
std::integral_constant<size_t, 0>) const ->
|
||||||
|
typename FormatContext::iterator {
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, size_t N>
|
||||||
|
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
|
std::integral_constant<size_t, N>) const ->
|
||||||
|
typename FormatContext::iterator {
|
||||||
|
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||||
|
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||||
|
if (N > 1) {
|
||||||
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
|
ctx.advance_to(out);
|
||||||
|
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
// Check if T has an interface like a container adaptor (e.g. std::stack,
|
||||||
|
// std::queue, std::priority_queue).
|
||||||
|
template <typename T> class is_container_adaptor_like {
|
||||||
|
template <typename U> static auto check(U* p) -> typename U::container_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container> struct all {
|
||||||
|
const Container& c;
|
||||||
|
auto begin() const -> typename Container::const_iterator { return c.begin(); }
|
||||||
|
auto end() const -> typename Container::const_iterator { return c.end(); }
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
enable_if_t<detail::is_container_adaptor_like<T>::value>>
|
||||||
|
: formatter<detail::all<typename T::container_type>, Char> {
|
||||||
|
using all = detail::all<typename T::container_type>;
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
|
struct getter : T {
|
||||||
|
static auto get(const T& t) -> all {
|
||||||
|
return {t.*(&getter::c)}; // Access c through the derived class.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return formatter<all>::format(getter::get(t), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::tuple<int, char> t = {1, 'a'};
|
||||||
|
fmt::print("{}", fmt::join(t, ", "));
|
||||||
|
// Output: "1, a"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||||
|
-> tuple_join_view<char, T...> {
|
||||||
|
return {tuple, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||||
|
basic_string_view<wchar_t> sep)
|
||||||
|
-> tuple_join_view<wchar_t, T...> {
|
||||||
|
return {tuple, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `initializer_list` with elements separated by
|
||||||
|
`sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||||
|
// Output: "1, 2, 3"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
auto join(std::initializer_list<T> list, string_view sep)
|
||||||
|
-> join_view<const T*, const T*> {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_RANGES_H_
|
349
include/fmt/std.h
Normal file
349
include/fmt/std.h
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
// Formatting library for C++ - formatters for standard library types
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_STD_H_
|
||||||
|
#define FMT_STD_H_
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "ostream.h"
|
||||||
|
|
||||||
|
#if FMT_HAS_INCLUDE(<version>)
|
||||||
|
# include <version>
|
||||||
|
#endif
|
||||||
|
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
|
||||||
|
#if FMT_CPLUSPLUS >= 201703L
|
||||||
|
# if FMT_HAS_INCLUDE(<filesystem>)
|
||||||
|
# include <filesystem>
|
||||||
|
# endif
|
||||||
|
# if FMT_HAS_INCLUDE(<variant>)
|
||||||
|
# include <variant>
|
||||||
|
# endif
|
||||||
|
# if FMT_HAS_INCLUDE(<optional>)
|
||||||
|
# include <optional>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GCC 4 does not support FMT_HAS_INCLUDE.
|
||||||
|
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
|
||||||
|
# include <cxxabi.h>
|
||||||
|
// Android NDK with gabi++ library on some architectures does not implement
|
||||||
|
// abi::__cxa_demangle().
|
||||||
|
# ifndef __GABIXX_CXXABI_H__
|
||||||
|
# define FMT_HAS_ABI_CXA_DEMANGLE
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_filesystem
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||||
|
const std::filesystem::path& p) {
|
||||||
|
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
|
||||||
|
}
|
||||||
|
# ifdef _WIN32
|
||||||
|
template <>
|
||||||
|
inline void write_escaped_path<char>(memory_buffer& quoted,
|
||||||
|
const std::filesystem::path& p) {
|
||||||
|
auto buf = basic_memory_buffer<wchar_t>();
|
||||||
|
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
|
||||||
|
// Convert UTF-16 to UTF-8.
|
||||||
|
if (!to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
|
||||||
|
FMT_THROW(std::runtime_error("invalid utf16"));
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
template <>
|
||||||
|
inline void write_escaped_path<std::filesystem::path::value_type>(
|
||||||
|
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
|
||||||
|
const std::filesystem::path& p) {
|
||||||
|
write_escaped_string<std::filesystem::path::value_type>(
|
||||||
|
std::back_inserter(quoted), p.native());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::filesystem::path, Char>
|
||||||
|
: formatter<basic_string_view<Char>> {
|
||||||
|
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
|
||||||
|
auto out = formatter<basic_string_view<Char>>::parse(ctx);
|
||||||
|
this->set_debug_format(false);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
|
||||||
|
typename FormatContext::iterator {
|
||||||
|
auto quoted = basic_memory_buffer<Char>();
|
||||||
|
detail::write_escaped_path(quoted, p);
|
||||||
|
return formatter<basic_string_view<Char>>::format(
|
||||||
|
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_optional
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<std::optional<T>, Char,
|
||||||
|
std::enable_if_t<is_formattable<T, Char>::value>> {
|
||||||
|
private:
|
||||||
|
formatter<T, Char> underlying_;
|
||||||
|
static constexpr basic_string_view<Char> optional =
|
||||||
|
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
|
||||||
|
'('>{};
|
||||||
|
static constexpr basic_string_view<Char> none =
|
||||||
|
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
|
||||||
|
-> decltype(u.set_debug_format(set)) {
|
||||||
|
u.set_debug_format(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
|
||||||
|
maybe_set_debug_format(underlying_, true);
|
||||||
|
return underlying_.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(std::optional<T> const& opt, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
if (!opt) return detail::write<Char>(ctx.out(), none);
|
||||||
|
|
||||||
|
auto out = ctx.out();
|
||||||
|
out = detail::write<Char>(out, optional);
|
||||||
|
ctx.advance_to(out);
|
||||||
|
out = underlying_.format(*opt, ctx);
|
||||||
|
return detail::write(out, ')');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
#endif // __cpp_lib_optional
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_variant
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename Char> struct formatter<std::monostate, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::monostate&, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
out = detail::write<Char>(out, "monostate");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using variant_index_sequence =
|
||||||
|
std::make_index_sequence<std::variant_size<T>::value>;
|
||||||
|
|
||||||
|
template <typename> struct is_variant_like_ : std::false_type {};
|
||||||
|
template <typename... Types>
|
||||||
|
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
|
||||||
|
|
||||||
|
// formattable element check.
|
||||||
|
template <typename T, typename C> class is_variant_formattable_ {
|
||||||
|
template <std::size_t... Is>
|
||||||
|
static std::conjunction<
|
||||||
|
is_formattable<std::variant_alternative_t<Is, T>, C>...>
|
||||||
|
check(std::index_sequence<Is...>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const bool value =
|
||||||
|
decltype(check(variant_index_sequence<T>{}))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T>
|
||||||
|
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
|
||||||
|
if constexpr (is_string<T>::value)
|
||||||
|
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||||
|
else if constexpr (std::is_same_v<T, Char>)
|
||||||
|
return write_escaped_char(out, v);
|
||||||
|
else
|
||||||
|
return write<Char>(out, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
template <typename T> struct is_variant_like {
|
||||||
|
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename C> struct is_variant_formattable {
|
||||||
|
static constexpr const bool value =
|
||||||
|
detail::is_variant_formattable_<T, C>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename Variant, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
Variant, Char,
|
||||||
|
std::enable_if_t<std::conjunction_v<
|
||||||
|
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Variant& value, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
|
||||||
|
out = detail::write<Char>(out, "variant(");
|
||||||
|
try {
|
||||||
|
std::visit(
|
||||||
|
[&](const auto& v) {
|
||||||
|
out = detail::write_variant_alternative<Char>(out, v);
|
||||||
|
},
|
||||||
|
value);
|
||||||
|
} catch (const std::bad_variant_access&) {
|
||||||
|
detail::write<Char>(out, "valueless by exception");
|
||||||
|
}
|
||||||
|
*out++ = ')';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
#endif // __cpp_lib_variant
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename Char> struct formatter<std::error_code, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
|
||||||
|
out = detail::write<Char>(out, Char(':'));
|
||||||
|
out = detail::write<Char>(out, ec.value());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_EXPORT
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
T, Char,
|
||||||
|
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
|
||||||
|
private:
|
||||||
|
bool with_typename_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
auto end = ctx.end();
|
||||||
|
if (it == end || *it == '}') return it;
|
||||||
|
if (*it == 't') {
|
||||||
|
++it;
|
||||||
|
with_typename_ = true;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(const std::exception& ex,
|
||||||
|
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||||
|
format_specs<Char> spec;
|
||||||
|
auto out = ctx.out();
|
||||||
|
if (!with_typename_)
|
||||||
|
return detail::write_bytes(out, string_view(ex.what()), spec);
|
||||||
|
|
||||||
|
const std::type_info& ti = typeid(ex);
|
||||||
|
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
|
||||||
|
int status = 0;
|
||||||
|
std::size_t size = 0;
|
||||||
|
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
|
||||||
|
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
|
||||||
|
|
||||||
|
string_view demangled_name_view;
|
||||||
|
if (demangled_name_ptr) {
|
||||||
|
demangled_name_view = demangled_name_ptr.get();
|
||||||
|
|
||||||
|
// Normalization of stdlib inline namespace names.
|
||||||
|
// libc++ inline namespaces.
|
||||||
|
// std::__1::* -> std::*
|
||||||
|
// std::__1::__fs::* -> std::*
|
||||||
|
// libstdc++ inline namespaces.
|
||||||
|
// std::__cxx11::* -> std::*
|
||||||
|
// std::filesystem::__cxx11::* -> std::filesystem::*
|
||||||
|
if (demangled_name_view.starts_with("std::")) {
|
||||||
|
char* begin = demangled_name_ptr.get();
|
||||||
|
char* to = begin + 5; // std::
|
||||||
|
for (char *from = to, *end = begin + demangled_name_view.size();
|
||||||
|
from < end;) {
|
||||||
|
// This is safe, because demangled_name is NUL-terminated.
|
||||||
|
if (from[0] == '_' && from[1] == '_') {
|
||||||
|
char* next = from + 1;
|
||||||
|
while (next < end && *next != ':') next++;
|
||||||
|
if (next[0] == ':' && next[1] == ':') {
|
||||||
|
from = next + 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*to++ = *from++;
|
||||||
|
}
|
||||||
|
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
demangled_name_view = string_view(ti.name());
|
||||||
|
}
|
||||||
|
out = detail::write_bytes(out, demangled_name_view, spec);
|
||||||
|
#elif FMT_MSC_VERSION
|
||||||
|
string_view demangled_name_view(ti.name());
|
||||||
|
if (demangled_name_view.starts_with("class "))
|
||||||
|
demangled_name_view.remove_prefix(6);
|
||||||
|
else if (demangled_name_view.starts_with("struct "))
|
||||||
|
demangled_name_view.remove_prefix(7);
|
||||||
|
out = detail::write_bytes(out, demangled_name_view, spec);
|
||||||
|
#else
|
||||||
|
out = detail::write_bytes(out, string_view(ti.name()), spec);
|
||||||
|
#endif
|
||||||
|
out = detail::write<Char>(out, Char(':'));
|
||||||
|
out = detail::write<Char>(out, Char(' '));
|
||||||
|
out = detail::write_bytes(out, string_view(ex.what()), spec);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_STD_H_
|
258
include/fmt/xchar.h
Normal file
258
include/fmt/xchar.h
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
// Formatting library for C++ - optional wchar_t and exotic character support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_XCHAR_H_
|
||||||
|
#define FMT_XCHAR_H_
|
||||||
|
|
||||||
|
#include <cwchar>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
||||||
|
|
||||||
|
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
|
||||||
|
loc_value value, const format_specs<wchar_t>& specs,
|
||||||
|
locale_ref loc) -> bool {
|
||||||
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
auto& numpunct =
|
||||||
|
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
|
||||||
|
auto separator = std::wstring();
|
||||||
|
auto grouping = numpunct.grouping();
|
||||||
|
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
|
||||||
|
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
using wstring_view = basic_string_view<wchar_t>;
|
||||||
|
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||||
|
using wformat_context = buffer_context<wchar_t>;
|
||||||
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||||
|
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
// Workaround broken conversion on older gcc.
|
||||||
|
template <typename... Args> using wformat_string = wstring_view;
|
||||||
|
inline auto runtime(wstring_view s) -> wstring_view { return s; }
|
||||||
|
#else
|
||||||
|
template <typename... Args>
|
||||||
|
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||||
|
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
|
||||||
|
return {{s}};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <> struct is_char<wchar_t> : std::true_type {};
|
||||||
|
template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||||
|
template <> struct is_char<char16_t> : std::true_type {};
|
||||||
|
template <> struct is_char<char32_t> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
constexpr format_arg_store<wformat_context, T...> make_wformat_args(
|
||||||
|
const T&... args) {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline namespace literals {
|
||||||
|
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
|
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace literals
|
||||||
|
|
||||||
|
template <typename It, typename Sentinel>
|
||||||
|
auto join(It begin, Sentinel end, wstring_view sep)
|
||||||
|
-> join_view<It, Sentinel, wchar_t> {
|
||||||
|
return {begin, end, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
auto join(Range&& range, wstring_view sep)
|
||||||
|
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
|
||||||
|
wchar_t> {
|
||||||
|
return join(std::begin(range), std::end(range), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto join(std::initializer_list<T> list, wstring_view sep)
|
||||||
|
-> join_view<const T*, const T*, wchar_t> {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
|
auto vformat(basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
auto buf = basic_memory_buffer<Char>();
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return to_string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||||
|
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass char_t as a default template parameter instead of using
|
||||||
|
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||||
|
template <typename S, typename... T, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
|
||||||
|
!std::is_same<Char, wchar_t>::value)>
|
||||||
|
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
|
||||||
|
return vformat(detail::to_string_view(format_str),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat(
|
||||||
|
const Locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
return detail::vformat(loc, detail::to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format(const Locale& loc, const S& format_str, T&&... args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
return detail::vformat(loc, detail::to_string_view(format_str),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
auto vformat_to(OutputIt out, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> OutputIt {
|
||||||
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
|
detail::vformat_to(buf, detail::to_string_view(format_str), args);
|
||||||
|
return detail::get_iterator(buf, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... T,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
|
||||||
|
return vformat_to(out, detail::to_string_view(fmt),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat_to(
|
||||||
|
OutputIt out, const Locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||||
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
|
vformat_to(buf, detail::to_string_view(format_str), args,
|
||||||
|
detail::locale_ref(loc));
|
||||||
|
return detail::get_iterator(buf, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename Locale, typename S, typename... T,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
|
||||||
|
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||||
|
T&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
return vformat_to(out, loc, detail::to_string_view(format_str),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat_to_n(
|
||||||
|
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> format_to_n_result<OutputIt> {
|
||||||
|
using traits = detail::fixed_buffer_traits;
|
||||||
|
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return {buf.out(), buf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... T,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
||||||
|
-> format_to_n_result<OutputIt> {
|
||||||
|
return vformat_to_n(out, n, detail::to_string_view(fmt),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... T, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
|
||||||
|
auto buf = detail::counting_buffer<Char>();
|
||||||
|
detail::vformat_to(buf, detail::to_string_view(fmt),
|
||||||
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
return buf.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
|
||||||
|
auto buf = wmemory_buffer();
|
||||||
|
detail::vformat_to(buf, fmt, args);
|
||||||
|
buf.push_back(L'\0');
|
||||||
|
if (std::fputws(buf.data(), f) == -1)
|
||||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint(wstring_view fmt, wformat_args args) {
|
||||||
|
vprint(stdout, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
||||||
|
*/
|
||||||
|
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||||
|
return format(FMT_STRING(L"{}"), value);
|
||||||
|
}
|
||||||
|
FMT_END_EXPORT
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_XCHAR_H_
|
347
src/btop.cpp
347
src/btop.cpp
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -37,21 +37,34 @@ tab-size = 4
|
|||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(__clang__) && __GNUC__ < 11
|
||||||
|
#include <semaphore.h>
|
||||||
|
#else
|
||||||
|
#include <semaphore>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <btop_shared.hpp>
|
#include "btop_shared.hpp"
|
||||||
#include <btop_tools.hpp>
|
#include "btop_tools.hpp"
|
||||||
#include <btop_config.hpp>
|
#include "btop_config.hpp"
|
||||||
#include <btop_input.hpp>
|
#include "btop_input.hpp"
|
||||||
#include <btop_theme.hpp>
|
#include "btop_theme.hpp"
|
||||||
#include <btop_draw.hpp>
|
#include "btop_draw.hpp"
|
||||||
#include <btop_menu.hpp>
|
#include "btop_menu.hpp"
|
||||||
|
|
||||||
|
using std::atomic;
|
||||||
|
using std::cout;
|
||||||
|
using std::flush;
|
||||||
|
using std::min;
|
||||||
|
using std::string;
|
||||||
|
using std::string_view;
|
||||||
|
using std::to_string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
using std::string, std::string_view, std::vector, std::atomic, std::endl, std::cout, std::min, std::flush, std::endl;
|
|
||||||
using std::string_literals::operator""s, std::to_string;
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace Global {
|
namespace Global {
|
||||||
const vector<array<string, 2>> Banner_src = {
|
const vector<array<string, 2>> Banner_src = {
|
||||||
@ -62,7 +75,7 @@ namespace Global {
|
|||||||
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
|
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
|
||||||
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
|
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
|
||||||
};
|
};
|
||||||
const string Version = "1.2.8";
|
const string Version = "1.2.13";
|
||||||
|
|
||||||
int coreCount;
|
int coreCount;
|
||||||
string overlay;
|
string overlay;
|
||||||
@ -80,9 +93,9 @@ namespace Global {
|
|||||||
string exit_error_msg;
|
string exit_error_msg;
|
||||||
atomic<bool> thread_exception (false);
|
atomic<bool> thread_exception (false);
|
||||||
|
|
||||||
bool debuginit = false;
|
bool debuginit{}; // defaults to false
|
||||||
bool debug = false;
|
bool debug{}; // defaults to false
|
||||||
bool utf_force = false;
|
bool utf_force{}; // defaults to false
|
||||||
|
|
||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
|
|
||||||
@ -92,8 +105,8 @@ namespace Global {
|
|||||||
atomic<bool> should_sleep (false);
|
atomic<bool> should_sleep (false);
|
||||||
atomic<bool> _runner_started (false);
|
atomic<bool> _runner_started (false);
|
||||||
|
|
||||||
bool arg_tty = false;
|
bool arg_tty{}; // defaults to false
|
||||||
bool arg_low_color = false;
|
bool arg_low_color{}; // defaults to false
|
||||||
int arg_preset = -1;
|
int arg_preset = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,22 +115,23 @@ void argumentParser(const int& argc, char **argv) {
|
|||||||
for(int i = 1; i < argc; i++) {
|
for(int i = 1; i < argc; i++) {
|
||||||
const string argument = argv[i];
|
const string argument = argv[i];
|
||||||
if (is_in(argument, "-h", "--help")) {
|
if (is_in(argument, "-h", "--help")) {
|
||||||
cout << "usage: btop [-h] [-v] [-/+t] [-p <id>] [--utf-force] [--debug]\n\n"
|
fmt::println(
|
||||||
<< "optional arguments:\n"
|
"usage: btop [-h] [-v] [-/+t] [-p <id>] [--utf-force] [--debug]\n\n"
|
||||||
<< " -h, --help show this help message and exit\n"
|
"optional arguments:\n"
|
||||||
<< " -v, --version show version info and exit\n"
|
" -h, --help show this help message and exit\n"
|
||||||
<< " -lc, --low-color disable truecolor, converts 24-bit colors to 256-color\n"
|
" -v, --version show version info and exit\n"
|
||||||
<< " -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n"
|
" -lc, --low-color disable truecolor, converts 24-bit colors to 256-color\n"
|
||||||
<< " +t, --tty_off force (OFF) tty mode\n"
|
" -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n"
|
||||||
<< " -p, --preset <id> start with preset, integer value between 0-9\n"
|
" +t, --tty_off force (OFF) tty mode\n"
|
||||||
<< " --utf-force force start even if no UTF-8 locale was detected\n"
|
" -p, --preset <id> start with preset, integer value between 0-9\n"
|
||||||
<< " --debug start in DEBUG mode: shows microsecond timer for information collect\n"
|
" --utf-force force start even if no UTF-8 locale was detected\n"
|
||||||
<< " and screen draw functions and sets loglevel to DEBUG\n"
|
" --debug start in DEBUG mode: shows microsecond timer for information collect\n"
|
||||||
<< endl;
|
" and screen draw functions and sets loglevel to DEBUG"
|
||||||
|
);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
else if (is_in(argument, "-v", "--version")) {
|
else if (is_in(argument, "-v", "--version")) {
|
||||||
cout << "btop version: " << Global::Version << endl;
|
fmt::println("btop version: {}", Global::Version);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
else if (is_in(argument, "-lc", "--low-color")) {
|
else if (is_in(argument, "-lc", "--low-color")) {
|
||||||
@ -133,14 +147,14 @@ void argumentParser(const int& argc, char **argv) {
|
|||||||
}
|
}
|
||||||
else if (is_in(argument, "-p", "--preset")) {
|
else if (is_in(argument, "-p", "--preset")) {
|
||||||
if (++i >= argc) {
|
if (++i >= argc) {
|
||||||
cout << "ERROR: Preset option needs an argument." << endl;
|
fmt::println("ERROR: Preset option needs an argument.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
else if (const string val = argv[i]; isint(val) and val.size() == 1) {
|
else if (const string val = argv[i]; isint(val) and val.size() == 1) {
|
||||||
Global::arg_preset = std::clamp(stoi(val), 0, 9);
|
Global::arg_preset = std::clamp(stoi(val), 0, 9);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cout << "ERROR: Preset option only accepts an integer value between 0-9." << endl;
|
fmt::println("ERROR: Preset option only accepts an integer value between 0-9.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,8 +163,8 @@ void argumentParser(const int& argc, char **argv) {
|
|||||||
else if (argument == "--debug")
|
else if (argument == "--debug")
|
||||||
Global::debug = true;
|
Global::debug = true;
|
||||||
else {
|
else {
|
||||||
cout << " Unknown argument: " << argument << "\n" <<
|
fmt::println(" Unknown argument: {}\n"
|
||||||
" Use -h or --help for help." << endl;
|
" Use -h or --help for help.", argument);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,8 +183,11 @@ void term_resize(bool force) {
|
|||||||
if (force and refreshed) force = false;
|
if (force and refreshed) force = false;
|
||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
static const array<string, 4> all_boxes = {"cpu", "mem", "net", "proc"};
|
static const array<string, 10> all_boxes = {"gpu5", "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4"};
|
||||||
|
#else
|
||||||
|
static const array<string, 5> all_boxes = {"", "cpu", "mem", "net", "proc"};
|
||||||
|
#endif
|
||||||
Global::resized = true;
|
Global::resized = true;
|
||||||
if (Runner::active) Runner::stop();
|
if (Runner::active) Runner::stop();
|
||||||
Term::refresh();
|
Term::refresh();
|
||||||
@ -178,30 +195,52 @@ void term_resize(bool force) {
|
|||||||
|
|
||||||
auto boxes = Config::getS("shown_boxes");
|
auto boxes = Config::getS("shown_boxes");
|
||||||
auto min_size = Term::get_min_size(boxes);
|
auto min_size = Term::get_min_size(boxes);
|
||||||
|
auto minWidth = min_size.at(0), minHeight = min_size.at(1);
|
||||||
|
|
||||||
while (not force or (Term::width < min_size.at(0) or Term::height < min_size.at(1))) {
|
while (not force or (Term::width < minWidth or Term::height < minHeight)) {
|
||||||
sleep_ms(100);
|
sleep_ms(100);
|
||||||
if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
|
if (Term::width < minWidth or Term::height < minHeight) {
|
||||||
cout << Term::clear << Global::bg_black << Global::fg_white << Mv::to((Term::height / 2) - 2, (Term::width / 2) - 11)
|
int width = Term::width, height = Term::height;
|
||||||
<< "Terminal size too small:" << Mv::to((Term::height / 2) - 1, (Term::width / 2) - 10)
|
cout << fmt::format("{clear}{bg_black}{fg_white}"
|
||||||
<< " Width = " << (Term::width < min_size.at(1) ? Global::fg_red : Global::fg_green) << Term::width
|
"{mv1}Terminal size too small:"
|
||||||
<< Global::fg_white << " Height = " << (Term::height < min_size.at(0) ? Global::fg_red : Global::fg_green) << Term::height
|
"{mv2} Width = {fg_width}{width} {fg_white}Height = {fg_height}{height}"
|
||||||
<< Mv::to((Term::height / 2) + 1, (Term::width / 2) - 12) << Global::fg_white
|
"{mv3}{fg_white}Needed for current config:"
|
||||||
<< "Needed for current config:" << Mv::to((Term::height / 2) + 2, (Term::width / 2) - 10)
|
"{mv4}Width = {minWidth} Height = {minHeight}",
|
||||||
<< "Width = " << min_size.at(0) << " Height = " << min_size.at(1) << flush;
|
"clear"_a = Term::clear, "bg_black"_a = Global::bg_black, "fg_white"_a = Global::fg_white,
|
||||||
|
"mv1"_a = Mv::to((height / 2) - 2, (width / 2) - 11),
|
||||||
|
"mv2"_a = Mv::to((height / 2) - 1, (width / 2) - 10),
|
||||||
|
"fg_width"_a = (width < minWidth ? Global::fg_red : Global::fg_green),
|
||||||
|
"width"_a = width,
|
||||||
|
"fg_height"_a = (height < minHeight ? Global::fg_red : Global::fg_green),
|
||||||
|
"height"_a = height,
|
||||||
|
"mv3"_a = Mv::to((height / 2) + 1, (width / 2) - 12),
|
||||||
|
"mv4"_a = Mv::to((height / 2) + 2, (width / 2) - 10),
|
||||||
|
"minWidth"_a = minWidth,
|
||||||
|
"minHeight"_a = minHeight
|
||||||
|
) << std::flush;
|
||||||
|
|
||||||
bool got_key = false;
|
bool got_key = false;
|
||||||
for (; not Term::refresh() and not got_key; got_key = Input::poll(10));
|
for (; not Term::refresh() and not got_key; got_key = Input::poll(10));
|
||||||
if (got_key) {
|
if (got_key) {
|
||||||
auto key = Input::get();
|
auto key = Input::get();
|
||||||
if (key == "q")
|
if (key == "q")
|
||||||
clean_quit(0);
|
clean_quit(0);
|
||||||
else if (is_in(key, "1", "2", "3", "4")) {
|
else if (key.size() == 1 and isint(key)) {
|
||||||
Config::current_preset = -1;
|
auto intKey = stoi(key);
|
||||||
Config::toggle_box(all_boxes.at(std::stoi(key) - 1));
|
#ifdef GPU_SUPPORT
|
||||||
boxes = Config::getS("shown_boxes");
|
if ((intKey == 0 and Gpu::gpu_names.size() >= 5) or (intKey >= 5 and std::cmp_greater_equal(Gpu::gpu_names.size(), intKey - 4))) {
|
||||||
|
#else
|
||||||
|
if (intKey > 0 and intKey < 5) {
|
||||||
|
#endif
|
||||||
|
auto box = all_boxes.at(intKey);
|
||||||
|
Config::current_preset = -1;
|
||||||
|
Config::toggle_box(box);
|
||||||
|
boxes = Config::getS("shown_boxes");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
min_size = Term::get_min_size(boxes);
|
min_size = Term::get_min_size(boxes);
|
||||||
|
minWidth = min_size.at(0), minHeight = min_size.at(1);
|
||||||
}
|
}
|
||||||
else if (not Term::refresh()) break;
|
else if (not Term::refresh()) break;
|
||||||
}
|
}
|
||||||
@ -216,20 +255,25 @@ void clean_quit(int sig) {
|
|||||||
Runner::stop();
|
Runner::stop();
|
||||||
if (Global::_runner_started) {
|
if (Global::_runner_started) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (pthread_join(Runner::runner_id, NULL) != 0) {
|
if (pthread_join(Runner::runner_id, nullptr) != 0) {
|
||||||
Logger::warning("Failed to join _runner thread on exit!");
|
Logger::warning("Failed to join _runner thread on exit!");
|
||||||
pthread_cancel(Runner::runner_id);
|
pthread_cancel(Runner::runner_id);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
ts.tv_sec = 5;
|
ts.tv_sec = 5;
|
||||||
if (pthread_timedjoin_np(Runner::runner_id, NULL, &ts) != 0) {
|
if (pthread_timedjoin_np(Runner::runner_id, nullptr, &ts) != 0) {
|
||||||
Logger::warning("Failed to join _runner thread on exit!");
|
Logger::warning("Failed to join _runner thread on exit!");
|
||||||
pthread_cancel(Runner::runner_id);
|
pthread_cancel(Runner::runner_id);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
Gpu::Nvml::shutdown();
|
||||||
|
Gpu::Rsmi::shutdown();
|
||||||
|
#endif
|
||||||
|
|
||||||
Config::write();
|
Config::write();
|
||||||
|
|
||||||
if (Term::initialized) {
|
if (Term::initialized) {
|
||||||
@ -240,7 +284,7 @@ void clean_quit(int sig) {
|
|||||||
if (not Global::exit_error_msg.empty()) {
|
if (not Global::exit_error_msg.empty()) {
|
||||||
sig = 1;
|
sig = 1;
|
||||||
Logger::error(Global::exit_error_msg);
|
Logger::error(Global::exit_error_msg);
|
||||||
std::cerr << Global::fg_red << "ERROR: " << Global::fg_white << Global::exit_error_msg << Fx::reset << endl;
|
fmt::println(std::cerr, "{}ERROR: {}{}{}", Global::fg_red, Global::fg_white, Global::exit_error_msg, Fx::reset);
|
||||||
}
|
}
|
||||||
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
||||||
|
|
||||||
@ -307,16 +351,15 @@ namespace Runner {
|
|||||||
atomic<bool> stopping (false);
|
atomic<bool> stopping (false);
|
||||||
atomic<bool> waiting (false);
|
atomic<bool> waiting (false);
|
||||||
atomic<bool> redraw (false);
|
atomic<bool> redraw (false);
|
||||||
|
atomic<bool> coreNum_reset (false);
|
||||||
|
|
||||||
//* Setup semaphore for triggering thread to do work
|
//* Setup semaphore for triggering thread to do work
|
||||||
#if __GNUC__ < 11
|
#if !defined(__clang__) && __GNUC__ < 11
|
||||||
#include <semaphore.h>
|
|
||||||
sem_t do_work;
|
sem_t do_work;
|
||||||
inline void thread_sem_init() { sem_init(&do_work, 0, 0); }
|
inline void thread_sem_init() { sem_init(&do_work, 0, 0); }
|
||||||
inline void thread_wait() { sem_wait(&do_work); }
|
inline void thread_wait() { sem_wait(&do_work); }
|
||||||
inline void thread_trigger() { sem_post(&do_work); }
|
inline void thread_trigger() { sem_post(&do_work); }
|
||||||
#else
|
#else
|
||||||
#include <semaphore>
|
|
||||||
std::binary_semaphore do_work(0);
|
std::binary_semaphore do_work(0);
|
||||||
inline void thread_sem_init() { ; }
|
inline void thread_sem_init() { ; }
|
||||||
inline void thread_wait() { do_work.acquire(); }
|
inline void thread_wait() { do_work.acquire(); }
|
||||||
@ -328,8 +371,14 @@ namespace Runner {
|
|||||||
pthread_mutex_t& pt_mutex;
|
pthread_mutex_t& pt_mutex;
|
||||||
public:
|
public:
|
||||||
int status;
|
int status;
|
||||||
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) { pthread_mutex_init(&pt_mutex, NULL); status = pthread_mutex_lock(&pt_mutex); }
|
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) {
|
||||||
~thread_lock() { if (status == 0) pthread_mutex_unlock(&pt_mutex); }
|
pthread_mutex_init(&pt_mutex, nullptr);
|
||||||
|
status = pthread_mutex_lock(&pt_mutex);
|
||||||
|
}
|
||||||
|
~thread_lock() {
|
||||||
|
if (status == 0)
|
||||||
|
pthread_mutex_unlock(&pt_mutex);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Wrapper for raising priviliges when using SUID bit
|
//* Wrapper for raising priviliges when using SUID bit
|
||||||
@ -337,23 +386,27 @@ namespace Runner {
|
|||||||
int status = -1;
|
int status = -1;
|
||||||
public:
|
public:
|
||||||
gain_priv() {
|
gain_priv() {
|
||||||
if (Global::real_uid != Global::set_uid) this->status = seteuid(Global::set_uid);
|
if (Global::real_uid != Global::set_uid)
|
||||||
|
this->status = seteuid(Global::set_uid);
|
||||||
}
|
}
|
||||||
~gain_priv() {
|
~gain_priv() {
|
||||||
if (status == 0) status = seteuid(Global::real_uid);
|
if (status == 0)
|
||||||
|
status = seteuid(Global::real_uid);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string output;
|
string output;
|
||||||
string empty_bg;
|
string empty_bg;
|
||||||
bool pause_output = false;
|
bool pause_output{}; // defaults to false
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
pthread_t runner_id;
|
pthread_t runner_id;
|
||||||
pthread_mutex_t mtx;
|
pthread_mutex_t mtx;
|
||||||
|
|
||||||
enum debug_actions {
|
enum debug_actions {
|
||||||
collect_begin,
|
collect_begin,
|
||||||
|
collect_done,
|
||||||
draw_begin,
|
draw_begin,
|
||||||
|
draw_begin_only,
|
||||||
draw_done
|
draw_done
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -365,6 +418,14 @@ namespace Runner {
|
|||||||
string debug_bg;
|
string debug_bg;
|
||||||
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
|
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
|
||||||
|
|
||||||
|
class MyNumPunct : public std::numpunct<char>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual char do_thousands_sep() const { return '\''; }
|
||||||
|
virtual std::string do_grouping() const { return "\03"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct runner_conf {
|
struct runner_conf {
|
||||||
vector<string> boxes;
|
vector<string> boxes;
|
||||||
bool no_update;
|
bool no_update;
|
||||||
@ -381,6 +442,13 @@ namespace Runner {
|
|||||||
case collect_begin:
|
case collect_begin:
|
||||||
debug_times[name].at(collect) = time_micros();
|
debug_times[name].at(collect) = time_micros();
|
||||||
return;
|
return;
|
||||||
|
case collect_done:
|
||||||
|
debug_times[name].at(collect) = time_micros() - debug_times[name].at(collect);
|
||||||
|
debug_times["total"].at(collect) += debug_times[name].at(collect);
|
||||||
|
return;
|
||||||
|
case draw_begin_only:
|
||||||
|
debug_times[name].at(draw) = time_micros();
|
||||||
|
return;
|
||||||
case draw_begin:
|
case draw_begin:
|
||||||
debug_times[name].at(draw) = time_micros();
|
debug_times[name].at(draw) = time_micros();
|
||||||
debug_times[name].at(collect) = debug_times[name].at(draw) - debug_times[name].at(collect);
|
debug_times[name].at(collect) = debug_times[name].at(draw) - debug_times[name].at(collect);
|
||||||
@ -394,15 +462,14 @@ namespace Runner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
|
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
|
||||||
void * _runner(void * _) {
|
void * _runner(void *) {
|
||||||
(void)_;
|
|
||||||
//? Block some signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
|
//? Block some signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
|
||||||
sigemptyset(&mask);
|
sigemptyset(&mask);
|
||||||
// sigaddset(&mask, SIGINT);
|
// sigaddset(&mask, SIGINT);
|
||||||
// sigaddset(&mask, SIGTSTP);
|
// sigaddset(&mask, SIGTSTP);
|
||||||
sigaddset(&mask, SIGWINCH);
|
sigaddset(&mask, SIGWINCH);
|
||||||
sigaddset(&mask, SIGTERM);
|
sigaddset(&mask, SIGTERM);
|
||||||
pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
|
||||||
|
|
||||||
//? pthread_mutex_lock to lock thread and monitor health from main thread
|
//? pthread_mutex_lock to lock thread and monitor health from main thread
|
||||||
thread_lock pt_lck(mtx);
|
thread_lock pt_lck(mtx);
|
||||||
@ -438,7 +505,15 @@ namespace Runner {
|
|||||||
|
|
||||||
//! DEBUG stats
|
//! DEBUG stats
|
||||||
if (Global::debug) {
|
if (Global::debug) {
|
||||||
if (debug_bg.empty() or redraw) Runner::debug_bg = Draw::createBox(2, 2, 32, 8, "", true, "debug");
|
if (debug_bg.empty() or redraw)
|
||||||
|
Runner::debug_bg = Draw::createBox(2, 2, 33,
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
9,
|
||||||
|
#else
|
||||||
|
8,
|
||||||
|
#endif
|
||||||
|
"", true, "μs");
|
||||||
|
|
||||||
debug_times.clear();
|
debug_times.clear();
|
||||||
debug_times["total"] = {0, 0};
|
debug_times["total"] = {0, 0};
|
||||||
}
|
}
|
||||||
@ -447,6 +522,29 @@ namespace Runner {
|
|||||||
|
|
||||||
//* Run collection and draw functions for all boxes
|
//* Run collection and draw functions for all boxes
|
||||||
try {
|
try {
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
//? GPU data collection
|
||||||
|
const bool gpu_in_cpu_panel = Gpu::gpu_names.size() > 0 and (
|
||||||
|
Config::getS("cpu_graph_lower").starts_with("gpu-") or Config::getS("cpu_graph_upper").starts_with("gpu-")
|
||||||
|
or (Gpu::shown == 0 and Config::getS("show_gpu_info") != "Off")
|
||||||
|
);
|
||||||
|
|
||||||
|
vector<unsigned int> gpu_panels = {};
|
||||||
|
for (auto& box : conf.boxes)
|
||||||
|
if (box.starts_with("gpu"))
|
||||||
|
gpu_panels.push_back(box.back()-'0');
|
||||||
|
|
||||||
|
vector<Gpu::gpu_info> gpus;
|
||||||
|
if (gpu_in_cpu_panel or not gpu_panels.empty()) {
|
||||||
|
if (Global::debug) debug_timer("gpu", collect_begin);
|
||||||
|
gpus = Gpu::collect(conf.no_update);
|
||||||
|
if (Global::debug) debug_timer("gpu", collect_done);
|
||||||
|
}
|
||||||
|
auto& gpus_ref = gpus;
|
||||||
|
#else
|
||||||
|
vector<Gpu::gpu_info> gpus_ref{};
|
||||||
|
#endif
|
||||||
|
|
||||||
//? CPU
|
//? CPU
|
||||||
if (v_contains(conf.boxes, "cpu")) {
|
if (v_contains(conf.boxes, "cpu")) {
|
||||||
try {
|
try {
|
||||||
@ -455,18 +553,43 @@ namespace Runner {
|
|||||||
//? Start collect
|
//? Start collect
|
||||||
auto cpu = Cpu::collect(conf.no_update);
|
auto cpu = Cpu::collect(conf.no_update);
|
||||||
|
|
||||||
|
if (coreNum_reset) {
|
||||||
|
coreNum_reset = false;
|
||||||
|
Cpu::core_mapping = Cpu::get_core_mapping();
|
||||||
|
Global::resized = true;
|
||||||
|
Input::interrupt = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Global::debug) debug_timer("cpu", draw_begin);
|
if (Global::debug) debug_timer("cpu", draw_begin);
|
||||||
|
|
||||||
//? Draw box
|
//? Draw box
|
||||||
if (not pause_output) output += Cpu::draw(cpu, conf.force_redraw, conf.no_update);
|
if (not pause_output) output += Cpu::draw(cpu, gpus_ref, conf.force_redraw, conf.no_update);
|
||||||
|
|
||||||
if (Global::debug) debug_timer("cpu", draw_done);
|
if (Global::debug) debug_timer("cpu", draw_done);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Cpu:: -> " + (string)e.what());
|
throw std::runtime_error("Cpu:: -> " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
//? GPU
|
||||||
|
if (not gpu_panels.empty() and not gpus_ref.empty()) {
|
||||||
|
try {
|
||||||
|
if (Global::debug) debug_timer("gpu", draw_begin_only);
|
||||||
|
|
||||||
|
//? Draw box
|
||||||
|
if (not pause_output)
|
||||||
|
for (unsigned long i = 0; i < gpu_panels.size(); ++i)
|
||||||
|
output += Gpu::draw(gpus_ref[gpu_panels[i]], i, conf.force_redraw, conf.no_update);
|
||||||
|
|
||||||
|
if (Global::debug) debug_timer("gpu", draw_done);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
throw std::runtime_error("Gpu:: -> " + string{e.what()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
//? MEM
|
//? MEM
|
||||||
if (v_contains(conf.boxes, "mem")) {
|
if (v_contains(conf.boxes, "mem")) {
|
||||||
try {
|
try {
|
||||||
@ -483,7 +606,7 @@ namespace Runner {
|
|||||||
if (Global::debug) debug_timer("mem", draw_done);
|
if (Global::debug) debug_timer("mem", draw_done);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Mem:: -> " + (string)e.what());
|
throw std::runtime_error("Mem:: -> " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +626,7 @@ namespace Runner {
|
|||||||
if (Global::debug) debug_timer("net", draw_done);
|
if (Global::debug) debug_timer("net", draw_done);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Net:: -> " + (string)e.what());
|
throw std::runtime_error("Net:: -> " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,12 +646,13 @@ namespace Runner {
|
|||||||
if (Global::debug) debug_timer("proc", draw_done);
|
if (Global::debug) debug_timer("proc", draw_done);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Proc:: -> " + (string)e.what());
|
throw std::runtime_error("Proc:: -> " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
|
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
|
||||||
Global::thread_exception = true;
|
Global::thread_exception = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt = true;
|
||||||
stopping = true;
|
stopping = true;
|
||||||
@ -549,26 +673,53 @@ namespace Runner {
|
|||||||
if (empty_bg.empty()) {
|
if (empty_bg.empty()) {
|
||||||
const int x = Term::width / 2 - 10, y = Term::height / 2 - 10;
|
const int x = Term::width / 2 - 10, y = Term::height / 2 - 10;
|
||||||
output += Term::clear;
|
output += Term::clear;
|
||||||
empty_bg += Draw::banner_gen(y, 0, true)
|
empty_bg = fmt::format(
|
||||||
+ Mv::to(y+6, x) + Theme::c("title") + Fx::b + "No boxes shown!"
|
"{banner}"
|
||||||
+ Mv::to(y+8, x) + Theme::c("hi_fg") + "1" + Theme::c("main_fg") + " | Show CPU box"
|
"{mv1}{titleFg}{b}No boxes shown!"
|
||||||
+ Mv::to(y+9, x) + Theme::c("hi_fg") + "2" + Theme::c("main_fg") + " | Show MEM box"
|
"{mv2}{hiFg}1 {mainFg}| Show CPU box"
|
||||||
+ Mv::to(y+10, x) + Theme::c("hi_fg") + "3" + Theme::c("main_fg") + " | Show NET box"
|
"{mv3}{hiFg}2 {mainFg}| Show MEM box"
|
||||||
+ Mv::to(y+11, x) + Theme::c("hi_fg") + "4" + Theme::c("main_fg") + " | Show PROC box"
|
"{mv4}{hiFg}3 {mainFg}| Show NET box"
|
||||||
+ Mv::to(y+12, x-2) + Theme::c("hi_fg") + "esc" + Theme::c("main_fg") + " | Show menu"
|
"{mv5}{hiFg}4 {mainFg}| Show PROC box"
|
||||||
+ Mv::to(y+13, x) + Theme::c("hi_fg") + "q" + Theme::c("main_fg") + " | Quit";
|
"{mv6}{hiFg}5-0 {mainFg}| Show GPU boxes"
|
||||||
|
"{mv7}{hiFg}esc {mainFg}| Show menu"
|
||||||
|
"{mv8}{hiFg}q {mainFg}| Quit",
|
||||||
|
"banner"_a = Draw::banner_gen(y, 0, true),
|
||||||
|
"titleFg"_a = Theme::c("title"), "b"_a = Fx::b, "hiFg"_a = Theme::c("hi_fg"), "mainFg"_a = Theme::c("main_fg"),
|
||||||
|
"mv1"_a = Mv::to(y+6, x),
|
||||||
|
"mv2"_a = Mv::to(y+8, x),
|
||||||
|
"mv3"_a = Mv::to(y+9, x),
|
||||||
|
"mv4"_a = Mv::to(y+10, x),
|
||||||
|
"mv5"_a = Mv::to(y+11, x),
|
||||||
|
"mv6"_a = Mv::to(y+12, x-2),
|
||||||
|
"mv7"_a = Mv::to(y+13, x-2),
|
||||||
|
"mv8"_a = Mv::to(y+14, x)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
output += empty_bg;
|
output += empty_bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! DEBUG stats -->
|
//! DEBUG stats -->
|
||||||
if (Global::debug and not Menu::active) {
|
if (Global::debug and not Menu::active) {
|
||||||
output += debug_bg + Theme::c("title") + Fx::b + ljust(" Box", 9) + ljust("Collect μs", 12, true) + ljust("Draw μs", 9, true) + Theme::c("main_fg") + Fx::ub;
|
output += fmt::format("{pre}{box:5.5} {collect:>12.12} {draw:>12.12}{post}",
|
||||||
|
"pre"_a = debug_bg + Theme::c("title") + Fx::b,
|
||||||
|
"box"_a = "box", "collect"_a = "collect", "draw"_a = "draw",
|
||||||
|
"post"_a = Theme::c("main_fg") + Fx::ub
|
||||||
|
);
|
||||||
|
static auto loc = std::locale(std::locale::classic(), new MyNumPunct);
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
for (const string name : {"cpu", "mem", "net", "proc", "gpu", "total"}) {
|
||||||
|
#else
|
||||||
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
|
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
|
||||||
|
#endif
|
||||||
if (not debug_times.contains(name)) debug_times[name] = {0,0};
|
if (not debug_times.contains(name)) debug_times[name] = {0,0};
|
||||||
const auto& [time_collect, time_draw] = debug_times.at(name);
|
const auto& [time_collect, time_draw] = debug_times.at(name);
|
||||||
if (name == "total") output += Fx::b;
|
if (name == "total") output += Fx::b;
|
||||||
output += Mv::l(29) + Mv::d(1) + ljust(name, 8) + ljust(to_string(time_collect), 12) + ljust(to_string(time_draw), 9);
|
output += fmt::format(loc, "{mvLD}{name:5.5} {collect:12L} {draw:12L}",
|
||||||
|
"mvLD"_a = Mv::l(31) + Mv::d(1),
|
||||||
|
"name"_a = name,
|
||||||
|
"collect"_a = time_collect,
|
||||||
|
"draw"_a = time_draw
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,19 +730,19 @@ namespace Runner {
|
|||||||
<< Term::sync_end << flush;
|
<< Term::sync_end << flush;
|
||||||
}
|
}
|
||||||
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
||||||
pthread_exit(NULL);
|
return {};
|
||||||
}
|
}
|
||||||
//? ------------------------------------------ Secondary thread end -----------------------------------------------
|
//? ------------------------------------------ Secondary thread end -----------------------------------------------
|
||||||
|
|
||||||
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
||||||
void run(const string& box, const bool no_update, const bool force_redraw) {
|
void run(const string& box, bool no_update, bool force_redraw) {
|
||||||
atomic_wait_for(active, true, 5000);
|
atomic_wait_for(active, true, 5000);
|
||||||
if (active) {
|
if (active) {
|
||||||
Logger::error("Stall in Runner thread, restarting!");
|
Logger::error("Stall in Runner thread, restarting!");
|
||||||
active = false;
|
active = false;
|
||||||
// exit(1);
|
// exit(1);
|
||||||
pthread_cancel(Runner::runner_id);
|
pthread_cancel(Runner::runner_id);
|
||||||
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
if (pthread_create(&Runner::runner_id, nullptr, &Runner::_runner, nullptr) != 0) {
|
||||||
Global::exit_error_msg = "Failed to re-create _runner thread!";
|
Global::exit_error_msg = "Failed to re-create _runner thread!";
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
@ -679,19 +830,19 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
//? Setup paths for config, log and user themes
|
//? Setup paths for config, log and user themes
|
||||||
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
||||||
if (std::getenv(env) != NULL and access(std::getenv(env), W_OK) != -1) {
|
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");
|
Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Config::conf_dir.empty()) {
|
if (Config::conf_dir.empty()) {
|
||||||
cout << "WARNING: Could not get path user HOME folder.\n"
|
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." << endl;
|
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
|
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
|
||||||
cout << "WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n"
|
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." << endl;
|
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Config::conf_file = Config::conf_dir / "btop.conf";
|
Config::conf_file = Config::conf_dir / "btop.conf";
|
||||||
@ -746,17 +897,17 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//? Try to find and set a UTF-8 locale
|
//? Try to find and set a UTF-8 locale
|
||||||
if (std::setlocale(LC_ALL, "") != NULL and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
|
if (std::setlocale(LC_ALL, "") != nullptr and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
|
||||||
and str_to_upper(s_replace((string)std::setlocale(LC_ALL, ""), "-", "")).ends_with("UTF8")) {
|
and str_to_upper(s_replace((string)std::setlocale(LC_ALL, ""), "-", "")).ends_with("UTF8")) {
|
||||||
Logger::debug("Using locale " + (string)std::setlocale(LC_ALL, ""));
|
Logger::debug("Using locale " + (string)std::setlocale(LC_ALL, ""));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
string found;
|
string found;
|
||||||
bool set_failure = false;
|
bool set_failure{}; // defaults to false
|
||||||
for (const auto loc_env : array{"LANG", "LC_ALL"}) {
|
for (const auto loc_env : array{"LANG", "LC_ALL"}) {
|
||||||
if (std::getenv(loc_env) != NULL and str_to_upper(s_replace((string)std::getenv(loc_env), "-", "")).ends_with("UTF8")) {
|
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);
|
found = std::getenv(loc_env);
|
||||||
if (std::setlocale(LC_ALL, found.c_str()) == NULL) {
|
if (std::setlocale(LC_ALL, found.c_str()) == nullptr) {
|
||||||
set_failure = true;
|
set_failure = true;
|
||||||
Logger::warning("Failed to set locale " + found + " continuing anyway.");
|
Logger::warning("Failed to set locale " + found + " continuing anyway.");
|
||||||
}
|
}
|
||||||
@ -769,7 +920,7 @@ int main(int argc, char **argv) {
|
|||||||
for (auto& l : ssplit(loc, ';')) {
|
for (auto& l : ssplit(loc, ';')) {
|
||||||
if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
|
if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
|
||||||
found = l.substr(l.find('=') + 1);
|
found = l.substr(l.find('=') + 1);
|
||||||
if (std::setlocale(LC_ALL, found.c_str()) != NULL) {
|
if (std::setlocale(LC_ALL, found.c_str()) != nullptr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -790,10 +941,10 @@ int main(int argc, char **argv) {
|
|||||||
if (cur_locale.empty()) {
|
if (cur_locale.empty()) {
|
||||||
Logger::warning("No UTF-8 locale detected! Some symbols might not display correctly.");
|
Logger::warning("No UTF-8 locale detected! Some symbols might not display correctly.");
|
||||||
}
|
}
|
||||||
else if (std::setlocale(LC_ALL, string(cur_locale + ".UTF-8").c_str()) != NULL) {
|
else if (std::setlocale(LC_ALL, string(cur_locale + ".UTF-8").c_str()) != nullptr) {
|
||||||
Logger::debug("Setting LC_ALL=" + cur_locale + ".UTF-8");
|
Logger::debug("Setting LC_ALL=" + cur_locale + ".UTF-8");
|
||||||
}
|
}
|
||||||
else if(std::setlocale(LC_ALL, "en_US.UTF-8") != NULL) {
|
else if(std::setlocale(LC_ALL, "en_US.UTF-8") != nullptr) {
|
||||||
Logger::debug("Setting LC_ALL=en_US.UTF-8");
|
Logger::debug("Setting LC_ALL=en_US.UTF-8");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -848,7 +999,7 @@ int main(int argc, char **argv) {
|
|||||||
Shared::init();
|
Shared::init();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what();
|
Global::exit_error_msg = "Exception in Shared::init() -> " + string{e.what()};
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,7 +1016,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
//? Start runner thread
|
//? Start runner thread
|
||||||
Runner::thread_sem_init();
|
Runner::thread_sem_init();
|
||||||
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
if (pthread_create(&Runner::runner_id, nullptr, &Runner::_runner, nullptr) != 0) {
|
||||||
Global::exit_error_msg = "Failed to create _runner thread!";
|
Global::exit_error_msg = "Failed to create _runner thread!";
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
@ -960,7 +1111,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
|
Global::exit_error_msg = "Exception in main loop -> " + string{e.what()};
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -17,18 +17,25 @@ tab-size = 4
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <ranges>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <ranges>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <btop_config.hpp>
|
#include <fmt/core.h>
|
||||||
#include <btop_shared.hpp>
|
|
||||||
#include <btop_tools.hpp>
|
#include "btop_config.hpp"
|
||||||
|
#include "btop_shared.hpp"
|
||||||
|
#include "btop_tools.hpp"
|
||||||
|
|
||||||
|
using std::array;
|
||||||
|
using std::atomic;
|
||||||
|
using std::string_view;
|
||||||
|
|
||||||
using std::array, std::atomic, std::string_view, std::string_literals::operator""s;
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
|
||||||
//* Functions and variables for reading and writing the btop config file
|
//* Functions and variables for reading and writing the btop config file
|
||||||
@ -51,7 +58,7 @@ namespace Config {
|
|||||||
|
|
||||||
{"presets", "#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.\n"
|
{"presets", "#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.\n"
|
||||||
"#* Format: \"box_name:P:G,box_name:P:G\" P=(0 or 1) for alternate positions, G=graph symbol to use for box.\n"
|
"#* Format: \"box_name:P:G,box_name:P:G\" P=(0 or 1) for alternate positions, G=graph symbol to use for box.\n"
|
||||||
"#* Use withespace \" \" as separator between different presets.\n"
|
"#* Use whitespace \" \" as separator between different presets.\n"
|
||||||
"#* Example: \"cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty\""},
|
"#* Example: \"cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty\""},
|
||||||
|
|
||||||
{"vim_keys", "#* Set to True to enable \"h,j,k,l,g,G\" keys for directional control in lists.\n"
|
{"vim_keys", "#* Set to True to enable \"h,j,k,l,g,G\" keys for directional control in lists.\n"
|
||||||
@ -66,14 +73,16 @@ namespace Config {
|
|||||||
"#* Note that \"tty\" only has half the horizontal resolution of the other two, so will show a shorter historical view."},
|
"#* Note that \"tty\" only has half the horizontal resolution of the other two, so will show a shorter historical view."},
|
||||||
|
|
||||||
{"graph_symbol_cpu", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
{"graph_symbol_cpu", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"graph_symbol_gpu", "# Graph symbol to use for graphs in gpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
||||||
|
#endif
|
||||||
{"graph_symbol_mem", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
{"graph_symbol_mem", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
||||||
|
|
||||||
{"graph_symbol_net", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
{"graph_symbol_net", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
||||||
|
|
||||||
{"graph_symbol_proc", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
{"graph_symbol_proc", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
|
||||||
|
|
||||||
{"shown_boxes", "#* Manually set which boxes to show. Available values are \"cpu mem net proc\", separate values with whitespace."},
|
{"shown_boxes", "#* Manually set which boxes to show. Available values are \"cpu mem net proc\" and \"gpu0\" through \"gpu5\", separate values with whitespace."},
|
||||||
|
|
||||||
{"update_ms", "#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs."},
|
{"update_ms", "#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs."},
|
||||||
|
|
||||||
@ -98,14 +107,18 @@ namespace Config {
|
|||||||
|
|
||||||
{"proc_left", "#* Show proc box on left side of screen instead of right."},
|
{"proc_left", "#* Show proc box on left side of screen instead of right."},
|
||||||
|
|
||||||
{"proc_filter_kernel", "#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop)."},
|
{"proc_filter_kernel", "#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop)."},
|
||||||
|
|
||||||
|
{"proc_aggregate", "#* In tree-view, always accumulate child process resources in the parent process."},
|
||||||
|
|
||||||
{"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n"
|
{"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n"
|
||||||
"#* Select from a list of detected attributes from the options menu."},
|
"#* Select from a list of detected attributes from the options menu."},
|
||||||
|
|
||||||
{"cpu_graph_lower", "#* Sets the CPU stat shown in lower half of the CPU graph, \"total\" is always available.\n"
|
{"cpu_graph_lower", "#* Sets the CPU stat shown in lower half of the CPU graph, \"total\" is always available.\n"
|
||||||
"#* Select from a list of detected attributes from the options menu."},
|
"#* Select from a list of detected attributes from the options menu."},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"show_gpu_info", "#* If gpu info should be shown in the cpu box. Available values = \"Auto\", \"On\" and \"Off\"."},
|
||||||
|
#endif
|
||||||
{"cpu_invert_lower", "#* Toggles if the lower CPU graph should be inverted."},
|
{"cpu_invert_lower", "#* Toggles if the lower CPU graph should be inverted."},
|
||||||
|
|
||||||
{"cpu_single_graph", "#* Set to True to completely disable the lower CPU graph."},
|
{"cpu_single_graph", "#* Set to True to completely disable the lower CPU graph."},
|
||||||
@ -185,21 +198,36 @@ namespace Config {
|
|||||||
{"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."},
|
{"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."},
|
||||||
|
|
||||||
{"log_level", "#* Set loglevel for \"~/.config/btop/btop.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
|
{"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."}
|
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
|
||||||
|
{"nvml_measure_pcie_speeds",
|
||||||
|
"#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards."},
|
||||||
|
|
||||||
|
{"gpu_mirror_graph", "#* Horizontally mirror the GPU graph."},
|
||||||
|
|
||||||
|
{"custom_gpu_name0", "#* Custom gpu0 model name, empty string to disable."},
|
||||||
|
{"custom_gpu_name1", "#* Custom gpu1 model name, empty string to disable."},
|
||||||
|
{"custom_gpu_name2", "#* Custom gpu2 model name, empty string to disable."},
|
||||||
|
{"custom_gpu_name3", "#* Custom gpu3 model name, empty string to disable."},
|
||||||
|
{"custom_gpu_name4", "#* Custom gpu4 model name, empty string to disable."},
|
||||||
|
{"custom_gpu_name5", "#* Custom gpu5 model name, empty string to disable."},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
unordered_flat_map<string, string> strings = {
|
unordered_flat_map<std::string_view, string> strings = {
|
||||||
{"color_theme", "Default"},
|
{"color_theme", "Default"},
|
||||||
{"shown_boxes", "cpu mem net proc"},
|
{"shown_boxes", "cpu mem net proc"},
|
||||||
{"graph_symbol", "braille"},
|
{"graph_symbol", "braille"},
|
||||||
{"presets", "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"},
|
{"presets", "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"},
|
||||||
{"graph_symbol_cpu", "default"},
|
{"graph_symbol_cpu", "default"},
|
||||||
|
{"graph_symbol_gpu", "default"},
|
||||||
{"graph_symbol_mem", "default"},
|
{"graph_symbol_mem", "default"},
|
||||||
{"graph_symbol_net", "default"},
|
{"graph_symbol_net", "default"},
|
||||||
{"graph_symbol_proc", "default"},
|
{"graph_symbol_proc", "default"},
|
||||||
{"proc_sorting", "cpu lazy"},
|
{"proc_sorting", "cpu lazy"},
|
||||||
{"cpu_graph_upper", "total"},
|
{"cpu_graph_upper", "Auto"},
|
||||||
{"cpu_graph_lower", "total"},
|
{"cpu_graph_lower", "Auto"},
|
||||||
{"cpu_sensor", "Auto"},
|
{"cpu_sensor", "Auto"},
|
||||||
{"selected_battery", "Auto"},
|
{"selected_battery", "Auto"},
|
||||||
{"cpu_core_map", ""},
|
{"cpu_core_map", ""},
|
||||||
@ -213,10 +241,19 @@ namespace Config {
|
|||||||
{"proc_filter", ""},
|
{"proc_filter", ""},
|
||||||
{"proc_command", ""},
|
{"proc_command", ""},
|
||||||
{"selected_name", ""},
|
{"selected_name", ""},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"custom_gpu_name0", ""},
|
||||||
|
{"custom_gpu_name1", ""},
|
||||||
|
{"custom_gpu_name2", ""},
|
||||||
|
{"custom_gpu_name3", ""},
|
||||||
|
{"custom_gpu_name4", ""},
|
||||||
|
{"custom_gpu_name5", ""},
|
||||||
|
{"show_gpu_info", "Auto"}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
unordered_flat_map<string, string> stringsTmp;
|
unordered_flat_map<std::string_view, string> stringsTmp;
|
||||||
|
|
||||||
unordered_flat_map<string, bool> bools = {
|
unordered_flat_map<std::string_view, bool> bools = {
|
||||||
{"theme_background", true},
|
{"theme_background", true},
|
||||||
{"truecolor", true},
|
{"truecolor", true},
|
||||||
{"rounded_corners", true},
|
{"rounded_corners", true},
|
||||||
@ -229,7 +266,7 @@ namespace Config {
|
|||||||
{"proc_cpu_graphs", true},
|
{"proc_cpu_graphs", true},
|
||||||
{"proc_info_smaps", false},
|
{"proc_info_smaps", false},
|
||||||
{"proc_left", false},
|
{"proc_left", false},
|
||||||
{"proc_filter_kernel", false},
|
{"proc_filter_kernel", false},
|
||||||
{"cpu_invert_lower", true},
|
{"cpu_invert_lower", true},
|
||||||
{"cpu_single_graph", false},
|
{"cpu_single_graph", false},
|
||||||
{"cpu_bottom", false},
|
{"cpu_bottom", false},
|
||||||
@ -261,10 +298,15 @@ namespace Config {
|
|||||||
{"lowcolor", false},
|
{"lowcolor", false},
|
||||||
{"show_detailed", false},
|
{"show_detailed", false},
|
||||||
{"proc_filtering", false},
|
{"proc_filtering", false},
|
||||||
|
{"proc_aggregate", false},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"nvml_measure_pcie_speeds", true},
|
||||||
|
{"gpu_mirror_graph", true},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
unordered_flat_map<string, bool> boolsTmp;
|
unordered_flat_map<std::string_view, bool> boolsTmp;
|
||||||
|
|
||||||
unordered_flat_map<string, int> ints = {
|
unordered_flat_map<std::string_view, int> ints = {
|
||||||
{"update_ms", 2000},
|
{"update_ms", 2000},
|
||||||
{"net_download", 100},
|
{"net_download", 100},
|
||||||
{"net_upload", 100},
|
{"net_upload", 100},
|
||||||
@ -275,9 +317,9 @@ namespace Config {
|
|||||||
{"proc_selected", 0},
|
{"proc_selected", 0},
|
||||||
{"proc_last_selected", 0},
|
{"proc_last_selected", 0},
|
||||||
};
|
};
|
||||||
unordered_flat_map<string, int> intsTmp;
|
unordered_flat_map<std::string_view, int> intsTmp;
|
||||||
|
|
||||||
bool _locked(const string& name) {
|
bool _locked(const std::string_view name) {
|
||||||
atomic_wait(writelock, true);
|
atomic_wait(writelock, true);
|
||||||
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
||||||
write_new = true;
|
write_new = true;
|
||||||
@ -311,7 +353,7 @@ namespace Config {
|
|||||||
validError = "Malformatted preset in config value presets!";
|
validError = "Malformatted preset in config value presets!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (not is_in(vals.at(0), "cpu", "mem", "net", "proc")) {
|
if (not is_in(vals.at(0), "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4", "gpu5")) {
|
||||||
validError = "Invalid box name in config value presets!";
|
validError = "Invalid box name in config value presets!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -327,7 +369,7 @@ namespace Config {
|
|||||||
new_presets.push_back(preset);
|
new_presets.push_back(preset);
|
||||||
}
|
}
|
||||||
|
|
||||||
preset_list = move(new_presets);
|
preset_list = std::move(new_presets);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +406,7 @@ namespace Config {
|
|||||||
|
|
||||||
string validError;
|
string validError;
|
||||||
|
|
||||||
bool intValid(const string& name, const string& value) {
|
bool intValid(const std::string_view name, const string& value) {
|
||||||
int i_value;
|
int i_value;
|
||||||
try {
|
try {
|
||||||
i_value = stoi(value);
|
i_value = stoi(value);
|
||||||
@ -378,7 +420,7 @@ namespace Config {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
validError = (string)e.what();
|
validError = string{e.what()};
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +436,7 @@ namespace Config {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stringValid(const string& name, const string& value) {
|
bool stringValid(const std::string_view name, const string& value) {
|
||||||
if (name == "log_level" and not v_contains(Logger::log_levels, value))
|
if (name == "log_level" and not v_contains(Logger::log_levels, value))
|
||||||
validError = "Invalid log_level: " + value;
|
validError = "Invalid log_level: " + value;
|
||||||
|
|
||||||
@ -402,11 +444,16 @@ namespace Config {
|
|||||||
validError = "Invalid graph symbol identifier: " + value;
|
validError = "Invalid graph symbol identifier: " + value;
|
||||||
|
|
||||||
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
|
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
|
||||||
validError = "Invalid graph symbol identifier for" + name + ": " + 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 value.empty() and not check_boxes(value))
|
||||||
validError = "Invalid box name(s) in shown_boxes!";
|
validError = "Invalid box name(s) in shown_boxes!";
|
||||||
|
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
else if (name == "show_gpu_info" and not v_contains(show_gpu_values, value))
|
||||||
|
validError = "Invalid value for show_gpu_info: " + value;
|
||||||
|
#endif
|
||||||
|
|
||||||
else if (name == "presets" and not presetsValid(value))
|
else if (name == "presets" and not presetsValid(value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -451,7 +498,7 @@ namespace Config {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string getAsString(const string& name) {
|
string getAsString(const std::string_view name) {
|
||||||
if (bools.contains(name))
|
if (bools.contains(name))
|
||||||
return (bools.at(name) ? "True" : "False");
|
return (bools.at(name) ? "True" : "False");
|
||||||
else if (ints.contains(name))
|
else if (ints.contains(name))
|
||||||
@ -461,7 +508,7 @@ namespace Config {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void flip(const string& name) {
|
void flip(const std::string_view name) {
|
||||||
if (_locked(name)) {
|
if (_locked(name)) {
|
||||||
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
|
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
|
||||||
else boolsTmp.insert_or_assign(name, (not bools.at(name)));
|
else boolsTmp.insert_or_assign(name, (not bools.at(name)));
|
||||||
@ -498,7 +545,7 @@ namespace Config {
|
|||||||
boolsTmp.clear();
|
boolsTmp.clear();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Global::exit_error_msg = "Exception during Config::unlock() : " + (string)e.what();
|
Global::exit_error_msg = "Exception during Config::unlock() : " + string{e.what()};
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +556,15 @@ namespace Config {
|
|||||||
auto new_boxes = ssplit(boxes);
|
auto new_boxes = ssplit(boxes);
|
||||||
for (auto& box : new_boxes) {
|
for (auto& box : new_boxes) {
|
||||||
if (not v_contains(valid_boxes, box)) return false;
|
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;
|
||||||
|
if (std::cmp_greater(gpu_num, Gpu::gpu_names.size())) return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
current_boxes = move(new_boxes);
|
current_boxes = std::move(new_boxes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -20,10 +20,13 @@ tab-size = 4
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <robin_hood.h>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
using std::string, std::vector, robin_hood::unordered_flat_map;
|
#include <robin_hood.h>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using robin_hood::unordered_flat_map;
|
||||||
|
|
||||||
//* Functions and variables for reading and writing the btop config file
|
//* Functions and variables for reading and writing the btop config file
|
||||||
namespace Config {
|
namespace Config {
|
||||||
@ -31,18 +34,25 @@ namespace Config {
|
|||||||
extern std::filesystem::path conf_dir;
|
extern std::filesystem::path conf_dir;
|
||||||
extern std::filesystem::path conf_file;
|
extern std::filesystem::path conf_file;
|
||||||
|
|
||||||
extern unordered_flat_map<string, string> strings;
|
extern unordered_flat_map<std::string_view, string> strings;
|
||||||
extern unordered_flat_map<string, string> stringsTmp;
|
extern unordered_flat_map<std::string_view, string> stringsTmp;
|
||||||
extern unordered_flat_map<string, bool> bools;
|
extern unordered_flat_map<std::string_view, bool> bools;
|
||||||
extern unordered_flat_map<string, bool> boolsTmp;
|
extern unordered_flat_map<std::string_view, bool> boolsTmp;
|
||||||
extern unordered_flat_map<string, int> ints;
|
extern unordered_flat_map<std::string_view, int> ints;
|
||||||
extern unordered_flat_map<string, int> intsTmp;
|
extern unordered_flat_map<std::string_view, int> intsTmp;
|
||||||
|
|
||||||
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
||||||
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
|
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
|
||||||
const vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
|
const vector<string> valid_boxes = {
|
||||||
|
"cpu", "mem", "net", "proc"
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
,"gpu0", "gpu1", "gpu2", "gpu3", "gpu4", "gpu5"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
const vector<string> temp_scales = { "celsius", "fahrenheit", "kelvin", "rankine" };
|
const vector<string> temp_scales = { "celsius", "fahrenheit", "kelvin", "rankine" };
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
const vector<string> show_gpu_values = { "Auto", "On", "Off" };
|
||||||
|
#endif
|
||||||
extern vector<string> current_boxes;
|
extern vector<string> current_boxes;
|
||||||
extern vector<string> preset_list;
|
extern vector<string> preset_list;
|
||||||
extern vector<string> available_batteries;
|
extern vector<string> available_batteries;
|
||||||
@ -60,44 +70,44 @@ namespace Config {
|
|||||||
//* Apply selected preset
|
//* Apply selected preset
|
||||||
void apply_preset(const string& preset);
|
void apply_preset(const string& preset);
|
||||||
|
|
||||||
bool _locked(const string& name);
|
bool _locked(const std::string_view name);
|
||||||
|
|
||||||
//* Return bool for config key <name>
|
//* Return bool for config key <name>
|
||||||
inline const bool& getB(const string& name) { return bools.at(name); }
|
inline bool getB(const std::string_view name) { return bools.at(name); }
|
||||||
|
|
||||||
//* Return integer for config key <name>
|
//* Return integer for config key <name>
|
||||||
inline const int& getI(const string& name) { return ints.at(name); }
|
inline const int& getI(const std::string_view name) { return ints.at(name); }
|
||||||
|
|
||||||
//* Return string for config key <name>
|
//* Return string for config key <name>
|
||||||
inline const string& getS(const string& name) { return strings.at(name); }
|
inline const string& getS(const std::string_view name) { return strings.at(name); }
|
||||||
|
|
||||||
string getAsString(const string& name);
|
string getAsString(const std::string_view name);
|
||||||
|
|
||||||
extern string validError;
|
extern string validError;
|
||||||
|
|
||||||
bool intValid(const string& name, const string& value);
|
bool intValid(const std::string_view name, const string& value);
|
||||||
bool stringValid(const string& name, const string& value);
|
bool stringValid(const std::string_view name, const string& value);
|
||||||
|
|
||||||
//* Set config key <name> to bool <value>
|
//* Set config key <name> to bool <value>
|
||||||
inline void set(const string& name, const bool& value) {
|
inline void set(const std::string_view name, bool value) {
|
||||||
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
|
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
|
||||||
else bools.at(name) = value;
|
else bools.at(name) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Set config key <name> to int <value>
|
//* Set config key <name> to int <value>
|
||||||
inline void set(const string& name, const int& value) {
|
inline void set(const std::string_view name, const int& value) {
|
||||||
if (_locked(name)) intsTmp.insert_or_assign(name, value);
|
if (_locked(name)) intsTmp.insert_or_assign(name, value);
|
||||||
else ints.at(name) = value;
|
else ints.at(name) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Set config key <name> to string <value>
|
//* Set config key <name> to string <value>
|
||||||
inline void set(const string& name, const string& value) {
|
inline void set(const std::string_view name, const string& value) {
|
||||||
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
|
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
|
||||||
else strings.at(name) = value;
|
else strings.at(name) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Flip config key bool <name>
|
//* Flip config key bool <name>
|
||||||
void flip(const string& name);
|
void flip(const std::string_view name);
|
||||||
|
|
||||||
//* Lock config and cache changes until unlocked
|
//* Lock config and cache changes until unlocked
|
||||||
void lock();
|
void lock();
|
||||||
@ -111,10 +121,3 @@ namespace Config {
|
|||||||
//* Write the config file to disk
|
//* Write the config file to disk
|
||||||
void write();
|
void write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,11 @@ tab-size = 4
|
|||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
using std::string, std::array, std::vector, robin_hood::unordered_flat_map, std::deque;
|
using robin_hood::unordered_flat_map;
|
||||||
|
using std::array;
|
||||||
|
using std::deque;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
namespace Symbols {
|
namespace Symbols {
|
||||||
const string h_line = "─";
|
const string h_line = "─";
|
||||||
@ -62,8 +66,8 @@ namespace Draw {
|
|||||||
|
|
||||||
//* An editable text field
|
//* An editable text field
|
||||||
class TextEdit {
|
class TextEdit {
|
||||||
size_t pos = 0;
|
size_t pos{}; // defaults to 0
|
||||||
size_t upos = 0;
|
size_t upos{}; // defaults to 0
|
||||||
bool numeric;
|
bool numeric;
|
||||||
public:
|
public:
|
||||||
string text;
|
string text;
|
||||||
@ -75,9 +79,11 @@ namespace Draw {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//* Create a box and return as a string
|
//* Create a box and return as a string
|
||||||
string createBox(const int x, const int y, const int width, const int height, string line_color="", const bool fill=false, const string title="", const string title2="", const int num=0);
|
string createBox(const int x, const int y, const int width,
|
||||||
|
const int height, string line_color = "", bool fill = false,
|
||||||
|
const string title = "", const string title2 = "", const int num = 0);
|
||||||
|
|
||||||
bool update_clock(bool force=false);
|
bool update_clock(bool force = false);
|
||||||
|
|
||||||
//* Class holding a percentage meter
|
//* Class holding a percentage meter
|
||||||
class Meter {
|
class Meter {
|
||||||
@ -87,7 +93,7 @@ namespace Draw {
|
|||||||
array<string, 101> cache;
|
array<string, 101> cache;
|
||||||
public:
|
public:
|
||||||
Meter();
|
Meter();
|
||||||
Meter(const int width, const string& color_gradient, const bool invert = false);
|
Meter(const int width, const string& color_gradient, bool invert = false);
|
||||||
|
|
||||||
//* Return a string representation of the meter with given value
|
//* Return a string representation of the meter with given value
|
||||||
string operator()(int value);
|
string operator()(int value);
|
||||||
@ -109,18 +115,15 @@ namespace Draw {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Graph();
|
Graph();
|
||||||
Graph( int width,
|
Graph(int width, int height,
|
||||||
int height,
|
const string& color_gradient,
|
||||||
const string& color_gradient,
|
const deque<long long>& data,
|
||||||
const deque<long long>& data,
|
const string& symbol="default",
|
||||||
const string& symbol="default",
|
bool invert=false, bool no_zero=false,
|
||||||
bool invert=false,
|
long long max_value=0, long long offset=0);
|
||||||
bool no_zero=false,
|
|
||||||
long long max_value=0,
|
|
||||||
long long offset=0);
|
|
||||||
|
|
||||||
//* Add last value from back of <data> and return string representation of graph
|
//* Add last value from back of <data> and return string representation of graph
|
||||||
string& operator()(const deque<long long>& data, const bool data_same=false);
|
string& operator()(const deque<long long>& data, bool data_same=false);
|
||||||
|
|
||||||
//* Return string representation of graph
|
//* Return string representation of graph
|
||||||
string& operator()();
|
string& operator()();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -21,17 +21,29 @@ tab-size = 4
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <btop_input.hpp>
|
|
||||||
#include <btop_tools.hpp>
|
|
||||||
#include <btop_config.hpp>
|
|
||||||
#include <btop_shared.hpp>
|
|
||||||
#include <btop_menu.hpp>
|
|
||||||
#include <btop_draw.hpp>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "btop_input.hpp"
|
||||||
|
#include "btop_tools.hpp"
|
||||||
|
#include "btop_config.hpp"
|
||||||
|
#include "btop_shared.hpp"
|
||||||
|
#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 std::cin, std::vector, std::string_literals::operator""s;
|
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
using namespace std::literals; // for operator""s
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
@ -52,9 +64,13 @@ namespace Input {
|
|||||||
{"[C", "right"},
|
{"[C", "right"},
|
||||||
{"OC", "right"},
|
{"OC", "right"},
|
||||||
{"[2~", "insert"},
|
{"[2~", "insert"},
|
||||||
|
{"[4h", "insert"},
|
||||||
{"[3~", "delete"},
|
{"[3~", "delete"},
|
||||||
|
{"[P", "delete"},
|
||||||
{"[H", "home"},
|
{"[H", "home"},
|
||||||
|
{"[1~", "home"},
|
||||||
{"[F", "end"},
|
{"[F", "end"},
|
||||||
|
{"[4~", "end"},
|
||||||
{"[5~", "page_up"},
|
{"[5~", "page_up"},
|
||||||
{"[6~", "page_down"},
|
{"[6~", "page_down"},
|
||||||
{"\t", "tab"},
|
{"\t", "tab"},
|
||||||
@ -235,8 +251,8 @@ namespace Input {
|
|||||||
void process(const string& key) {
|
void process(const string& key) {
|
||||||
if (key.empty()) return;
|
if (key.empty()) return;
|
||||||
try {
|
try {
|
||||||
auto& filtering = Config::getB("proc_filtering");
|
auto filtering = Config::getB("proc_filtering");
|
||||||
auto& vim_keys = Config::getB("vim_keys");
|
auto vim_keys = Config::getB("vim_keys");
|
||||||
auto help_key = (vim_keys ? "H" : "h");
|
auto help_key = (vim_keys ? "H" : "h");
|
||||||
auto kill_key = (vim_keys ? "K" : "k");
|
auto kill_key = (vim_keys ? "K" : "k");
|
||||||
//? Global input actions
|
//? Global input actions
|
||||||
@ -257,11 +273,21 @@ namespace Input {
|
|||||||
Menu::show(Menu::Menus::Options);
|
Menu::show(Menu::Menus::Options);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (is_in(key, "1", "2", "3", "4")) {
|
else if (key.size() == 1 and isint(key)) {
|
||||||
|
auto intKey = stoi(key);
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
static const array<string, 10> boxes = {"gpu5", "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4"};
|
||||||
|
if ((intKey == 0 and Gpu::gpu_names.size() < 5) or (intKey >= 5 and std::cmp_less(Gpu::gpu_names.size(), intKey - 4)))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
static const array<string, 10> boxes = {"", "cpu", "mem", "net", "proc"};
|
||||||
|
if (intKey == 0 or intKey > 4)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
atomic_wait(Runner::active);
|
atomic_wait(Runner::active);
|
||||||
Config::current_preset = -1;
|
Config::current_preset = -1;
|
||||||
static const array<string, 4> boxes = {"cpu", "mem", "net", "proc"};
|
|
||||||
Config::toggle_box(boxes.at(std::stoi(key) - 1));
|
Config::toggle_box(boxes.at(intKey));
|
||||||
Draw::calcSizes();
|
Draw::calcSizes();
|
||||||
Runner::run("all", false, true);
|
Runner::run("all", false, true);
|
||||||
return;
|
return;
|
||||||
@ -294,12 +320,12 @@ namespace Input {
|
|||||||
if (key == "enter" or key == "down") {
|
if (key == "enter" or key == "down") {
|
||||||
Config::set("proc_filter", Proc::filter.text);
|
Config::set("proc_filter", Proc::filter.text);
|
||||||
Config::set("proc_filtering", false);
|
Config::set("proc_filtering", false);
|
||||||
old_filter.clear();
|
old_filter.clear();
|
||||||
if(key == "down"){
|
if(key == "down"){
|
||||||
process("down");
|
process("down");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == "escape" or key == "mouse_click") {
|
else if (key == "escape" or key == "mouse_click") {
|
||||||
Config::set("proc_filter", old_filter);
|
Config::set("proc_filter", old_filter);
|
||||||
Config::set("proc_filtering", false);
|
Config::set("proc_filtering", false);
|
||||||
@ -340,6 +366,9 @@ namespace Input {
|
|||||||
else if (key == "c")
|
else if (key == "c")
|
||||||
Config::flip("proc_per_core");
|
Config::flip("proc_per_core");
|
||||||
|
|
||||||
|
else if (key == "%")
|
||||||
|
Config::flip("proc_mem_bytes");
|
||||||
|
|
||||||
else if (key == "delete" and not Config::getS("proc_filter").empty())
|
else if (key == "delete" and not Config::getS("proc_filter").empty())
|
||||||
Config::set("proc_filter", ""s);
|
Config::set("proc_filter", ""s);
|
||||||
|
|
||||||
@ -545,9 +574,8 @@ namespace Input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Input::process(\"" + key + "\") : " + (string)e.what());
|
throw std::runtime_error("Input::process(\"" + key + "\") : " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -24,10 +24,15 @@ tab-size = 4
|
|||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
using robin_hood::unordered_flat_map, std::array, std::string, std::atomic, std::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:
|
/* The input functions relies on the following std::cin options being set:
|
||||||
cin.sync_with_stdio(false);
|
cin.sync_with_stdio(false);
|
||||||
cin.tie(NULL);
|
cin.tie(nullptr);
|
||||||
These will automatically be set when running Term::init() from btop_tools.cpp
|
These will automatically be set when running Term::init() from btop_tools.cpp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -19,44 +19,108 @@ tab-size = 4
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <ranges>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <btop_menu.hpp>
|
#include "btop_menu.hpp"
|
||||||
#include <btop_tools.hpp>
|
#include "btop_tools.hpp"
|
||||||
#include <btop_config.hpp>
|
#include "btop_config.hpp"
|
||||||
#include <btop_theme.hpp>
|
#include "btop_theme.hpp"
|
||||||
#include <btop_draw.hpp>
|
#include "btop_draw.hpp"
|
||||||
#include <btop_shared.hpp>
|
#include "btop_shared.hpp"
|
||||||
|
|
||||||
|
using robin_hood::unordered_flat_map;
|
||||||
|
using std::array;
|
||||||
|
using std::ceil;
|
||||||
|
using std::max;
|
||||||
|
using std::min;
|
||||||
|
using std::ref;
|
||||||
|
using std::views::iota;
|
||||||
|
|
||||||
using std::deque, robin_hood::unordered_flat_map, std::array, std::views::iota, std::ref, std::max, std::min, std::ceil, std::clamp;
|
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
|
||||||
|
|
||||||
namespace Menu {
|
namespace Menu {
|
||||||
|
|
||||||
atomic<bool> active (false);
|
atomic<bool> active (false);
|
||||||
string bg;
|
string bg;
|
||||||
bool redraw = true;
|
bool redraw{true};
|
||||||
int currentMenu = -1;
|
int currentMenu = -1;
|
||||||
msgBox messageBox;
|
msgBox messageBox;
|
||||||
int signalToSend = 0;
|
int signalToSend{}; // defaults to 0
|
||||||
int signalKillRet = 0;
|
int signalKillRet{}; // defaults to 0
|
||||||
|
|
||||||
const array<string, 32> P_Signals = {
|
const array<string, 32> P_Signals = {
|
||||||
"0",
|
"0",
|
||||||
|
#ifdef __linux__
|
||||||
|
#if defined(__hppa__)
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "SIGSTKFLT", "SIGFPE",
|
||||||
|
"SIGKILL", "SIGBUS", "SIGSEGV", "SIGXCPU",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1",
|
||||||
|
"SIGUSR2", "SIGCHLD", "SIGPWR", "SIGVTALRM",
|
||||||
|
"SIGPROF", "SIGIO", "SIGWINCH", "SIGSTOP",
|
||||||
|
"SIGTSTP", "SIGCONT", "SIGTTIN", "SIGTTOU",
|
||||||
|
"SIGURG", "SIGXFSZ", "SIGSYS"
|
||||||
|
#elif defined(__mips__)
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "SIGEMT", "SIGFPE",
|
||||||
|
"SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGUSR1",
|
||||||
|
"SIGUSR2", "SIGCHLD", "SIGPWR", "SIGWINCH",
|
||||||
|
"SIGURG", "SIGIO", "SIGSTOP", "SIGTSTP",
|
||||||
|
"SIGCONT", "SIGTTIN", "SIGTTOU", "SIGVTALRM",
|
||||||
|
"SIGPROF", "SIGXCPU", "SIGXFSZ"
|
||||||
|
#elif defined(__alpha__)
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "SIGEMT", "SIGFPE",
|
||||||
|
"SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGURG",
|
||||||
|
"SIGSTOP", "SIGTSTP", "SIGCONT", "SIGCHLD",
|
||||||
|
"SIGTTIN", "SIGTTOU", "SIGIO", "SIGXCPU",
|
||||||
|
"SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
|
||||||
|
"SIGPWR", "SIGUSR1", "SIGUSR2"
|
||||||
|
#elif defined (__sparc__)
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "SIGEMT", "SIGFPE",
|
||||||
|
"SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGURG",
|
||||||
|
"SIGSTOP", "SIGTSTP", "SIGCONT", "SIGCHLD",
|
||||||
|
"SIGTTIN", "SIGTTOU", "SIGIO", "SIGXCPU",
|
||||||
|
"SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
|
||||||
|
"SIGLOST", "SIGUSR1", "SIGUSR2"
|
||||||
|
#else
|
||||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
"SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
|
"SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
|
||||||
"SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2",
|
"SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2",
|
||||||
"SIGPIPE", "SIGALRM", "SIGTERM", "16", "SIGCHLD",
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT",
|
||||||
"SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN",
|
"SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP",
|
||||||
"SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ",
|
"SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU",
|
||||||
"SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
|
"SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
|
||||||
"SIGPWR", "SIGSYS"
|
"SIGIO", "SIGPWR", "SIGSYS"
|
||||||
|
#endif
|
||||||
|
#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "SIGEMT", "SIGFPE",
|
||||||
|
"SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "SIGURG",
|
||||||
|
"SIGSTOP", "SIGTSTP", "SIGCONT", "SIGCHLD",
|
||||||
|
"SIGTTIN", "SIGTTOU", "SIGIO", "SIGXCPU",
|
||||||
|
"SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
|
||||||
|
"SIGINFO", "SIGUSR1", "SIGUSR2"
|
||||||
|
#else
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||||
|
"SIGTRAP", "SIGABRT", "7", "SIGFPE",
|
||||||
|
"SIGKILL", "10", "SIGSEGV", "12",
|
||||||
|
"SIGPIPE", "SIGALRM", "SIGTERM", "16",
|
||||||
|
"17", "18", "19", "20",
|
||||||
|
"21", "22", "23", "24",
|
||||||
|
"25", "26", "27", "28",
|
||||||
|
"29", "30", "31"
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||||
@ -109,6 +173,7 @@ namespace Menu {
|
|||||||
{"2", "Toggle MEM box."},
|
{"2", "Toggle MEM box."},
|
||||||
{"3", "Toggle NET box."},
|
{"3", "Toggle NET box."},
|
||||||
{"4", "Toggle PROC box."},
|
{"4", "Toggle PROC box."},
|
||||||
|
{"5", "Toggle GPU box."},
|
||||||
{"d", "Toggle disks view in MEM box."},
|
{"d", "Toggle disks view in MEM box."},
|
||||||
{"F2, o", "Shows options."},
|
{"F2, o", "Shows options."},
|
||||||
{"F1, ?, h", "Shows this window."},
|
{"F1, ?, h", "Shows this window."},
|
||||||
@ -131,6 +196,7 @@ namespace Menu {
|
|||||||
{"c", "Toggle per-core cpu usage of processes."},
|
{"c", "Toggle per-core cpu usage of processes."},
|
||||||
{"r", "Reverse sorting order in processes box."},
|
{"r", "Reverse sorting order in processes box."},
|
||||||
{"e", "Toggle processes tree view."},
|
{"e", "Toggle processes tree view."},
|
||||||
|
{"%", "Toggles memory display mode in processes box."},
|
||||||
{"Selected +, -", "Expand/collapse the selected process in tree view."},
|
{"Selected +, -", "Expand/collapse the selected process in tree view."},
|
||||||
{"Selected t", "Terminate selected process with SIGTERM - 15."},
|
{"Selected t", "Terminate selected process with SIGTERM - 15."},
|
||||||
{"Selected k", "Kill selected process with SIGKILL - 9."},
|
{"Selected k", "Kill selected process with SIGKILL - 9."},
|
||||||
@ -197,7 +263,7 @@ namespace Menu {
|
|||||||
"P=(0 or 1) for alternate positions.",
|
"P=(0 or 1) for alternate positions.",
|
||||||
"G=graph symbol to use for box.",
|
"G=graph symbol to use for box.",
|
||||||
"",
|
"",
|
||||||
"Use withespace \" \" as separator between",
|
"Use whitespace \" \" as separator between",
|
||||||
"different presets.",
|
"different presets.",
|
||||||
"",
|
"",
|
||||||
"Example:",
|
"Example:",
|
||||||
@ -206,6 +272,9 @@ namespace Menu {
|
|||||||
"Manually set which boxes to show.",
|
"Manually set which boxes to show.",
|
||||||
"",
|
"",
|
||||||
"Available values are \"cpu mem net proc\".",
|
"Available values are \"cpu mem net proc\".",
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
"Or \"gpu0\" through \"gpu5\" for GPU boxes.",
|
||||||
|
#endif
|
||||||
"Separate values with whitespace.",
|
"Separate values with whitespace.",
|
||||||
"",
|
"",
|
||||||
"Toggle between presets with key \"p\"."},
|
"Toggle between presets with key \"p\"."},
|
||||||
@ -308,23 +377,49 @@ namespace Menu {
|
|||||||
{"cpu_graph_upper",
|
{"cpu_graph_upper",
|
||||||
"Cpu upper graph.",
|
"Cpu upper graph.",
|
||||||
"",
|
"",
|
||||||
"Sets the CPU stat shown in upper half of",
|
"Sets the CPU/GPU stat shown in upper half of",
|
||||||
"the CPU graph.",
|
"the CPU graph.",
|
||||||
"",
|
"",
|
||||||
"\"total\" = Total cpu usage.",
|
"CPU:",
|
||||||
|
"\"total\" = Total cpu usage. (Auto)",
|
||||||
"\"user\" = User mode cpu usage.",
|
"\"user\" = User mode cpu usage.",
|
||||||
"\"system\" = Kernel mode cpu usage.",
|
"\"system\" = Kernel mode cpu usage.",
|
||||||
"+ more depending on kernel."},
|
"+ more depending on kernel.",
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
"",
|
||||||
|
"GPU:",
|
||||||
|
"\"gpu-totals\" = GPU usage split by device.",
|
||||||
|
"\"gpu-vram-totals\" = VRAM usage split by GPU.",
|
||||||
|
"\"gpu-pwr-totals\" = Power usage split by GPU.",
|
||||||
|
"\"gpu-average\" = Avg usage of all GPUs.",
|
||||||
|
"\"gpu-vram-total\" = VRAM usage of all GPUs.",
|
||||||
|
"\"gpu-pwr-total\" = Power usage of all GPUs.",
|
||||||
|
"Not all stats are supported on all devices."
|
||||||
|
#endif
|
||||||
|
},
|
||||||
{"cpu_graph_lower",
|
{"cpu_graph_lower",
|
||||||
"Cpu lower graph.",
|
"Cpu lower graph.",
|
||||||
"",
|
"",
|
||||||
"Sets the CPU stat shown in lower half of",
|
"Sets the CPU/GPU stat shown in lower half of",
|
||||||
"the CPU graph.",
|
"the CPU graph.",
|
||||||
"",
|
"",
|
||||||
|
"CPU:",
|
||||||
"\"total\" = Total cpu usage.",
|
"\"total\" = Total cpu usage.",
|
||||||
"\"user\" = User mode cpu usage.",
|
"\"user\" = User mode cpu usage.",
|
||||||
"\"system\" = Kernel mode cpu usage.",
|
"\"system\" = Kernel mode cpu usage.",
|
||||||
"+ more depending on kernel."},
|
"+ more depending on kernel.",
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
"",
|
||||||
|
"GPU:",
|
||||||
|
"\"gpu-totals\" = GPU usage split/device. (Auto)",
|
||||||
|
"\"gpu-vram-totals\" = VRAM usage split by GPU.",
|
||||||
|
"\"gpu-pwr-totals\" = Power usage split by GPU.",
|
||||||
|
"\"gpu-average\" = Avg usage of all GPUs.",
|
||||||
|
"\"gpu-vram-total\" = VRAM usage of all GPUs.",
|
||||||
|
"\"gpu-pwr-total\" = Power usage of all GPUs.",
|
||||||
|
"Not all stats are supported on all devices."
|
||||||
|
#endif
|
||||||
|
},
|
||||||
{"cpu_invert_lower",
|
{"cpu_invert_lower",
|
||||||
"Toggles orientation of the lower CPU graph.",
|
"Toggles orientation of the lower CPU graph.",
|
||||||
"",
|
"",
|
||||||
@ -336,12 +431,24 @@ namespace Menu {
|
|||||||
"to fit to box height.",
|
"to fit to box height.",
|
||||||
"",
|
"",
|
||||||
"True or False."},
|
"True or False."},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"show_gpu_info",
|
||||||
|
"Show gpu info in cpu box.",
|
||||||
|
"",
|
||||||
|
"Toggles gpu stats in cpu box and the",
|
||||||
|
"gpu graph (if \"cpu_graph_lower\" is set to",
|
||||||
|
"\"Auto\").",
|
||||||
|
"",
|
||||||
|
"\"Auto\" to show when no gpu box is shown.",
|
||||||
|
"\"On\" to always show.",
|
||||||
|
"\"Off\" to never show."},
|
||||||
|
#endif
|
||||||
{"check_temp",
|
{"check_temp",
|
||||||
"Enable cpu temperature reporting.",
|
"Enable cpu temperature reporting.",
|
||||||
"",
|
"",
|
||||||
"True or False."},
|
"True or False."},
|
||||||
{"cpu_sensor",
|
{"cpu_sensor",
|
||||||
"Cpu temperature sensor",
|
"Cpu temperature sensor.",
|
||||||
"",
|
"",
|
||||||
"Select the sensor that corresponds to",
|
"Select the sensor that corresponds to",
|
||||||
"your cpu temperature.",
|
"your cpu temperature.",
|
||||||
@ -381,7 +488,7 @@ namespace Menu {
|
|||||||
"Rankine, 0 = abosulte zero, 1 degree change",
|
"Rankine, 0 = abosulte zero, 1 degree change",
|
||||||
"equals 1 degree change in Fahrenheit."},
|
"equals 1 degree change in Fahrenheit."},
|
||||||
{"show_cpu_freq",
|
{"show_cpu_freq",
|
||||||
"Show CPU frequency",
|
"Show CPU frequency.",
|
||||||
"",
|
"",
|
||||||
"Can cause slowdowns on systems with many",
|
"Can cause slowdowns on systems with many",
|
||||||
"cores and certain kernel versions."},
|
"cores and certain kernel versions."},
|
||||||
@ -397,6 +504,50 @@ namespace Menu {
|
|||||||
"",
|
"",
|
||||||
"True or False."},
|
"True or False."},
|
||||||
},
|
},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{
|
||||||
|
{"nvml_measure_pcie_speeds",
|
||||||
|
"Measure PCIe throughput on NVIDIA cards.",
|
||||||
|
"",
|
||||||
|
"May impact performance on certain cards.",
|
||||||
|
"",
|
||||||
|
"True or False."},
|
||||||
|
{"graph_symbol_gpu",
|
||||||
|
"Graph symbol to use for graphs in gpu box.",
|
||||||
|
"",
|
||||||
|
"\"default\", \"braille\", \"block\" or \"tty\".",
|
||||||
|
"",
|
||||||
|
"\"default\" for the general default symbol.",},
|
||||||
|
{"gpu_mirror_graph",
|
||||||
|
"Horizontally mirror the GPU graph.",
|
||||||
|
"",
|
||||||
|
"True or False."},
|
||||||
|
{"custom_gpu_name0",
|
||||||
|
"Custom gpu0 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
{"custom_gpu_name1",
|
||||||
|
"Custom gpu1 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
{"custom_gpu_name2",
|
||||||
|
"Custom gpu2 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
{"custom_gpu_name3",
|
||||||
|
"Custom gpu3 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
{"custom_gpu_name4",
|
||||||
|
"Custom gpu4 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
{"custom_gpu_name5",
|
||||||
|
"Custom gpu5 model name in gpu stats box.",
|
||||||
|
"",
|
||||||
|
"Empty string to disable."},
|
||||||
|
},
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
{"mem_below_net",
|
{"mem_below_net",
|
||||||
"Mem box location.",
|
"Mem box location.",
|
||||||
@ -583,6 +734,11 @@ namespace Menu {
|
|||||||
"Set true to show processes grouped by",
|
"Set true to show processes grouped by",
|
||||||
"parents with lines drawn between parent",
|
"parents with lines drawn between parent",
|
||||||
"and child process."},
|
"and child process."},
|
||||||
|
{"proc_aggregate",
|
||||||
|
"Aggregate child's resources in parent.",
|
||||||
|
"",
|
||||||
|
"In tree-view, include all child resources",
|
||||||
|
"with the parent even while expanded."},
|
||||||
{"proc_colors",
|
{"proc_colors",
|
||||||
"Enable colors in process view.",
|
"Enable colors in process view.",
|
||||||
"",
|
"",
|
||||||
@ -611,19 +767,19 @@ namespace Menu {
|
|||||||
"Show cpu graph for each process.",
|
"Show cpu graph for each process.",
|
||||||
"",
|
"",
|
||||||
"True or False"},
|
"True or False"},
|
||||||
{"proc_filter_kernel",
|
{"proc_filter_kernel",
|
||||||
"(Linux) Filter kernel processes from output.",
|
"(Linux) Filter kernel processes from output.",
|
||||||
"",
|
"",
|
||||||
"Set to 'True' to filter out internal",
|
"Set to 'True' to filter out internal",
|
||||||
"processes started by the Linux kernel."},
|
"processes started by the Linux kernel."},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
msgBox::msgBox() {}
|
msgBox::msgBox() {}
|
||||||
msgBox::msgBox(int width, int boxtype, vector<string> content, string title)
|
msgBox::msgBox(int width, int boxtype, vector<string> content, string title)
|
||||||
: width(width), boxtype(boxtype) {
|
: width(width), boxtype(boxtype) {
|
||||||
const auto& tty_mode = Config::getB("tty_mode");
|
auto tty_mode = Config::getB("tty_mode");
|
||||||
const auto& rounded = Config::getB("rounded_corners");
|
auto rounded = Config::getB("rounded_corners");
|
||||||
const auto& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
|
const auto& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
|
||||||
const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
|
const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
|
||||||
const auto& right_down = (tty_mode or not rounded ? Symbols::right_down : Symbols::round_right_down);
|
const auto& right_down = (tty_mode or not rounded ? Symbols::right_down : Symbols::round_right_down);
|
||||||
@ -712,8 +868,11 @@ namespace Menu {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int signalChoose(const string& key) {
|
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"));
|
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||||
static int x = 0, y = 0, selected_signal = -1;
|
static int x{}; // defaults to 0
|
||||||
|
static int y{}; // defaults to 0
|
||||||
|
static int selected_signal = -1;
|
||||||
|
|
||||||
if (bg.empty()) selected_signal = -1;
|
if (bg.empty()) selected_signal = -1;
|
||||||
auto& out = Global::overlay;
|
auto& out = Global::overlay;
|
||||||
int retval = Changed;
|
int retval = Changed;
|
||||||
@ -839,7 +998,7 @@ namespace Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int signalSend(const string& key) {
|
int signalSend(const string& key) {
|
||||||
auto& s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||||
if (s_pid == 0) return Closed;
|
if (s_pid == 0) return Closed;
|
||||||
if (redraw) {
|
if (redraw) {
|
||||||
atomic_wait(Runner::active);
|
atomic_wait(Runner::active);
|
||||||
@ -912,10 +1071,11 @@ namespace Menu {
|
|||||||
|
|
||||||
int mainMenu(const string& key) {
|
int mainMenu(const string& key) {
|
||||||
enum MenuItems { Options, Help, Quit };
|
enum MenuItems { Options, Help, Quit };
|
||||||
static int y = 0, selected = 0;
|
static int y{}; // defaults to 0
|
||||||
|
static int selected{}; // defaults to 0
|
||||||
static vector<string> colors_selected;
|
static vector<string> colors_selected;
|
||||||
static vector<string> colors_normal;
|
static vector<string> colors_normal;
|
||||||
auto& tty_mode = Config::getB("tty_mode");
|
auto tty_mode = Config::getB("tty_mode");
|
||||||
if (bg.empty()) selected = 0;
|
if (bg.empty()) selected = 0;
|
||||||
int retval = Changed;
|
int retval = Changed;
|
||||||
|
|
||||||
@ -969,7 +1129,6 @@ namespace Menu {
|
|||||||
retval = NoChange;
|
retval = NoChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (retval == Changed) {
|
if (retval == Changed) {
|
||||||
auto& out = Global::overlay;
|
auto& out = Global::overlay;
|
||||||
out = bg + Fx::reset + Fx::b;
|
out = bg + Fx::reset + Fx::b;
|
||||||
@ -986,14 +1145,23 @@ namespace Menu {
|
|||||||
out += Fx::reset;
|
out += Fx::reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (redraw ? Changed : retval);
|
return (redraw ? Changed : retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
int optionsMenu(const string& key) {
|
int optionsMenu(const string& key) {
|
||||||
enum Predispositions { isBool, isInt, isString, is2D, isBrowseable, isEditable};
|
enum Predispositions { isBool, isInt, isString, is2D, isBrowseable, isEditable};
|
||||||
static int y = 0, x = 0, height = 0, page = 0, pages = 0, selected = 0, select_max = 0, item_height = 0, selected_cat = 0, max_items = 0, last_sel = 0;
|
static int y{}; // defaults to 0
|
||||||
static bool editing = false;
|
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 Draw::TextEdit editor;
|
static Draw::TextEdit editor;
|
||||||
static string warnings;
|
static string warnings;
|
||||||
static bitset<8> selPred;
|
static bitset<8> selPred;
|
||||||
@ -1011,9 +1179,13 @@ namespace Menu {
|
|||||||
{"cpu_graph_lower", std::cref(Cpu::available_fields)},
|
{"cpu_graph_lower", std::cref(Cpu::available_fields)},
|
||||||
{"cpu_sensor", std::cref(Cpu::available_sensors)},
|
{"cpu_sensor", std::cref(Cpu::available_sensors)},
|
||||||
{"selected_battery", std::cref(Config::available_batteries)},
|
{"selected_battery", std::cref(Config::available_batteries)},
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
{"show_gpu_info", std::cref(Config::show_gpu_values)},
|
||||||
|
{"graph_symbol_gpu", std::cref(Config::valid_graph_symbols_def)},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
auto& tty_mode = Config::getB("tty_mode");
|
auto tty_mode = Config::getB("tty_mode");
|
||||||
auto& vim_keys = Config::getB("vim_keys");
|
auto vim_keys = Config::getB("vim_keys");
|
||||||
if (max_items == 0) {
|
if (max_items == 0) {
|
||||||
for (const auto& cat : categories) {
|
for (const auto& cat : categories) {
|
||||||
if ((int)cat.size() > max_items) max_items = cat.size();
|
if ((int)cat.size() > max_items) max_items = cat.size();
|
||||||
@ -1025,9 +1197,9 @@ namespace Menu {
|
|||||||
Theme::updateThemes();
|
Theme::updateThemes();
|
||||||
}
|
}
|
||||||
int retval = Changed;
|
int retval = Changed;
|
||||||
bool recollect = false;
|
bool recollect{}; // defaults to false
|
||||||
bool screen_redraw = false;
|
bool screen_redraw{}; // defaults to false
|
||||||
bool theme_refresh = false;
|
bool theme_refresh{}; // defaults to false
|
||||||
|
|
||||||
//? Draw background if needed else process input
|
//? Draw background if needed else process input
|
||||||
if (redraw) {
|
if (redraw) {
|
||||||
@ -1062,7 +1234,8 @@ namespace Menu {
|
|||||||
const auto& option = categories[selected_cat][item_height * page + selected][0];
|
const auto& option = categories[selected_cat][item_height * page + selected][0];
|
||||||
if (selPred.test(isString) and Config::stringValid(option, editor.text)) {
|
if (selPred.test(isString) and Config::stringValid(option, editor.text)) {
|
||||||
Config::set(option, editor.text);
|
Config::set(option, editor.text);
|
||||||
if (option == "custom_cpu_name") screen_redraw = true;
|
if (option == "custom_cpu_name" or option.starts_with("custom_gpu_name"))
|
||||||
|
screen_redraw = true;
|
||||||
else if (is_in(option, "shown_boxes", "presets")) {
|
else if (is_in(option, "shown_boxes", "presets")) {
|
||||||
screen_redraw = true;
|
screen_redraw = true;
|
||||||
Config::current_preset = -1;
|
Config::current_preset = -1;
|
||||||
@ -1143,7 +1316,7 @@ namespace Menu {
|
|||||||
if (--selected_cat < 0) selected_cat = (int)categories.size() - 1;
|
if (--selected_cat < 0) selected_cat = (int)categories.size() - 1;
|
||||||
page = selected = 0;
|
page = selected = 0;
|
||||||
}
|
}
|
||||||
else if (is_in(key, "1", "2", "3", "4", "5") or key.starts_with("select_cat_")) {
|
else if (is_in(key, "1", "2", "3", "4", "5", "6") or key.starts_with("select_cat_")) {
|
||||||
selected_cat = key.back() - '0' - 1;
|
selected_cat = key.back() - '0' - 1;
|
||||||
page = selected = 0;
|
page = selected = 0;
|
||||||
}
|
}
|
||||||
@ -1195,7 +1368,7 @@ namespace Menu {
|
|||||||
Logger::set(optList.at(i));
|
Logger::set(optList.at(i));
|
||||||
Logger::info("Logger set to " + optList.at(i));
|
Logger::info("Logger set to " + optList.at(i));
|
||||||
}
|
}
|
||||||
else if (is_in(option, "proc_sorting", "cpu_sensor") or option.starts_with("graph_symbol") or option.starts_with("cpu_graph_"))
|
else if (is_in(option, "proc_sorting", "cpu_sensor", "show_gpu_info") or option.starts_with("graph_symbol") or option.starts_with("cpu_graph_"))
|
||||||
screen_redraw = true;
|
screen_redraw = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1241,11 +1414,19 @@ namespace Menu {
|
|||||||
|
|
||||||
//? Category buttons
|
//? Category buttons
|
||||||
out += Mv::to(y+7, x+4);
|
out += Mv::to(y+7, x+4);
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
for (int i = 0; const auto& m : {"general", "cpu", "gpu", "mem", "net", "proc"}) {
|
||||||
|
#else
|
||||||
for (int i = 0; const auto& m : {"general", "cpu", "mem", "net", "proc"}) {
|
for (int i = 0; const auto& m : {"general", "cpu", "mem", "net", "proc"}) {
|
||||||
|
#endif
|
||||||
out += Fx::b + (i == selected_cat
|
out += Fx::b + (i == selected_cat
|
||||||
? Theme::c("hi_fg") + '[' + Theme::c("title") + m + Theme::c("hi_fg") + ']'
|
? Theme::c("hi_fg") + '[' + Theme::c("title") + m + Theme::c("hi_fg") + ']'
|
||||||
: Theme::c("hi_fg") + to_string(i + 1) + Theme::c("title") + m + ' ')
|
: Theme::c("hi_fg") + to_string(i + 1) + Theme::c("title") + m + ' ')
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
+ Mv::r(7);
|
||||||
|
#else
|
||||||
+ Mv::r(10);
|
+ Mv::r(10);
|
||||||
|
#endif
|
||||||
if (string button_name = "select_cat_" + to_string(i + 1); not editing and not mouse_mappings.contains(button_name))
|
if (string button_name = "select_cat_" + to_string(i + 1); not editing and not mouse_mappings.contains(button_name))
|
||||||
mouse_mappings[button_name] = {y+6, x+2 + 15*i, 3, 15};
|
mouse_mappings[button_name] = {y+6, x+2 + 15*i, 3, 15};
|
||||||
i++;
|
i++;
|
||||||
@ -1304,11 +1485,11 @@ namespace Menu {
|
|||||||
optionsMenu("");
|
optionsMenu("");
|
||||||
}
|
}
|
||||||
if (screen_redraw) {
|
if (screen_redraw) {
|
||||||
auto overlay_bkp = move(Global::overlay);
|
auto overlay_bkp = std::move(Global::overlay);
|
||||||
auto clock_bkp = move(Global::clock);
|
auto clock_bkp = std::move(Global::clock);
|
||||||
Draw::calcSizes();
|
Draw::calcSizes();
|
||||||
Global::overlay = move(overlay_bkp);
|
Global::overlay = std::move(overlay_bkp);
|
||||||
Global::clock = move(clock_bkp);
|
Global::clock = std::move(clock_bkp);
|
||||||
recollect = true;
|
recollect = true;
|
||||||
}
|
}
|
||||||
if (recollect) {
|
if (recollect) {
|
||||||
@ -1320,7 +1501,12 @@ namespace Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int helpMenu(const string& key) {
|
int helpMenu(const string& key) {
|
||||||
static int y = 0, x = 0, height = 0, page = 0, pages = 0;
|
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
|
||||||
|
|
||||||
if (bg.empty()) page = 0;
|
if (bg.empty()) page = 0;
|
||||||
int retval = Changed;
|
int retval = Changed;
|
||||||
|
|
||||||
|
@ -23,9 +23,12 @@ tab-size = 4
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
|
||||||
#include <btop_input.hpp>
|
#include "btop_input.hpp"
|
||||||
|
|
||||||
using std::string, std::atomic, std::vector, std::bitset;
|
using std::atomic;
|
||||||
|
using std::bitset;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
namespace Menu {
|
namespace Menu {
|
||||||
|
|
||||||
@ -43,7 +46,12 @@ namespace Menu {
|
|||||||
//? Strings in content vector is not checked for box width overflow
|
//? Strings in content vector is not checked for box width overflow
|
||||||
class msgBox {
|
class msgBox {
|
||||||
string box_contents, button_left, button_right;
|
string box_contents, button_left, button_right;
|
||||||
int height = 0, width = 0, boxtype = 0, selected = 0, x = 0, y = 0;
|
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
|
||||||
public:
|
public:
|
||||||
enum BoxTypes { OK, YES_NO, NO_YES };
|
enum BoxTypes { OK, YES_NO, NO_YES };
|
||||||
enum msgReturn {
|
enum msgReturn {
|
||||||
|
@ -18,16 +18,28 @@ tab-size = 4
|
|||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include <btop_shared.hpp>
|
#include "btop_config.hpp"
|
||||||
#include <btop_tools.hpp>
|
#include "btop_shared.hpp"
|
||||||
|
#include "btop_tools.hpp"
|
||||||
|
|
||||||
using std::string_literals::operator""s;
|
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
namespace Gpu {
|
||||||
|
vector<string> gpu_names;
|
||||||
|
vector<int> gpu_b_height_offsets;
|
||||||
|
unordered_flat_map<string, deque<long long>> shared_gpu_percent = {
|
||||||
|
{"gpu-average", {}},
|
||||||
|
{"gpu-vram-total", {}},
|
||||||
|
{"gpu-pwr-total", {}},
|
||||||
|
};
|
||||||
|
long long gpu_pwr_total_max;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Proc {
|
namespace Proc {
|
||||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, const bool reverse, const bool tree) {
|
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
switch (v_index(sort_vector, sorting)) {
|
switch (v_index(sort_vector, sorting)) {
|
||||||
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
|
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
|
||||||
@ -71,7 +83,7 @@ namespace Proc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, const bool reverse, int& c_index, const int index_max, const bool collapsed) {
|
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, int& c_index, const int index_max, bool collapsed) {
|
||||||
if (proc_vec.size() > 1) {
|
if (proc_vec.size() > 1) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
switch (v_index(sort_vector, sorting)) {
|
switch (v_index(sort_vector, sorting)) {
|
||||||
@ -99,16 +111,17 @@ namespace Proc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found, const bool no_update, const bool should_filter) {
|
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||||
|
int cur_depth, bool collapsed, const string& filter, bool found, bool no_update, bool should_filter) {
|
||||||
auto cur_pos = out_procs.size();
|
auto cur_pos = out_procs.size();
|
||||||
bool filtering = false;
|
bool filtering = false;
|
||||||
|
|
||||||
//? If filtering, include children of matching processes
|
//? If filtering, include children of matching processes
|
||||||
if (not found and (should_filter or not filter.empty())) {
|
if (not found and (should_filter or not filter.empty())) {
|
||||||
if (not s_contains(std::to_string(cur_proc.pid), filter)
|
if (not s_contains(std::to_string(cur_proc.pid), filter)
|
||||||
and not s_contains(cur_proc.name, filter)
|
and not s_contains_ic(cur_proc.name, filter)
|
||||||
and not s_contains(cur_proc.cmd, filter)
|
and not s_contains_ic(cur_proc.cmd, filter)
|
||||||
and not s_contains(cur_proc.user, filter)) {
|
and not s_contains_ic(cur_proc.user, filter)) {
|
||||||
filtering = true;
|
filtering = true;
|
||||||
cur_proc.filtered = true;
|
cur_proc.filtered = true;
|
||||||
filter_found++;
|
filter_found++;
|
||||||
@ -132,7 +145,7 @@ namespace Proc {
|
|||||||
std::string_view cmd_view = cur_proc.cmd;
|
std::string_view cmd_view = cur_proc.cmd;
|
||||||
cmd_view = cmd_view.substr((size_t)0, std::min(cmd_view.find(' '), cmd_view.size()));
|
cmd_view = cmd_view.substr((size_t)0, std::min(cmd_view.find(' '), cmd_view.size()));
|
||||||
cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
|
cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
|
||||||
cur_proc.short_cmd = (string)cmd_view;
|
cur_proc.short_cmd = string{cmd_view};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -140,12 +153,10 @@ namespace Proc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//? Recursive iteration over all children
|
//? Recursive iteration over all children
|
||||||
int children = 0;
|
|
||||||
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
|
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
|
||||||
if (collapsed and not filtering) {
|
if (collapsed and not filtering) {
|
||||||
cur_proc.filtered = true;
|
cur_proc.filtered = true;
|
||||||
}
|
}
|
||||||
children++;
|
|
||||||
|
|
||||||
_tree_gen(p, in_procs, out_procs.back().children, cur_depth + 1, (collapsed or cur_proc.collapsed), filter, found, no_update, should_filter);
|
_tree_gen(p, in_procs, out_procs.back().children, cur_depth + 1, (collapsed or cur_proc.collapsed), filter, found, no_update, should_filter);
|
||||||
|
|
||||||
@ -158,17 +169,24 @@ namespace Proc {
|
|||||||
filter_found++;
|
filter_found++;
|
||||||
p.filtered = true;
|
p.filtered = true;
|
||||||
}
|
}
|
||||||
|
else if (Config::getB("proc_aggregate")) {
|
||||||
|
cur_proc.cpu_p += p.cpu_p;
|
||||||
|
cur_proc.cpu_c += p.cpu_c;
|
||||||
|
cur_proc.mem += p.mem;
|
||||||
|
cur_proc.threads += p.threads;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (collapsed or filtering) {
|
if (collapsed or filtering) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Add tree terminator symbol if it's the last child in a sub-tree
|
//? Add tree terminator symbol if it's the last child in a sub-tree
|
||||||
if (children > 0 and out_procs.back().children.back().entry.get().prefix.size() >= 8 and not out_procs.back().children.back().entry.get().prefix.ends_with("]─"))
|
if (out_procs.back().children.size() > 0 and out_procs.back().children.back().entry.get().prefix.size() >= 8 and not out_procs.back().children.back().entry.get().prefix.ends_with("]─"))
|
||||||
out_procs.back().children.back().entry.get().prefix.replace(out_procs.back().children.back().entry.get().prefix.size() - 8, 8, " └─ ");
|
out_procs.back().children.back().entry.get().prefix.replace(out_procs.back().children.back().entry.get().prefix.size() - 8, 8, " └─ ");
|
||||||
|
|
||||||
//? Add collapse/expand symbols if process have any children
|
//? Add collapse/expand symbols if process have any children
|
||||||
out_procs.at(cur_pos).entry.get().prefix = " │ "s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
|
out_procs.at(cur_pos).entry.get().prefix = " │ "s * cur_depth + (out_procs.at(cur_pos).children.size() > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -18,18 +18,26 @@ tab-size = 4
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <array>
|
||||||
#include <vector>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <robin_hood.h>
|
#include <filesystem>
|
||||||
#include <array>
|
#include <string>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <robin_hood.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array, std::tuple;
|
using robin_hood::unordered_flat_map;
|
||||||
|
using std::array;
|
||||||
|
using std::atomic;
|
||||||
|
using std::deque;
|
||||||
|
using std::string;
|
||||||
|
using std::tuple;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
using namespace std::literals; // for operator""s
|
||||||
|
|
||||||
void term_resize(bool force=false);
|
void term_resize(bool force=false);
|
||||||
void banner_gen();
|
void banner_gen();
|
||||||
@ -55,11 +63,12 @@ namespace Runner {
|
|||||||
extern atomic<bool> reading;
|
extern atomic<bool> reading;
|
||||||
extern atomic<bool> stopping;
|
extern atomic<bool> stopping;
|
||||||
extern atomic<bool> redraw;
|
extern atomic<bool> redraw;
|
||||||
|
extern atomic<bool> coreNum_reset;
|
||||||
extern pthread_t runner_id;
|
extern pthread_t runner_id;
|
||||||
extern bool pause_output;
|
extern bool pause_output;
|
||||||
extern string debug_bg;
|
extern string debug_bg;
|
||||||
|
|
||||||
void run(const string& box="", const bool no_update=false, const bool force_redraw=false);
|
void run(const string& box="", bool no_update = false, bool force_redraw = false);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -77,6 +86,91 @@ namespace Shared {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace Gpu {
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
extern vector<string> box;
|
||||||
|
extern int width, height, min_width, min_height;
|
||||||
|
extern vector<int> x_vec, y_vec;
|
||||||
|
extern vector<bool> redraw;
|
||||||
|
extern int shown;
|
||||||
|
extern vector<char> shown_panels;
|
||||||
|
extern vector<string> gpu_names;
|
||||||
|
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
|
||||||
|
|
||||||
|
const array mem_names { "used"s, "free"s };
|
||||||
|
|
||||||
|
//* Container for process information // TODO
|
||||||
|
/*struct proc_info {
|
||||||
|
unsigned int pid;
|
||||||
|
unsigned long long mem;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
//* Container for supported Gpu::*::collect() functions
|
||||||
|
struct gpu_info_supported {
|
||||||
|
bool gpu_utilization = true,
|
||||||
|
mem_utilization = true,
|
||||||
|
gpu_clock = true,
|
||||||
|
mem_clock = true,
|
||||||
|
pwr_usage = true,
|
||||||
|
pwr_state = true,
|
||||||
|
temp_info = true,
|
||||||
|
mem_total = true,
|
||||||
|
mem_used = true,
|
||||||
|
pcie_txrx = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//* Per-device container for GPU info
|
||||||
|
struct gpu_info {
|
||||||
|
unordered_flat_map<string, deque<long long>> gpu_percent = {
|
||||||
|
{"gpu-totals", {}},
|
||||||
|
{"gpu-vram-totals", {}},
|
||||||
|
{"gpu-pwr-totals", {}},
|
||||||
|
};
|
||||||
|
unsigned int gpu_clock_speed; // MHz
|
||||||
|
|
||||||
|
long long pwr_usage; // mW
|
||||||
|
long long pwr_max_usage = 255000;
|
||||||
|
long long pwr_state;
|
||||||
|
|
||||||
|
deque<long long> temp = {0};
|
||||||
|
long long temp_max = 110;
|
||||||
|
|
||||||
|
long long mem_total = 0;
|
||||||
|
long long mem_used = 0;
|
||||||
|
deque<long long> mem_utilization_percent = {0}; // TODO: properly handle GPUs that can't report some stats
|
||||||
|
long long mem_clock_speed = 0; // MHz
|
||||||
|
|
||||||
|
long long pcie_tx = 0; // KB/s
|
||||||
|
long long pcie_rx = 0;
|
||||||
|
|
||||||
|
gpu_info_supported supported_functions;
|
||||||
|
|
||||||
|
// vector<proc_info> graphics_processes = {}; // TODO
|
||||||
|
// vector<proc_info> compute_processes = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Nvml {
|
||||||
|
extern bool shutdown();
|
||||||
|
}
|
||||||
|
namespace Rsmi {
|
||||||
|
extern bool shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
//* Collect gpu stats and temperatures
|
||||||
|
auto collect(bool no_update = false) -> vector<gpu_info>&;
|
||||||
|
|
||||||
|
//* Draw contents of gpu box using <gpus> as source
|
||||||
|
string draw(const gpu_info& gpu, unsigned long index, bool force_redraw, bool data_same);
|
||||||
|
#else
|
||||||
|
struct gpu_info {
|
||||||
|
bool supported = false;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
extern string box;
|
extern string box;
|
||||||
extern int x, y, width, height, min_width, min_height;
|
extern int x, y, width, height, min_width, min_height;
|
||||||
@ -103,14 +197,14 @@ namespace Cpu {
|
|||||||
vector<deque<long long>> core_percent;
|
vector<deque<long long>> core_percent;
|
||||||
vector<deque<long long>> temp;
|
vector<deque<long long>> temp;
|
||||||
long long temp_max = 0;
|
long long temp_max = 0;
|
||||||
array<float, 3> load_avg;
|
array<double, 3> load_avg;
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Collect cpu stats and temperatures
|
//* Collect cpu stats and temperatures
|
||||||
auto collect(const bool no_update=false) -> cpu_info&;
|
auto collect(bool no_update = false) -> cpu_info&;
|
||||||
|
|
||||||
//* Draw contents of cpu box using <cpu> as source
|
//* Draw contents of cpu box using <cpu> as source
|
||||||
string draw(const cpu_info& cpu, const bool force_redraw=false, const bool data_same=false);
|
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
|
//* Parse /proc/cpu info for mapping of core ids
|
||||||
auto get_core_mapping() -> unordered_flat_map<int, int>;
|
auto get_core_mapping() -> unordered_flat_map<int, int>;
|
||||||
@ -124,17 +218,21 @@ namespace Mem {
|
|||||||
extern string box;
|
extern string box;
|
||||||
extern int x, y, width, height, min_width, min_height;
|
extern int x, y, width, height, min_width, min_height;
|
||||||
extern bool has_swap, shown, redraw;
|
extern bool has_swap, shown, redraw;
|
||||||
const array<string, 4> mem_names = {"used", "available", "cached", "free"};
|
const array mem_names { "used"s, "available"s, "cached"s, "free"s };
|
||||||
const array<string, 2> swap_names = {"swap_used", "swap_free"};
|
const array swap_names { "swap_used"s, "swap_free"s };
|
||||||
extern int disk_ios;
|
extern int disk_ios;
|
||||||
|
|
||||||
struct disk_info {
|
struct disk_info {
|
||||||
std::filesystem::path dev;
|
std::filesystem::path dev;
|
||||||
string name;
|
string name;
|
||||||
string fstype = "";
|
string fstype{}; // defaults to ""
|
||||||
std::filesystem::path stat = "";
|
std::filesystem::path stat{}; // defaults to ""
|
||||||
int64_t total = 0, used = 0, free = 0;
|
int64_t total{}; // defaults to 0
|
||||||
int used_percent = 0, free_percent = 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
|
||||||
|
|
||||||
array<int64_t, 3> old_io = {0, 0, 0};
|
array<int64_t, 3> old_io = {0, 0, 0};
|
||||||
deque<long long> io_read = {};
|
deque<long long> io_read = {};
|
||||||
deque<long long> io_write = {};
|
deque<long long> io_write = {};
|
||||||
@ -156,10 +254,10 @@ namespace Mem {
|
|||||||
uint64_t get_totalMem();
|
uint64_t get_totalMem();
|
||||||
|
|
||||||
//* Collect mem & disks stats
|
//* Collect mem & disks stats
|
||||||
auto collect(const bool no_update=false) -> mem_info&;
|
auto collect(bool no_update = false) -> mem_info&;
|
||||||
|
|
||||||
//* Draw contents of mem box using <mem> as source
|
//* Draw contents of mem box using <mem> as source
|
||||||
string draw(const mem_info& mem, const bool force_redraw=false, const bool data_same=false);
|
string draw(const mem_info& mem, bool force_redraw = false, bool data_same = false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,23 +271,29 @@ namespace Net {
|
|||||||
extern unordered_flat_map<string, uint64_t> graph_max;
|
extern unordered_flat_map<string, uint64_t> graph_max;
|
||||||
|
|
||||||
struct net_stat {
|
struct net_stat {
|
||||||
uint64_t speed = 0, top = 0, total = 0, last = 0, offset = 0, rollover = 0;
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
struct net_info {
|
struct net_info {
|
||||||
unordered_flat_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
|
unordered_flat_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
|
||||||
unordered_flat_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
|
unordered_flat_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
|
||||||
string ipv4 = "", ipv6 = "";
|
string ipv4{}; // defaults to ""
|
||||||
bool connected = false;
|
string ipv6{}; // defaults to ""
|
||||||
|
bool connected{}; // defaults to false
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unordered_flat_map<string, net_info> current_net;
|
extern unordered_flat_map<string, net_info> current_net;
|
||||||
|
|
||||||
//* Collect net upload/download stats
|
//* Collect net upload/download stats
|
||||||
auto collect(const bool no_update=false) -> net_info&;
|
auto collect(bool no_update=false) -> net_info&;
|
||||||
|
|
||||||
//* Draw contents of net box using <net> as source
|
//* Draw contents of net box using <net> as source
|
||||||
string draw(const net_info& net, const bool force_redraw=false, const bool data_same=false);
|
string draw(const net_info& net, bool force_redraw = false, bool data_same = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Proc {
|
namespace Proc {
|
||||||
@ -232,25 +336,32 @@ namespace Proc {
|
|||||||
|
|
||||||
//* Container for process information
|
//* Container for process information
|
||||||
struct proc_info {
|
struct proc_info {
|
||||||
size_t pid = 0;
|
size_t pid{}; // defaults to 0
|
||||||
string name = "", cmd = "";
|
string name{}; // defaults to ""
|
||||||
string short_cmd = "";
|
string cmd{}; // defaults to ""
|
||||||
size_t threads = 0;
|
string short_cmd{}; // defaults to ""
|
||||||
int name_offset = 0;
|
size_t threads{}; // defaults to 0
|
||||||
string user = "";
|
int name_offset{}; // defaults to 0
|
||||||
uint64_t mem = 0;
|
string user{}; // defaults to ""
|
||||||
double cpu_p = 0.0, cpu_c = 0.0;
|
uint64_t mem{}; // defaults to 0
|
||||||
|
double cpu_p{}; // defaults to = 0.0
|
||||||
|
double cpu_c{}; // defaults to = 0.0
|
||||||
char state = '0';
|
char state = '0';
|
||||||
uint64_t p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
int64_t p_nice{}; // defaults to 0
|
||||||
string prefix = "";
|
uint64_t ppid{}; // defaults to 0
|
||||||
size_t depth = 0, tree_index = 0;
|
uint64_t cpu_s{}; // defaults to 0
|
||||||
bool collapsed = false, filtered = false;
|
uint64_t cpu_t{}; // defaults to 0
|
||||||
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Container for process info box
|
//* Container for process info box
|
||||||
struct detail_container {
|
struct detail_container {
|
||||||
size_t last_pid = 0;
|
size_t last_pid{}; // defaults to 0
|
||||||
bool skip_smaps = false;
|
bool skip_smaps{}; // defaults to false
|
||||||
proc_info entry;
|
proc_info entry;
|
||||||
string elapsed, parent, status, io_read, io_write, memory;
|
string elapsed, parent, status, io_read, io_write, memory;
|
||||||
long long first_mem = -1;
|
long long first_mem = -1;
|
||||||
@ -262,13 +373,13 @@ namespace Proc {
|
|||||||
extern detail_container detailed;
|
extern detail_container detailed;
|
||||||
|
|
||||||
//* Collect and sort process information from /proc
|
//* Collect and sort process information from /proc
|
||||||
auto collect(const bool no_update=false) -> vector<proc_info>&;
|
auto collect(bool no_update = false) -> vector<proc_info>&;
|
||||||
|
|
||||||
//* Update current selection and view, returns -1 if no change otherwise the current selection
|
//* Update current selection and view, returns -1 if no change otherwise the current selection
|
||||||
int selection(const string& cmd_key);
|
int selection(const string& cmd_key);
|
||||||
|
|
||||||
//* Draw contents of proc box using <plist> as data source
|
//* Draw contents of proc box using <plist> as data source
|
||||||
string draw(const vector<proc_info>& plist, const bool force_redraw=false, const bool data_same=false);
|
string draw(const vector<proc_info>& plist, bool force_redraw = false, bool data_same = false);
|
||||||
|
|
||||||
struct tree_proc {
|
struct tree_proc {
|
||||||
std::reference_wrapper<proc_info> entry;
|
std::reference_wrapper<proc_info> entry;
|
||||||
@ -276,11 +387,14 @@ namespace Proc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//* Sort vector of proc_info's
|
//* Sort vector of proc_info's
|
||||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, const bool reverse, const bool tree = false);
|
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree = false);
|
||||||
|
|
||||||
//* Recursive sort of process tree
|
//* Recursive sort of process tree
|
||||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, const bool reverse, int& c_index, const int index_max, const bool collapsed = false);
|
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting,
|
||||||
|
bool reverse, int& c_index, const int index_max, bool collapsed = false);
|
||||||
|
|
||||||
//* Generate process tree list
|
//* Generate process tree list
|
||||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false, const bool no_update=false, const bool should_filter=false);
|
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||||
|
int cur_depth, bool collapsed, const string& filter,
|
||||||
|
bool found = false, bool no_update = false, bool should_filter = false);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -17,20 +17,21 @@ tab-size = 4
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
#include <ranges>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <btop_tools.hpp>
|
#include "btop_tools.hpp"
|
||||||
#include <btop_config.hpp>
|
#include "btop_config.hpp"
|
||||||
#include <btop_theme.hpp>
|
#include "btop_theme.hpp"
|
||||||
|
|
||||||
|
using std::round;
|
||||||
|
using std::stoi;
|
||||||
|
using std::to_string;
|
||||||
|
using std::vector;
|
||||||
|
using std::views::iota;
|
||||||
|
|
||||||
using std::round, std::vector, std::stoi, std::views::iota,
|
|
||||||
std::clamp, std::max, std::min, std::ceil, std::to_string;
|
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
namespace rng = std::ranges;
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
string Term::fg, Term::bg;
|
string Term::fg, Term::bg;
|
||||||
@ -149,12 +150,14 @@ namespace Theme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string hex_to_color(string hexa, const bool& t_to_256, const string& depth) {
|
string hex_to_color(string hexa, bool t_to_256, const string& depth) {
|
||||||
if (hexa.size() > 1) {
|
if (hexa.size() > 1) {
|
||||||
hexa.erase(0, 1);
|
hexa.erase(0, 1);
|
||||||
for (auto& c : hexa) if (not isxdigit(c)) {
|
for (auto& c : hexa) {
|
||||||
Logger::error("Invalid hex value: " + hexa);
|
if (not isxdigit(c)) {
|
||||||
return "";
|
Logger::error("Invalid hex value: " + hexa);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
||||||
|
|
||||||
@ -186,7 +189,7 @@ namespace Theme {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
string dec_to_color(int r, int g, int b, const bool& t_to_256, const string& depth) {
|
string dec_to_color(int r, int g, int b, bool t_to_256, const string& depth) {
|
||||||
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
||||||
r = std::clamp(r, 0, 255);
|
r = std::clamp(r, 0, 255);
|
||||||
g = std::clamp(g, 0, 255);
|
g = std::clamp(g, 0, 255);
|
||||||
@ -200,14 +203,17 @@ namespace Theme {
|
|||||||
array<int, 3> hex_to_dec(string hexa) {
|
array<int, 3> hex_to_dec(string hexa) {
|
||||||
if (hexa.size() > 1) {
|
if (hexa.size() > 1) {
|
||||||
hexa.erase(0, 1);
|
hexa.erase(0, 1);
|
||||||
for (auto& c : hexa) if (not isxdigit(c)) return array<int, 3>{-1, -1, -1};
|
for (auto& c : hexa) {
|
||||||
|
if (not isxdigit(c))
|
||||||
|
return array{-1, -1, -1};
|
||||||
|
}
|
||||||
|
|
||||||
if (hexa.size() == 2) {
|
if (hexa.size() == 2) {
|
||||||
int h_int = stoi(hexa, nullptr, 16);
|
int h_int = stoi(hexa, nullptr, 16);
|
||||||
return array<int, 3>{h_int, h_int, h_int};
|
return array{h_int, h_int, h_int};
|
||||||
}
|
}
|
||||||
else if (hexa.size() == 6) {
|
else if (hexa.size() == 6) {
|
||||||
return array<int, 3>{
|
return array{
|
||||||
stoi(hexa.substr(0, 2), nullptr, 16),
|
stoi(hexa.substr(0, 2), nullptr, 16),
|
||||||
stoi(hexa.substr(2, 2), nullptr, 16),
|
stoi(hexa.substr(2, 2), nullptr, 16),
|
||||||
stoi(hexa.substr(4, 2), nullptr, 16)
|
stoi(hexa.substr(4, 2), nullptr, 16)
|
||||||
@ -221,7 +227,7 @@ namespace Theme {
|
|||||||
void generateColors(const unordered_flat_map<string, string>& source) {
|
void generateColors(const unordered_flat_map<string, string>& source) {
|
||||||
vector<string> t_rgb;
|
vector<string> t_rgb;
|
||||||
string depth;
|
string depth;
|
||||||
const bool& t_to_256 = Config::getB("lowcolor");
|
bool t_to_256 = Config::getB("lowcolor");
|
||||||
colors.clear(); rgbs.clear();
|
colors.clear(); rgbs.clear();
|
||||||
for (const auto& [name, color] : Default_theme) {
|
for (const auto& [name, color] : Default_theme) {
|
||||||
if (name == "main_bg" and not Config::getB("theme_background")) {
|
if (name == "main_bg" and not Config::getB("theme_background")) {
|
||||||
@ -247,11 +253,11 @@ namespace Theme {
|
|||||||
}
|
}
|
||||||
else if (not source.at(name).empty()) {
|
else if (not source.at(name).empty()) {
|
||||||
t_rgb = ssplit(source.at(name));
|
t_rgb = ssplit(source.at(name));
|
||||||
if (t_rgb.size() != 3)
|
if (t_rgb.size() != 3) {
|
||||||
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
||||||
else {
|
} else {
|
||||||
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
|
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
|
||||||
rgbs[name] = array<int, 3>{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
rgbs[name] = array{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,15 +290,16 @@ namespace Theme {
|
|||||||
//* Generate color gradients from two or three colors, 101 values indexed 0-100
|
//* Generate color gradients from two or three colors, 101 values indexed 0-100
|
||||||
void generateGradients() {
|
void generateGradients() {
|
||||||
gradients.clear();
|
gradients.clear();
|
||||||
const bool& t_to_256 = Config::getB("lowcolor");
|
bool t_to_256 = Config::getB("lowcolor");
|
||||||
|
|
||||||
//? Insert values for processes greyscale gradient and processes color gradient
|
//? Insert values for processes greyscale gradient and processes color gradient
|
||||||
rgbs.insert({ { "proc_start", rgbs["main_fg"] },
|
rgbs.insert({
|
||||||
{ "proc_mid", {-1, -1, -1} },
|
{ "proc_start", rgbs["main_fg"] },
|
||||||
{ "proc_end", rgbs["inactive_fg"] },
|
{ "proc_mid", {-1, -1, -1} },
|
||||||
{ "proc_color_start", rgbs["inactive_fg"] },
|
{ "proc_end", rgbs["inactive_fg"] },
|
||||||
{ "proc_color_mid", {-1, -1, -1} },
|
{ "proc_color_start", rgbs["inactive_fg"] },
|
||||||
{ "proc_color_end", rgbs["process_start"] },
|
{ "proc_color_mid", {-1, -1, -1} },
|
||||||
|
{ "proc_color_end", rgbs["process_start"] },
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const auto& [name, source_arr] : rgbs) {
|
for (const auto& [name, source_arr] : rgbs) {
|
||||||
@ -315,10 +322,10 @@ namespace Theme {
|
|||||||
|
|
||||||
//? Split iteration in two passes of 50 + 51 instead of one pass of 101 if gradient has start, mid and end values defined
|
//? Split iteration in two passes of 50 + 51 instead of one pass of 101 if gradient has start, mid and end values defined
|
||||||
int current_range = (input_colors[1][0] >= 0) ? 50 : 100;
|
int current_range = (input_colors[1][0] >= 0) ? 50 : 100;
|
||||||
for (const int& rgb : iota(0, 3)) {
|
for (int rgb : iota(0, 3)) {
|
||||||
int start = 0, offset = 0;
|
int start = 0, offset = 0;
|
||||||
int end = (current_range == 50) ? 1 : 2;
|
int end = (current_range == 50) ? 1 : 2;
|
||||||
for (const int& i : iota(0, 101)) {
|
for (int i : iota(0, 101)) {
|
||||||
output_colors[i][rgb] = input_colors[start][rgb] + (i - offset) * (input_colors[end][rgb] - input_colors[start][rgb]) / current_range;
|
output_colors[i][rgb] = input_colors[start][rgb] + (i - offset) * (input_colors[end][rgb] - input_colors[start][rgb]) / current_range;
|
||||||
|
|
||||||
//? Switch source arrays from start->mid to mid->end at 50 passes if mid is defined
|
//? Switch source arrays from start->mid to mid->end at 50 passes if mid is defined
|
||||||
@ -353,7 +360,7 @@ namespace Theme {
|
|||||||
const string base_name = rtrim(c.first, "_start");
|
const string base_name = rtrim(c.first, "_start");
|
||||||
string section = "_start";
|
string section = "_start";
|
||||||
int split = colors.at(base_name + "_mid").empty() ? 50 : 33;
|
int split = colors.at(base_name + "_mid").empty() ? 50 : 33;
|
||||||
for (const int& i : iota(0, 101)) {
|
for (int i : iota(0, 101)) {
|
||||||
gradients[base_name][i] = colors.at(base_name + section);
|
gradients[base_name][i] = colors.at(base_name + section);
|
||||||
if (i == split) {
|
if (i == split) {
|
||||||
section = (split == 33) ? "_mid" : "_end";
|
section = (split == 33) ? "_mid" : "_end";
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -18,12 +18,16 @@ tab-size = 4
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <robin_hood.h>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <robin_hood.h>
|
||||||
|
|
||||||
using std::string, robin_hood::unordered_flat_map, std::array;
|
using std::array;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using robin_hood::unordered_flat_map;
|
||||||
|
|
||||||
namespace Theme {
|
namespace Theme {
|
||||||
extern std::filesystem::path theme_dir;
|
extern std::filesystem::path theme_dir;
|
||||||
@ -36,13 +40,13 @@ namespace Theme {
|
|||||||
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
|
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
|
||||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||||
string hex_to_color(string hexa, const bool& t_to_256=false, const string& depth="fg");
|
string hex_to_color(string hexa, bool t_to_256=false, const string& depth="fg");
|
||||||
|
|
||||||
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
||||||
//* Args r: [0-255], g: [0-255], b: [0-255]
|
//* Args r: [0-255], g: [0-255], b: [0-255]
|
||||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||||
string dec_to_color(int r, int g, int b, const bool& t_to_256=false, const string& depth="fg");
|
string dec_to_color(int r, int g, int b, bool t_to_256=false, const string& depth="fg");
|
||||||
|
|
||||||
//* Update list of paths for available themes
|
//* Update list of paths for available themes
|
||||||
void updateThemes();
|
void updateThemes();
|
||||||
|
@ -17,6 +17,7 @@ tab-size = 4
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <codecvt>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@ -24,18 +25,28 @@ tab-size = 4
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <robin_hood.h>
|
|
||||||
#include <widechar_width.hpp>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <btop_shared.hpp>
|
#include "robin_hood.h"
|
||||||
#include <btop_tools.hpp>
|
#include "widechar_width.hpp"
|
||||||
#include <btop_config.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
|
||||||
|
|
||||||
using std::string_view, std::max, std::floor, std::to_string, std::cin, std::cout, std::flush, robin_hood::unordered_flat_map;
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
|
||||||
@ -44,9 +55,9 @@ namespace rng = std::ranges;
|
|||||||
//* Collection of escape codes and functions for terminal manipulation
|
//* Collection of escape codes and functions for terminal manipulation
|
||||||
namespace Term {
|
namespace Term {
|
||||||
|
|
||||||
atomic<bool> initialized = false;
|
atomic<bool> initialized{}; // defaults to false
|
||||||
atomic<int> width = 0;
|
atomic<int> width{}; // defaults to 0
|
||||||
atomic<int> height = 0;
|
atomic<int> height{}; // defaults to 0
|
||||||
string current_tty;
|
string current_tty;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -69,7 +80,7 @@ namespace Term {
|
|||||||
else settings.c_lflag &= ~(ICANON);
|
else settings.c_lflag &= ~(ICANON);
|
||||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
||||||
if (on) setlinebuf(stdin);
|
if (on) setlinebuf(stdin);
|
||||||
else setbuf(stdin, NULL);
|
else setbuf(stdin, nullptr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,19 +99,31 @@ namespace Term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto get_min_size(const string& boxes) -> array<int, 2> {
|
auto get_min_size(const string& boxes) -> array<int, 2> {
|
||||||
const bool cpu = boxes.find("cpu") != string::npos;
|
bool cpu = boxes.find("cpu") != string::npos;
|
||||||
const bool mem = boxes.find("mem") != string::npos;
|
bool mem = boxes.find("mem") != string::npos;
|
||||||
const bool net = boxes.find("net") != string::npos;
|
bool net = boxes.find("net") != string::npos;
|
||||||
const bool proc = boxes.find("proc") != string::npos;
|
bool proc = boxes.find("proc") != string::npos;
|
||||||
int width = 0;
|
#ifdef GPU_SUPPORT
|
||||||
|
int gpu = 0;
|
||||||
|
if (not Gpu::gpu_names.empty())
|
||||||
|
for (char i = '0'; i <= '5'; ++i)
|
||||||
|
gpu += (boxes.find(std::string("gpu") + i) != string::npos);
|
||||||
|
#endif
|
||||||
|
int width = 0;
|
||||||
if (mem) width = Mem::min_width;
|
if (mem) width = Mem::min_width;
|
||||||
else if (net) width = Mem::min_width;
|
else if (net) width = Mem::min_width;
|
||||||
width += (proc ? Proc::min_width : 0);
|
width += (proc ? Proc::min_width : 0);
|
||||||
if (cpu and width < Cpu::min_width) width = Cpu::min_width;
|
if (cpu and width < Cpu::min_width) width = Cpu::min_width;
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
if (gpu != 0 and width < Gpu::min_width) width = Gpu::min_width;
|
||||||
|
#endif
|
||||||
|
|
||||||
int height = (cpu ? Cpu::min_height : 0);
|
int height = (cpu ? Cpu::min_height : 0);
|
||||||
if (proc) height += Proc::min_height;
|
if (proc) height += Proc::min_height;
|
||||||
else height += (mem ? Mem::min_height : 0) + (net ? Net::min_height : 0);
|
else height += (mem ? Mem::min_height : 0) + (net ? Net::min_height : 0);
|
||||||
|
#ifdef GPU_SUPPORT
|
||||||
|
height += Gpu::min_height*gpu;
|
||||||
|
#endif
|
||||||
|
|
||||||
return { width, height };
|
return { width, height };
|
||||||
}
|
}
|
||||||
@ -110,15 +133,15 @@ namespace Term {
|
|||||||
initialized = (bool)isatty(STDIN_FILENO);
|
initialized = (bool)isatty(STDIN_FILENO);
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
tcgetattr(STDIN_FILENO, &initial_settings);
|
tcgetattr(STDIN_FILENO, &initial_settings);
|
||||||
current_tty = (ttyname(STDIN_FILENO) != NULL ? (string)ttyname(STDIN_FILENO) : "unknown");
|
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
|
||||||
|
|
||||||
//? Disable stream sync
|
//? Disable stream sync
|
||||||
cin.sync_with_stdio(false);
|
cin.sync_with_stdio(false);
|
||||||
cout.sync_with_stdio(false);
|
cout.sync_with_stdio(false);
|
||||||
|
|
||||||
//? Disable stream ties
|
//? Disable stream ties
|
||||||
cin.tie(NULL);
|
cin.tie(nullptr);
|
||||||
cout.tie(NULL);
|
cout.tie(nullptr);
|
||||||
echo(false);
|
echo(false);
|
||||||
linebuffered(false);
|
linebuffered(false);
|
||||||
refresh();
|
refresh();
|
||||||
@ -141,7 +164,7 @@ namespace Term {
|
|||||||
|
|
||||||
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
|
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
|
||||||
|
|
||||||
// ! Dsiabled due to issue when compiling with musl, reverted back to using regex
|
// ! Disabled due to issue when compiling with musl, reverted back to using regex
|
||||||
// namespace Fx {
|
// namespace Fx {
|
||||||
// string uncolor(const string& s) {
|
// string uncolor(const string& s) {
|
||||||
// string out = s;
|
// string out = s;
|
||||||
@ -195,8 +218,10 @@ namespace Tools {
|
|||||||
return chars;
|
return chars;
|
||||||
}
|
}
|
||||||
|
|
||||||
string uresize(string str, const size_t len, const bool wide) {
|
string uresize(string str, const size_t len, bool wide) {
|
||||||
if (len < 1 or str.empty()) return "";
|
if (len < 1 or str.empty())
|
||||||
|
return "";
|
||||||
|
|
||||||
if (wide) {
|
if (wide) {
|
||||||
try {
|
try {
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
|
||||||
@ -223,8 +248,10 @@ namespace Tools {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
string luresize(string str, const size_t len, const bool wide) {
|
string luresize(string str, const size_t len, bool wide) {
|
||||||
if (len < 1 or str.empty()) return "";
|
if (len < 1 or str.empty())
|
||||||
|
return "";
|
||||||
|
|
||||||
for (size_t x = 0, last_pos = 0, i = str.size() - 1; i > 0 ; i--) {
|
for (size_t x = 0, last_pos = 0, i = str.size() - 1; i > 0 ; i--) {
|
||||||
if (wide and static_cast<unsigned char>(str.at(i)) > 0xef) {
|
if (wide and static_cast<unsigned char>(str.at(i)) > 0xef) {
|
||||||
x += 2;
|
x += 2;
|
||||||
@ -252,63 +279,82 @@ namespace Tools {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string ltrim(const string& str, const string& t_str) {
|
string ltrim(const string& str, const string& t_str) {
|
||||||
string_view str_v = str;
|
std::string_view str_v{str};
|
||||||
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
|
while (str_v.starts_with(t_str))
|
||||||
return (string)str_v;
|
str_v.remove_prefix(t_str.size());
|
||||||
|
|
||||||
|
return string{str_v};
|
||||||
}
|
}
|
||||||
|
|
||||||
string rtrim(const string& str, const string& t_str) {
|
string rtrim(const string& str, const string& t_str) {
|
||||||
string_view str_v = str;
|
std::string_view str_v{str};
|
||||||
while (str_v.ends_with(t_str)) str_v.remove_suffix(t_str.size());
|
while (str_v.ends_with(t_str))
|
||||||
return (string)str_v;
|
str_v.remove_suffix(t_str.size());
|
||||||
|
|
||||||
|
return string{str_v};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ssplit(const string& str, const char& delim) -> vector<string> {
|
auto ssplit(const string& str, const char& delim) -> vector<string> {
|
||||||
vector<string> out;
|
vector<string> out;
|
||||||
for (const auto& s : str | rng::views::split(delim)
|
for (const auto& s : str | rng::views::split(delim)
|
||||||
| rng::views::transform([](auto &&rng) {
|
| rng::views::transform([](auto &&rng) {
|
||||||
return string_view(&*rng.begin(), rng::distance(rng));
|
return std::string_view(&*rng.begin(), rng::distance(rng));
|
||||||
})) {
|
})) {
|
||||||
if (not s.empty()) out.emplace_back(s);
|
if (not s.empty()) out.emplace_back(s);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ljust(string str, const size_t x, const bool utf, const bool wide, const bool limit) {
|
string ljust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||||
if (utf) {
|
if (utf) {
|
||||||
if (limit and ulen(str, wide) > x) return uresize(str, x, wide);
|
if (limit and ulen(str, wide) > x)
|
||||||
|
return uresize(str, x, wide);
|
||||||
|
|
||||||
return str + string(max((int)(x - ulen(str)), 0), ' ');
|
return str + string(max((int)(x - ulen(str)), 0), ' ');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (limit and str.size() > x) { str.resize(x); return str; }
|
if (limit and str.size() > x) {
|
||||||
|
str.resize(x);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
return str + string(max((int)(x - str.size()), 0), ' ');
|
return str + string(max((int)(x - str.size()), 0), ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string rjust(string str, const size_t x, const bool utf, const bool wide, const bool limit) {
|
string rjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||||
if (utf) {
|
if (utf) {
|
||||||
if (limit and ulen(str, wide) > x) return uresize(str, x, wide);
|
if (limit and ulen(str, wide) > x)
|
||||||
|
return uresize(str, x, wide);
|
||||||
|
|
||||||
return string(max((int)(x - ulen(str)), 0), ' ') + str;
|
return string(max((int)(x - ulen(str)), 0), ' ') + str;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (limit and str.size() > x) { str.resize(x); return str; };
|
if (limit and str.size() > x) {
|
||||||
|
str.resize(x);
|
||||||
|
return str;
|
||||||
|
};
|
||||||
return string(max((int)(x - str.size()), 0), ' ') + str;
|
return string(max((int)(x - str.size()), 0), ' ') + str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string cjust(string str, const size_t x, const bool utf, const bool wide, const bool limit) {
|
string cjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||||
if (utf) {
|
if (utf) {
|
||||||
if (limit and ulen(str, wide) > x) return uresize(str, x, wide);
|
if (limit and ulen(str, wide) > x)
|
||||||
|
return uresize(str, x, wide);
|
||||||
|
|
||||||
return string(max((int)ceil((double)(x - ulen(str)) / 2), 0), ' ') + str + string(max((int)floor((double)(x - ulen(str)) / 2), 0), ' ');
|
return string(max((int)ceil((double)(x - ulen(str)) / 2), 0), ' ') + str + string(max((int)floor((double)(x - ulen(str)) / 2), 0), ' ');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (limit and str.size() > x) { str.resize(x); return str; }
|
if (limit and str.size() > x) {
|
||||||
|
str.resize(x);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
return string(max((int)ceil((double)(x - str.size()) / 2), 0), ' ') + str + string(max((int)floor((double)(x - str.size()) / 2), 0), ' ');
|
return string(max((int)ceil((double)(x - str.size()) / 2), 0), ' ') + str + string(max((int)floor((double)(x - str.size()) / 2), 0), ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string trans(const string& str) {
|
string trans(const string& str) {
|
||||||
string_view oldstr = str;
|
std::string_view oldstr{str};
|
||||||
string newstr;
|
string newstr;
|
||||||
newstr.reserve(str.size());
|
newstr.reserve(str.size());
|
||||||
for (size_t pos; (pos = oldstr.find(' ')) != string::npos;) {
|
for (size_t pos; (pos = oldstr.find(' ')) != string::npos;) {
|
||||||
@ -318,7 +364,7 @@ namespace Tools {
|
|||||||
newstr.append(Mv::r(x));
|
newstr.append(Mv::r(x));
|
||||||
oldstr.remove_prefix(pos + x);
|
oldstr.remove_prefix(pos + x);
|
||||||
}
|
}
|
||||||
return (newstr.empty()) ? str : newstr + (string)oldstr;
|
return (newstr.empty()) ? str : newstr + string{oldstr};
|
||||||
}
|
}
|
||||||
|
|
||||||
string sec_to_dhms(size_t seconds, bool no_days, bool no_seconds) {
|
string sec_to_dhms(size_t seconds, bool no_days, bool no_seconds) {
|
||||||
@ -332,14 +378,37 @@ namespace Tools {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
string floating_humanizer(uint64_t value, const bool shorten, size_t start, const bool bit, const bool per_second) {
|
string floating_humanizer(uint64_t value, bool shorten, size_t start, bool bit, bool per_second) {
|
||||||
string out;
|
string out;
|
||||||
const size_t mult = (bit) ? 8 : 1;
|
const size_t mult = (bit) ? 8 : 1;
|
||||||
const bool mega = Config::getB("base_10_sizes");
|
bool mega = Config::getB("base_10_sizes");
|
||||||
static const array<string, 11> mebiUnits_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
|
|
||||||
static const array<string, 11> mebiUnits_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
|
// taking advantage of type deduction for array creation (since C++17)
|
||||||
static const array<string, 11> megaUnits_bit = {"bit", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb", "Bb", "Gb"};
|
// combined with string literals (operator""s)
|
||||||
static const array<string, 11> megaUnits_byte = {"Byte", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "GB"};
|
static const array mebiUnits_bit {
|
||||||
|
"bit"s, "Kib"s, "Mib"s,
|
||||||
|
"Gib"s, "Tib"s, "Pib"s,
|
||||||
|
"Eib"s, "Zib"s, "Yib"s,
|
||||||
|
"Bib"s, "GEb"s
|
||||||
|
};
|
||||||
|
static const array mebiUnits_byte {
|
||||||
|
"Byte"s, "KiB"s, "MiB"s,
|
||||||
|
"GiB"s, "TiB"s, "PiB"s,
|
||||||
|
"EiB"s, "ZiB"s, "YiB"s,
|
||||||
|
"BiB"s, "GEB"s
|
||||||
|
};
|
||||||
|
static const array megaUnits_bit {
|
||||||
|
"bit"s, "Kb"s, "Mb"s,
|
||||||
|
"Gb"s, "Tb"s, "Pb"s,
|
||||||
|
"Eb"s, "Zb"s, "Yb"s,
|
||||||
|
"Bb"s, "Gb"s
|
||||||
|
};
|
||||||
|
static const array megaUnits_byte {
|
||||||
|
"Byte"s, "KB"s, "MB"s,
|
||||||
|
"GB"s, "TB"s, "PB"s,
|
||||||
|
"EB"s, "ZB"s, "YB"s,
|
||||||
|
"BB"s, "GB"s
|
||||||
|
};
|
||||||
const auto& units = (bit) ? ( mega ? megaUnits_bit : mebiUnits_bit) : ( mega ? megaUnits_byte : mebiUnits_byte);
|
const auto& units = (bit) ? ( mega ? megaUnits_bit : mebiUnits_bit) : ( mega ? megaUnits_byte : mebiUnits_byte);
|
||||||
|
|
||||||
value *= 100 * mult;
|
value *= 100 * mult;
|
||||||
@ -366,15 +435,29 @@ namespace Tools {
|
|||||||
}
|
}
|
||||||
if (out.empty()) {
|
if (out.empty()) {
|
||||||
out = to_string(value);
|
out = to_string(value);
|
||||||
if (not mega and out.size() == 4 and start > 0) { out.pop_back(); out.insert(2, ".");}
|
if (not mega and out.size() == 4 and start > 0) {
|
||||||
else if (out.size() == 3 and start > 0) out.insert(1, ".");
|
out.pop_back();
|
||||||
else if (out.size() >= 2) out.resize(out.size() - 2);
|
out.insert(2, ".");
|
||||||
|
}
|
||||||
|
else if (out.size() == 3 and start > 0) {
|
||||||
|
out.insert(1, ".");
|
||||||
|
}
|
||||||
|
else if (out.size() >= 2) {
|
||||||
|
out.resize(out.size() - 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (shorten) {
|
if (shorten) {
|
||||||
auto f_pos = out.find('.');
|
auto f_pos = out.find('.');
|
||||||
if (f_pos == 1 and out.size() > 3) out = to_string(round(stof(out) * 10) / 10).substr(0,3);
|
if (f_pos == 1 and out.size() > 3) {
|
||||||
else if (f_pos != string::npos) out = to_string((int)round(stof(out)));
|
out = to_string(round(stod(out) * 10) / 10).substr(0,3);
|
||||||
if (out.size() > 3) { out = to_string((int)(out[0] - '0') + 1); start++;}
|
}
|
||||||
|
else if (f_pos != string::npos) {
|
||||||
|
out = to_string((int)round(stod(out)));
|
||||||
|
}
|
||||||
|
if (out.size() > 3) {
|
||||||
|
out = to_string((int)(out[0] - '0')) + ".0";
|
||||||
|
start++;
|
||||||
|
}
|
||||||
out.push_back(units[start][0]);
|
out.push_back(units[start][0]);
|
||||||
}
|
}
|
||||||
else out += " " + units[start];
|
else out += " " + units[start];
|
||||||
@ -384,11 +467,19 @@ namespace Tools {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string operator*(const string& str, int64_t n) {
|
std::string operator*(const string& str, int64_t n) {
|
||||||
if (n < 1 or str.empty()) return "";
|
if (n < 1 or str.empty()) {
|
||||||
else if(n == 1) return str;
|
return "";
|
||||||
|
}
|
||||||
|
else if (n == 1) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
string new_str;
|
string new_str;
|
||||||
new_str.reserve(str.size() * n);
|
new_str.reserve(str.size() * n);
|
||||||
for (; n > 0; n--) new_str.append(str);
|
|
||||||
|
for (; n > 0; n--)
|
||||||
|
new_str.append(str);
|
||||||
|
|
||||||
return new_str;
|
return new_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,11 +491,11 @@ namespace Tools {
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void atomic_wait(const atomic<bool>& atom, const bool old) noexcept {
|
void atomic_wait(const atomic<bool>& atom, bool old) noexcept {
|
||||||
while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
|
while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void atomic_wait_for(const atomic<bool>& atom, const bool old, const uint64_t wait_ms) noexcept {
|
void atomic_wait_for(const atomic<bool>& atom, bool old, const uint64_t wait_ms) noexcept {
|
||||||
const uint64_t start_time = time_ms();
|
const uint64_t start_time = time_ms();
|
||||||
while (atom.load(std::memory_order_relaxed) == old and (time_ms() - start_time < wait_ms)) sleep_ms(1);
|
while (atom.load(std::memory_order_relaxed) == old and (time_ms() - start_time < wait_ms)) sleep_ms(1);
|
||||||
}
|
}
|
||||||
@ -426,7 +517,7 @@ namespace Tools {
|
|||||||
for (string readstr; getline(file, readstr); out += readstr);
|
for (string readstr; getline(file, readstr); out += readstr);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Logger::error("readfile() : Exception when reading " + (string)path + " : " + e.what());
|
Logger::error("readfile() : Exception when reading " + string{path} + " : " + e.what());
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
return (out.empty() ? fallback : out);
|
return (out.empty() ? fallback : out);
|
||||||
@ -447,15 +538,83 @@ namespace Tools {
|
|||||||
string hostname() {
|
string hostname() {
|
||||||
char host[HOST_NAME_MAX];
|
char host[HOST_NAME_MAX];
|
||||||
gethostname(host, HOST_NAME_MAX);
|
gethostname(host, HOST_NAME_MAX);
|
||||||
return (string)host;
|
return string{host};
|
||||||
}
|
}
|
||||||
|
|
||||||
string username() {
|
string username() {
|
||||||
auto user = getenv("LOGNAME");
|
auto user = getenv("LOGNAME");
|
||||||
if (user == NULL or strlen(user) == 0) user = getenv("USER");
|
if (user == nullptr or strlen(user) == 0) user = getenv("USER");
|
||||||
return (user != NULL ? user : "");
|
return (user != nullptr ? user : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugTimer::DebugTimer(const string name, bool start, bool delayed_report) : name(name), delayed_report(delayed_report) {
|
||||||
|
if (start)
|
||||||
|
this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTimer::~DebugTimer() {
|
||||||
|
if (running)
|
||||||
|
this->stop(true);
|
||||||
|
this->force_report();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::start() {
|
||||||
|
if (running) return;
|
||||||
|
running = true;
|
||||||
|
start_time = time_micros();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::stop(bool report) {
|
||||||
|
if (not running) return;
|
||||||
|
running = false;
|
||||||
|
elapsed_time = time_micros() - start_time;
|
||||||
|
if (report) this->report();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::reset(bool restart) {
|
||||||
|
running = false;
|
||||||
|
start_time = 0;
|
||||||
|
elapsed_time = 0;
|
||||||
|
if (restart) this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::stop_rename_reset(const string &new_name, bool report, bool restart) {
|
||||||
|
this->stop(report);
|
||||||
|
name = new_name;
|
||||||
|
this->reset(restart);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::report() {
|
||||||
|
string report_line;
|
||||||
|
if (start_time == 0 and elapsed_time == 0)
|
||||||
|
report_line = fmt::format("DebugTimer::report() warning -> Timer [{}] has not been started!", name);
|
||||||
|
else if (running)
|
||||||
|
report_line = fmt::format(custom_locale, "Timer [{}] (running) currently at {:L} μs", name, time_micros() - start_time);
|
||||||
|
else
|
||||||
|
report_line = fmt::format(custom_locale, "Timer [{}] took {:L} μs", name, elapsed_time);
|
||||||
|
|
||||||
|
if (delayed_report)
|
||||||
|
report_buffer.emplace_back(report_line);
|
||||||
|
else
|
||||||
|
Logger::log_write(log_level, report_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugTimer::force_report() {
|
||||||
|
if (report_buffer.empty()) return;
|
||||||
|
for (const auto& line : report_buffer)
|
||||||
|
Logger::log_write(log_level, line);
|
||||||
|
report_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t DebugTimer::elapsed() {
|
||||||
|
if (running)
|
||||||
|
return time_micros() - start_time;
|
||||||
|
return elapsed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugTimer::is_running() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Logger {
|
namespace Logger {
|
||||||
@ -472,10 +631,14 @@ namespace Logger {
|
|||||||
int status = -1;
|
int status = -1;
|
||||||
public:
|
public:
|
||||||
lose_priv() {
|
lose_priv() {
|
||||||
if (geteuid() != Global::real_uid) this->status = seteuid(Global::real_uid);
|
if (geteuid() != Global::real_uid) {
|
||||||
|
this->status = seteuid(Global::real_uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
~lose_priv() {
|
~lose_priv() {
|
||||||
if (status == 0) status = seteuid(Global::set_uid);
|
if (status == 0) {
|
||||||
|
status = seteuid(Global::set_uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -483,7 +646,7 @@ namespace Logger {
|
|||||||
loglevel = v_index(log_levels, level);
|
loglevel = v_index(log_levels, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_write(const size_t level, const string& msg) {
|
void log_write(const Level level, const string& msg) {
|
||||||
if (loglevel < level or logfile.empty()) return;
|
if (loglevel < level or logfile.empty()) return;
|
||||||
atomic_lock lck(busy, true);
|
atomic_lock lck(busy, true);
|
||||||
lose_priv neutered{};
|
lose_priv neutered{};
|
||||||
@ -492,19 +655,26 @@ namespace Logger {
|
|||||||
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
||||||
auto old_log = logfile;
|
auto old_log = logfile;
|
||||||
old_log += ".1";
|
old_log += ".1";
|
||||||
if (fs::exists(old_log)) fs::remove(old_log, ec);
|
|
||||||
if (not ec) fs::rename(logfile, old_log, ec);
|
if (fs::exists(old_log))
|
||||||
|
fs::remove(old_log, ec);
|
||||||
|
|
||||||
|
if (not ec)
|
||||||
|
fs::rename(logfile, old_log, ec);
|
||||||
}
|
}
|
||||||
if (not ec) {
|
if (not ec) {
|
||||||
std::ofstream lwrite(logfile, std::ios::app);
|
std::ofstream lwrite(logfile, std::ios::app);
|
||||||
if (first) { first = false; lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";}
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";
|
||||||
|
}
|
||||||
lwrite << strf_time(tdf) << log_levels.at(level) << ": " << msg << "\n";
|
lwrite << strf_time(tdf) << log_levels.at(level) << ": " << msg << "\n";
|
||||||
}
|
}
|
||||||
else logfile.clear();
|
else logfile.clear();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
logfile.clear();
|
logfile.clear();
|
||||||
throw std::runtime_error("Exception in Logger::log_write() : " + (string)e.what());
|
throw std::runtime_error("Exception in Logger::log_write() : " + string{e.what()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,17 @@ tab-size = 4
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <algorithm> // for std::ranges::count_if
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <regex>
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <chrono>
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#ifndef HOST_NAME_MAX
|
#ifndef HOST_NAME_MAX
|
||||||
@ -37,9 +38,19 @@ tab-size = 4
|
|||||||
#define HOST_NAME_MAX 64
|
#define HOST_NAME_MAX 64
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#define FMT_HEADER_ONLY
|
||||||
|
#include "fmt/core.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "fmt/ostream.h"
|
||||||
|
#include "fmt/ranges.h"
|
||||||
|
|
||||||
using std::string, std::vector, std::atomic, std::to_string, std::tuple, std::array;
|
using std::array;
|
||||||
|
using std::atomic;
|
||||||
|
using std::string;
|
||||||
|
using std::to_string;
|
||||||
|
using std::tuple;
|
||||||
|
using std::vector;
|
||||||
|
using namespace fmt::literals;
|
||||||
|
|
||||||
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
|
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
|
||||||
|
|
||||||
@ -80,19 +91,19 @@ namespace Fx {
|
|||||||
//* Collection of escape codes and functions for cursor manipulation
|
//* Collection of escape codes and functions for cursor manipulation
|
||||||
namespace Mv {
|
namespace Mv {
|
||||||
//* Move cursor to <line>, <column>
|
//* Move cursor to <line>, <column>
|
||||||
inline string to(const int& line, const int& col) { return Fx::e + to_string(line) + ';' + to_string(col) + 'f'; }
|
inline string to(int line, int col) { return Fx::e + to_string(line) + ';' + to_string(col) + 'f'; }
|
||||||
|
|
||||||
//* Move cursor right <x> columns
|
//* Move cursor right <x> columns
|
||||||
inline string r(const int& x) { return Fx::e + to_string(x) + 'C'; }
|
inline string r(int x) { return Fx::e + to_string(x) + 'C'; }
|
||||||
|
|
||||||
//* Move cursor left <x> columns
|
//* Move cursor left <x> columns
|
||||||
inline string l(const int& x) { return Fx::e + to_string(x) + 'D'; }
|
inline string l(int x) { return Fx::e + to_string(x) + 'D'; }
|
||||||
|
|
||||||
//* Move cursor up x lines
|
//* Move cursor up x lines
|
||||||
inline string u(const int& x) { return Fx::e + to_string(x) + 'A'; }
|
inline string u(int x) { return Fx::e + to_string(x) + 'A'; }
|
||||||
|
|
||||||
//* Move cursor down x lines
|
//* Move cursor down x lines
|
||||||
inline string d(const int& x) { return Fx::e + to_string(x) + 'B'; }
|
inline string d(int x) { return Fx::e + to_string(x) + 'B'; }
|
||||||
|
|
||||||
//* Save cursor position
|
//* Save cursor position
|
||||||
const string save = Fx::e + "s";
|
const string save = Fx::e + "s";
|
||||||
@ -140,19 +151,25 @@ namespace Term {
|
|||||||
namespace Tools {
|
namespace Tools {
|
||||||
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
|
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
|
||||||
|
|
||||||
|
class MyNumPunct : public std::numpunct<char> {
|
||||||
|
protected:
|
||||||
|
virtual char do_thousands_sep() const { return '\''; }
|
||||||
|
virtual std::string do_grouping() const { return "\03"; }
|
||||||
|
};
|
||||||
|
|
||||||
size_t wide_ulen(const string& str);
|
size_t wide_ulen(const string& str);
|
||||||
size_t wide_ulen(const std::wstring& w_str);
|
size_t wide_ulen(const std::wstring& w_str);
|
||||||
|
|
||||||
//* Return number of UTF8 characters in a string (wide=true for column size needed on terminal)
|
//* Return number of UTF8 characters in a string (wide=true for column size needed on terminal)
|
||||||
inline size_t ulen(const string& str, const bool wide=false) {
|
inline size_t ulen(const string& str, bool wide = false) {
|
||||||
return (wide ? wide_ulen(str) : std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; }));
|
return (wide ? wide_ulen(str) : std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; }));
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Resize a string consisting of UTF8 characters (only reduces size)
|
//* Resize a string consisting of UTF8 characters (only reduces size)
|
||||||
string uresize(const string str, const size_t len, const bool wide=false);
|
string uresize(const string str, const size_t len, bool wide = false);
|
||||||
|
|
||||||
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
||||||
string luresize(const string str, const size_t len, const bool wide=false);
|
string luresize(const string str, const size_t len, bool wide = false);
|
||||||
|
|
||||||
//* Replace <from> in <str> with <to> and return new string
|
//* Replace <from> in <str> with <to> and return new string
|
||||||
string s_replace(const string& str, const string& from, const string& to);
|
string s_replace(const string& str, const string& from, const string& to);
|
||||||
@ -189,11 +206,11 @@ namespace Tools {
|
|||||||
|
|
||||||
//* Check if string <str> contains string <find_val>, while ignoring case
|
//* Check if string <str> contains string <find_val>, while ignoring case
|
||||||
inline bool s_contains_ic(const string& str, const string& find_val) {
|
inline bool s_contains_ic(const string& str, const string& find_val) {
|
||||||
auto it = std::search(
|
auto it = std::search(
|
||||||
str.begin(), str.end(),
|
str.begin(), str.end(),
|
||||||
find_val.begin(), find_val.end(),
|
find_val.begin(), find_val.end(),
|
||||||
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
|
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
|
||||||
);
|
);
|
||||||
return it != str.end();
|
return it != str.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,31 +271,35 @@ namespace Tools {
|
|||||||
auto ssplit(const string& str, const char& delim = ' ') -> vector<string>;
|
auto ssplit(const string& str, const char& delim = ' ') -> vector<string>;
|
||||||
|
|
||||||
//* Put current thread to sleep for <ms> milliseconds
|
//* Put current thread to sleep for <ms> milliseconds
|
||||||
inline void sleep_ms(const size_t& ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }
|
inline void sleep_ms(const size_t& ms) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||||
|
}
|
||||||
|
|
||||||
//* Put current thread to sleep for <micros> microseconds
|
//* Put current thread to sleep for <micros> microseconds
|
||||||
inline void sleep_micros(const size_t& micros) { std::this_thread::sleep_for(std::chrono::microseconds(micros)); }
|
inline void sleep_micros(const size_t& micros) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||||
|
}
|
||||||
|
|
||||||
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||||
string ljust(string str, const size_t x, const bool utf=false, const bool wide=false, const bool limit=true);
|
string ljust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||||
|
|
||||||
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||||
string rjust(string str, const size_t x, const bool utf=false, const bool wide=false, const bool limit=true);
|
string rjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||||
|
|
||||||
//* Center justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
//* Center justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||||
string cjust(string str, const size_t x, const bool utf=false, const bool wide=false, const bool limit=true);
|
string cjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||||
|
|
||||||
//* Replace whitespaces " " with escape code for move right
|
//* Replace whitespaces " " with escape code for move right
|
||||||
string trans(const string& str);
|
string trans(const string& str);
|
||||||
|
|
||||||
//* Convert seconds to format "<days>d <hours>:<minutes>:<seconds>" and return string
|
//* Convert seconds to format "<days>d <hours>:<minutes>:<seconds>" and return string
|
||||||
string sec_to_dhms(size_t seconds, bool no_days=false, bool no_seconds=false);
|
string sec_to_dhms(size_t seconds, bool no_days = false, bool no_seconds = false);
|
||||||
|
|
||||||
//* Scales up in steps of 1024 to highest positive value unit and returns string with unit suffixed
|
//* Scales up in steps of 1024 to highest positive value unit and returns string with unit suffixed
|
||||||
//* bit=True or defaults to bytes
|
//* bit=True or defaults to bytes
|
||||||
//* start=int to set 1024 multiplier starting unit
|
//* start=int to set 1024 multiplier starting unit
|
||||||
//* short=True always returns 0 decimals and shortens unit to 1 character
|
//* short=True always returns 0 decimals and shortens unit to 1 character
|
||||||
string floating_humanizer(uint64_t value, const bool shorten=false, size_t start=0, const bool bit=false, const bool per_second=false);
|
string floating_humanizer(uint64_t value, bool shorten = false, size_t start = 0, bool bit = false, bool per_second = false);
|
||||||
|
|
||||||
//* Add std::string operator * : Repeat string <str> <n> number of times
|
//* Add std::string operator * : Repeat string <str> <n> number of times
|
||||||
std::string operator*(const string& str, int64_t n);
|
std::string operator*(const string& str, int64_t n);
|
||||||
@ -301,25 +322,24 @@ namespace Tools {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept;
|
void atomic_wait(const atomic<bool>& atom, bool old = true) noexcept;
|
||||||
|
|
||||||
void atomic_wait_for(const atomic<bool>& atom, const bool old=true, const uint64_t wait_ms=0) noexcept;
|
void atomic_wait_for(const atomic<bool>& atom, bool old = true, const uint64_t wait_ms = 0) noexcept;
|
||||||
|
|
||||||
//* Sets atomic<bool> to true on construct, sets to false on destruct
|
//* Sets atomic<bool> to true on construct, sets to false on destruct
|
||||||
class atomic_lock {
|
class atomic_lock {
|
||||||
atomic<bool>& atom;
|
atomic<bool>& atom;
|
||||||
bool not_true = false;
|
bool not_true{}; // defaults to false
|
||||||
public:
|
public:
|
||||||
atomic_lock(atomic<bool>& atom, bool wait=false);
|
atomic_lock(atomic<bool>& atom, bool wait = false);
|
||||||
~atomic_lock();
|
~atomic_lock();
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Read a complete file and return as a string
|
//* Read a complete file and return as a string
|
||||||
string readfile(const std::filesystem::path& path, const string& fallback="");
|
string readfile(const std::filesystem::path& path, const string& fallback = "");
|
||||||
|
|
||||||
//* Convert a celsius value to celsius, fahrenheit, kelvin or rankin and return tuple with new value and unit.
|
//* Convert a celsius value to celsius, fahrenheit, kelvin or rankin and return tuple with new value and unit.
|
||||||
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string>;
|
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Simple logging implementation
|
//* Simple logging implementation
|
||||||
@ -333,13 +353,56 @@ namespace Logger {
|
|||||||
};
|
};
|
||||||
extern std::filesystem::path logfile;
|
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"
|
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
|
||||||
void set(const string& level);
|
void set(const string& level);
|
||||||
|
|
||||||
void log_write(const size_t level, const string& msg);
|
void log_write(const Level level, const string& msg);
|
||||||
inline void error(const string msg) { log_write(1, msg); }
|
inline void error(const string msg) { log_write(ERROR, msg); }
|
||||||
inline void warning(const string msg) { log_write(2, msg); }
|
inline void warning(const string msg) { log_write(WARNING, msg); }
|
||||||
inline void info(const string msg) { log_write(3, msg); }
|
inline void info(const string msg) { log_write(INFO, msg); }
|
||||||
inline void debug(const string msg) { log_write(4, 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
|
||||||
|
//* Usage example: Tools::DebugTimer timer(name:"myTimer", [start:true], [delayed_report:true]) // Create timer and start
|
||||||
|
//* timer.stop(); // Stop timer and report elapsed time
|
||||||
|
//* timer.stop_rename_reset("myTimer2"); // Stop timer, report elapsed time, rename timer, reset and restart
|
||||||
|
class DebugTimer {
|
||||||
|
uint64_t start_time{};
|
||||||
|
uint64_t elapsed_time{};
|
||||||
|
bool running{};
|
||||||
|
std::locale custom_locale = std::locale(std::locale::classic(), new Tools::MyNumPunct);
|
||||||
|
vector<string> report_buffer{};
|
||||||
|
public:
|
||||||
|
string name{};
|
||||||
|
bool delayed_report{};
|
||||||
|
Logger::Level log_level = Logger::DEBUG;
|
||||||
|
DebugTimer() = default;
|
||||||
|
DebugTimer(const string name, bool start = true, bool delayed_report = true);
|
||||||
|
~DebugTimer();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop(bool report = true);
|
||||||
|
void reset(bool restart = true);
|
||||||
|
//* Stops and reports (default), renames timer then resets and restarts (default)
|
||||||
|
void stop_rename_reset(const string& new_name, bool report = true, bool restart = true);
|
||||||
|
void report();
|
||||||
|
void force_report();
|
||||||
|
uint64_t elapsed();
|
||||||
|
bool is_running();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -19,13 +19,15 @@ tab-size = 4
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <libproc.h>
|
#include <libproc.h>
|
||||||
|
// man 3 getifaddrs: "BUGS: If both <net/if.h> and <ifaddrs.h> are being included, <net/if.h> must be included before <ifaddrs.h>"
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/tcp_fsm.h>
|
#include <netinet/tcp_fsm.h>
|
||||||
|
#include <netinet/in.h> // for inet_ntop stuff
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sys/_timeval.h>
|
#include <sys/_timeval.h>
|
||||||
#include <sys/endian.h>
|
#include <sys/endian.h>
|
||||||
@ -57,9 +59,9 @@ tab-size = 4
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <btop_config.hpp>
|
#include "../btop_config.hpp"
|
||||||
#include <btop_shared.hpp>
|
#include "../btop_shared.hpp"
|
||||||
#include <btop_tools.hpp>
|
#include "../btop_tools.hpp"
|
||||||
|
|
||||||
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
|
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
|
||||||
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
|
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
|
||||||
@ -72,7 +74,7 @@ using namespace Tools;
|
|||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
vector<long long> core_old_totals;
|
vector<long long> core_old_totals;
|
||||||
vector<long long> core_old_idles;
|
vector<long long> core_old_idles;
|
||||||
vector<string> available_fields = {"total"};
|
vector<string> available_fields = {"Auto", "total"};
|
||||||
vector<string> available_sensors = {"Auto"};
|
vector<string> available_sensors = {"Auto"};
|
||||||
cpu_info current_cpu;
|
cpu_info current_cpu;
|
||||||
bool got_sensors = false, cpu_temp_only = false;
|
bool got_sensors = false, cpu_temp_only = false;
|
||||||
@ -118,12 +120,12 @@ namespace Shared {
|
|||||||
//? Shared global variables init
|
//? Shared global variables init
|
||||||
int mib[2];
|
int mib[2];
|
||||||
mib[0] = CTL_HW;
|
mib[0] = CTL_HW;
|
||||||
mib[1] = HW_NCPU;
|
mib[1] = HW_NCPU;
|
||||||
int ncpu;
|
int ncpu;
|
||||||
size_t len = sizeof(ncpu);
|
size_t len = sizeof(ncpu);
|
||||||
if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
|
if (sysctl(mib, 2, &ncpu, &len, nullptr, 0) == -1) {
|
||||||
Logger::warning("Could not determine number of cores, defaulting to 1.");
|
Logger::warning("Could not determine number of cores, defaulting to 1.");
|
||||||
} else {
|
} else {
|
||||||
coreCount = ncpu;
|
coreCount = ncpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,21 +143,21 @@ namespace Shared {
|
|||||||
|
|
||||||
int64_t memsize = 0;
|
int64_t memsize = 0;
|
||||||
size_t size = sizeof(memsize);
|
size_t size = sizeof(memsize);
|
||||||
if (sysctlbyname("hw.physmem", &memsize, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.physmem", &memsize, &size, nullptr, 0) < 0) {
|
||||||
Logger::warning("Could not get memory size");
|
Logger::warning("Could not get memory size");
|
||||||
}
|
}
|
||||||
totalMem = memsize;
|
totalMem = memsize;
|
||||||
|
|
||||||
struct timeval result;
|
struct timeval result;
|
||||||
size = sizeof(result);
|
size = sizeof(result);
|
||||||
if (sysctlbyname("kern.boottime", &result, &size, NULL, 0) < 0) {
|
if (sysctlbyname("kern.boottime", &result, &size, nullptr, 0) < 0) {
|
||||||
Logger::warning("Could not get boot time");
|
Logger::warning("Could not get boot time");
|
||||||
} else {
|
} else {
|
||||||
bootTime = result.tv_sec;
|
bootTime = result.tv_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = sizeof(kfscale);
|
size = sizeof(kfscale);
|
||||||
if (sysctlbyname("kern.fscale", &kfscale, &size, NULL, 0) == -1) {
|
if (sysctlbyname("kern.fscale", &kfscale, &size, nullptr, 0) == -1) {
|
||||||
kfscale = 2048;
|
kfscale = 2048;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +183,17 @@ namespace Shared {
|
|||||||
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 Shared
|
||||||
|
|
||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
@ -192,19 +205,19 @@ namespace Cpu {
|
|||||||
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
||||||
|
|
||||||
unordered_flat_map<string, long long> cpu_old = {
|
unordered_flat_map<string, long long> cpu_old = {
|
||||||
{"totals", 0},
|
{"totals", 0},
|
||||||
{"idles", 0},
|
{"idles", 0},
|
||||||
{"user", 0},
|
{"user", 0},
|
||||||
{"nice", 0},
|
{"nice", 0},
|
||||||
{"system", 0},
|
{"system", 0},
|
||||||
{"idle", 0}
|
{"idle", 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
string get_cpuName() {
|
string get_cpuName() {
|
||||||
string name;
|
string name;
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
size_t size = sizeof(buffer);
|
size_t size = sizeof(buffer);
|
||||||
if (sysctlbyname("hw.model", &buffer, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.model", &buffer, &size, nullptr, 0) < 0) {
|
||||||
Logger::error("Failed to get CPU name");
|
Logger::error("Failed to get CPU name");
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -251,13 +264,13 @@ namespace Cpu {
|
|||||||
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
|
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
|
||||||
int32_t temp;
|
int32_t temp;
|
||||||
size_t size = sizeof(temp);
|
size_t size = sizeof(temp);
|
||||||
if (sysctlbyname("dev.cpu.0.temperature", &temp, &size, NULL, 0) < 0) {
|
if (sysctlbyname("dev.cpu.0.temperature", &temp, &size, nullptr, 0) < 0) {
|
||||||
Logger::warning("Could not get temp sensor - maybe you need to load the coretemp module");
|
Logger::warning("Could not get temp sensor - maybe you need to load the coretemp module");
|
||||||
} else {
|
} else {
|
||||||
got_sensors = true;
|
got_sensors = true;
|
||||||
int temp;
|
int temp;
|
||||||
size_t size = sizeof(temp);
|
size_t size = sizeof(temp);
|
||||||
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, NULL, 0); //asuming the max temp is same for all cores
|
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, nullptr, 0); //asuming 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...
|
temp = (temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
|
||||||
current_cpu.temp_max = temp;
|
current_cpu.temp_max = temp;
|
||||||
}
|
}
|
||||||
@ -271,7 +284,7 @@ namespace Cpu {
|
|||||||
int found = 0;
|
int found = 0;
|
||||||
bool got_package = false;
|
bool got_package = false;
|
||||||
size_t size = sizeof(p_temp);
|
size_t size = sizeof(p_temp);
|
||||||
if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &p_temp, &size, NULL, 0) >= 0) {
|
if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &p_temp, &size, nullptr, 0) >= 0) {
|
||||||
got_package = true;
|
got_package = true;
|
||||||
p_temp = (p_temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
|
p_temp = (p_temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
|
||||||
}
|
}
|
||||||
@ -279,7 +292,7 @@ namespace Cpu {
|
|||||||
size = sizeof(temp);
|
size = sizeof(temp);
|
||||||
for (int i = 0; i < Shared::coreCount; i++) {
|
for (int i = 0; i < Shared::coreCount; i++) {
|
||||||
string s = "dev.cpu." + std::to_string(i) + ".temperature";
|
string s = "dev.cpu." + std::to_string(i) + ".temperature";
|
||||||
if (sysctlbyname(s.c_str(), &temp, &size, NULL, 0) >= 0) {
|
if (sysctlbyname(s.c_str(), &temp, &size, nullptr, 0) >= 0) {
|
||||||
temp = (temp - 2732) / 10;
|
temp = (temp - 2732) / 10;
|
||||||
if (not got_package) {
|
if (not got_package) {
|
||||||
p_temp += temp;
|
p_temp += temp;
|
||||||
@ -304,7 +317,7 @@ namespace Cpu {
|
|||||||
unsigned int freq = 1;
|
unsigned int freq = 1;
|
||||||
size_t size = sizeof(freq);
|
size_t size = sizeof(freq);
|
||||||
|
|
||||||
if (sysctlbyname("dev.cpu.0.freq", &freq, &size, NULL, 0) < 0) {
|
if (sysctlbyname("dev.cpu.0.freq", &freq, &size, nullptr, 0) < 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
|
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
|
||||||
@ -360,17 +373,17 @@ namespace Cpu {
|
|||||||
uint32_t percent = -1;
|
uint32_t percent = -1;
|
||||||
size_t size = sizeof(percent);
|
size_t size = sizeof(percent);
|
||||||
string status = "discharging";
|
string status = "discharging";
|
||||||
if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.acpi.battery.life", &percent, &size, nullptr, 0) < 0) {
|
||||||
has_battery = false;
|
has_battery = false;
|
||||||
} else {
|
} else {
|
||||||
has_battery = true;
|
has_battery = true;
|
||||||
size_t size = sizeof(seconds);
|
size_t size = sizeof(seconds);
|
||||||
if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, nullptr, 0) < 0) {
|
||||||
seconds = 0;
|
seconds = 0;
|
||||||
}
|
}
|
||||||
int state;
|
int state;
|
||||||
size = sizeof(state);
|
size = sizeof(state);
|
||||||
if (sysctlbyname("hw.acpi.battery.state", &state, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.acpi.battery.state", &state, &size, nullptr, 0) < 0) {
|
||||||
status = "unknown";
|
status = "unknown";
|
||||||
} else {
|
} else {
|
||||||
if (state == 2) {
|
if (state == 2) {
|
||||||
@ -385,22 +398,18 @@ namespace Cpu {
|
|||||||
return {percent, seconds, status};
|
return {percent, seconds, status};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto collect(const bool no_update) -> cpu_info & {
|
auto collect(bool no_update) -> cpu_info & {
|
||||||
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
||||||
return current_cpu;
|
return current_cpu;
|
||||||
auto &cpu = current_cpu;
|
auto &cpu = current_cpu;
|
||||||
|
|
||||||
double avg[3];
|
if (getloadavg(cpu.load_avg.data(), cpu.load_avg.size()) < 0) {
|
||||||
|
|
||||||
if (getloadavg(avg, sizeof(avg)) < 0) {
|
|
||||||
Logger::error("failed to get load averages");
|
Logger::error("failed to get load averages");
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.load_avg = { (float)avg[0], (float)avg[1], (float)avg[2]};
|
|
||||||
|
|
||||||
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
||||||
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
||||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
|
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, nullptr, 0) == -1) {
|
||||||
Logger::error("failed to get CPU times");
|
Logger::error("failed to get CPU times");
|
||||||
}
|
}
|
||||||
long long global_totals = 0;
|
long long global_totals = 0;
|
||||||
@ -534,35 +543,37 @@ namespace Mem {
|
|||||||
|
|
||||||
// find all zpools in the system. Do this only at startup.
|
// find all zpools in the system. Do this only at startup.
|
||||||
void get_zpools() {
|
void get_zpools() {
|
||||||
|
std::regex toReplace("\\.");
|
||||||
PipeWrapper poolPipe = PipeWrapper("zpool list -H -o name", "r");
|
PipeWrapper poolPipe = PipeWrapper("zpool list -H -o name", "r");
|
||||||
|
|
||||||
while (not std::feof(poolPipe())) {
|
while (not std::feof(poolPipe())) {
|
||||||
char poolName[512];
|
char poolName[512];
|
||||||
size_t len = 512;
|
size_t len = 512;
|
||||||
if (fgets(poolName, len, poolPipe())) {
|
if (fgets(poolName, len, poolPipe())) {
|
||||||
poolName[strcspn(poolName, "\n")] = 0;
|
poolName[strcspn(poolName, "\n")] = 0;
|
||||||
Logger::debug("zpool found: " + string(poolName));
|
Logger::debug("zpool found: " + string(poolName));
|
||||||
Mem::zpools.push_back(poolName);
|
Mem::zpools.push_back(std::regex_replace(poolName, toReplace, "%25"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
|
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
|
||||||
// this bit is for 'regular' mounts
|
// this bit is for 'regular' mounts
|
||||||
static struct statinfo cur;
|
static struct statinfo cur;
|
||||||
long double etime = 0;
|
long double etime = 0;
|
||||||
uint64_t total_bytes_read;
|
uint64_t total_bytes_read;
|
||||||
uint64_t total_bytes_write;
|
uint64_t total_bytes_write;
|
||||||
|
|
||||||
static std::unique_ptr<struct devinfo, decltype(std::free)*> curDevInfo (reinterpret_cast<struct devinfo*>(std::calloc(1, sizeof(struct devinfo))), std::free);
|
static std::unique_ptr<struct devinfo, decltype(std::free)*> curDevInfo (reinterpret_cast<struct devinfo*>(std::calloc(1, sizeof(struct devinfo))), std::free);
|
||||||
cur.dinfo = curDevInfo.get();
|
cur.dinfo = curDevInfo.get();
|
||||||
|
|
||||||
if (devstat_getdevs(NULL, &cur) != -1) {
|
if (devstat_getdevs(nullptr, &cur) != -1) {
|
||||||
for (int i = 0; i < cur.dinfo->numdevs; i++) {
|
for (int i = 0; i < cur.dinfo->numdevs; i++) {
|
||||||
auto d = cur.dinfo->devices[i];
|
auto d = cur.dinfo->devices[i];
|
||||||
string devStatName = "/dev/" + string(d.device_name) + std::to_string(d.unit_number);
|
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
|
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) {
|
||||||
devstat_compute_statistics(&d, NULL, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
|
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);
|
assign_values(disk, total_bytes_read, total_bytes_write);
|
||||||
string mountpoint = mapping.at(disk.dev);
|
string mountpoint = mapping.at(disk.dev);
|
||||||
Logger::debug("dev " + devStatName + " -> " + mountpoint + " read=" + std::to_string(total_bytes_read) + " write=" + std::to_string(total_bytes_write));
|
Logger::debug("dev " + devStatName + " -> " + mountpoint + " read=" + std::to_string(total_bytes_read) + " write=" + std::to_string(total_bytes_write));
|
||||||
@ -574,18 +585,18 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this code is for ZFS mounts
|
// this code is for ZFS mounts
|
||||||
for (string poolName : Mem::zpools) {
|
for (const auto &poolName : Mem::zpools) {
|
||||||
char sysCtl[1024];
|
char sysCtl[1024];
|
||||||
snprintf(sysCtl, sizeof(sysCtl), "sysctl kstat.zfs.%s.dataset | egrep \'dataset_name|nread|nwritten\'", poolName.c_str());
|
snprintf(sysCtl, sizeof(sysCtl), "sysctl kstat.zfs.%s.dataset | egrep \'dataset_name|nread|nwritten\'", poolName.c_str());
|
||||||
PipeWrapper f = PipeWrapper(sysCtl, "r");
|
PipeWrapper f = PipeWrapper(sysCtl, "r");
|
||||||
if (f()) {
|
if (f()) {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
size_t len = 512;
|
size_t len = 512;
|
||||||
|
uint64_t nread = 0, nwritten = 0;
|
||||||
while (not std::feof(f())) {
|
while (not std::feof(f())) {
|
||||||
uint64_t nread = 0, nwritten = 0;
|
|
||||||
if (fgets(buf, len, f())) {
|
if (fgets(buf, len, f())) {
|
||||||
char *name = std::strtok(buf, ": \n");
|
char *name = std::strtok(buf, ": \n");
|
||||||
char *value = std::strtok(NULL, ": \n");
|
char *value = std::strtok(nullptr, ": \n");
|
||||||
if (string(name).find("dataset_name") != string::npos) {
|
if (string(name).find("dataset_name") != string::npos) {
|
||||||
// create entry if datasetname matches with anything in mapping
|
// create entry if datasetname matches with anything in mapping
|
||||||
// relies on the fact that the dataset name is last value in the list
|
// relies on the fact that the dataset name is last value in the list
|
||||||
@ -608,17 +619,17 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto collect(const bool no_update) -> mem_info & {
|
auto collect(bool no_update) -> mem_info & {
|
||||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
||||||
return current_mem;
|
return current_mem;
|
||||||
|
|
||||||
auto &show_swap = Config::getB("show_swap");
|
auto show_swap = Config::getB("show_swap");
|
||||||
auto &show_disks = Config::getB("show_disks");
|
auto show_disks = Config::getB("show_disks");
|
||||||
auto &swap_disk = Config::getB("swap_disk");
|
auto swap_disk = Config::getB("swap_disk");
|
||||||
auto &mem = current_mem;
|
auto &mem = current_mem;
|
||||||
static const bool snapped = (getenv("BTOP_SNAPPED") != NULL);
|
static bool snapped = (getenv("BTOP_SNAPPED") != nullptr);
|
||||||
|
|
||||||
int mib[4];
|
int mib[4];
|
||||||
u_int memActive, memWire, cachedMem, freeMem;
|
u_int memActive, memWire, cachedMem, freeMem;
|
||||||
@ -626,12 +637,12 @@ namespace Mem {
|
|||||||
|
|
||||||
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", mib, &len);
|
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", mib, &len);
|
||||||
len = sizeof(memActive);
|
len = sizeof(memActive);
|
||||||
sysctl(mib, 4, &(memActive), &len, NULL, 0);
|
sysctl(mib, 4, &(memActive), &len, nullptr, 0);
|
||||||
memActive *= Shared::pageSize;
|
memActive *= Shared::pageSize;
|
||||||
|
|
||||||
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", mib, &len);
|
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", mib, &len);
|
||||||
len = sizeof(memWire);
|
len = sizeof(memWire);
|
||||||
sysctl(mib, 4, &(memWire), &len, NULL, 0);
|
sysctl(mib, 4, &(memWire), &len, nullptr, 0);
|
||||||
memWire *= Shared::pageSize;
|
memWire *= Shared::pageSize;
|
||||||
|
|
||||||
mem.stats.at("used") = memWire + memActive;
|
mem.stats.at("used") = memWire + memActive;
|
||||||
@ -639,16 +650,30 @@ namespace Mem {
|
|||||||
|
|
||||||
len = sizeof(cachedMem);
|
len = sizeof(cachedMem);
|
||||||
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", mib, &len);
|
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", mib, &len);
|
||||||
sysctl(mib, 4, &(cachedMem), &len, NULL, 0);
|
sysctl(mib, 4, &(cachedMem), &len, nullptr, 0);
|
||||||
cachedMem *= Shared::pageSize;
|
cachedMem *= Shared::pageSize;
|
||||||
mem.stats.at("cached") = cachedMem;
|
mem.stats.at("cached") = cachedMem;
|
||||||
|
|
||||||
len = sizeof(freeMem);
|
len = sizeof(freeMem);
|
||||||
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", mib, &len);
|
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", mib, &len);
|
||||||
sysctl(mib, 4, &(freeMem), &len, NULL, 0);
|
sysctl(mib, 4, &(freeMem), &len, nullptr, 0);
|
||||||
freeMem *= Shared::pageSize;
|
freeMem *= Shared::pageSize;
|
||||||
mem.stats.at("free") = freeMem;
|
mem.stats.at("free") = freeMem;
|
||||||
|
|
||||||
|
if (show_swap) {
|
||||||
|
char buf[_POSIX2_LINE_MAX];
|
||||||
|
Shared::kvm_openfiles_wrapper kd(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf);
|
||||||
|
struct kvm_swap swap[16];
|
||||||
|
int nswap = kvm_getswapinfo(kd(), swap, 16, 0);
|
||||||
|
int totalSwap = 0, usedSwap = 0;
|
||||||
|
for (int i = 0; i < nswap; i++) {
|
||||||
|
totalSwap += swap[i].ksw_total;
|
||||||
|
usedSwap += swap[i].ksw_used;
|
||||||
|
}
|
||||||
|
mem.stats.at("swap_total") = totalSwap * Shared::pageSize;
|
||||||
|
mem.stats.at("swap_used") = usedSwap * Shared::pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
if (show_swap and mem.stats.at("swap_total") > 0) {
|
if (show_swap and mem.stats.at("swap_total") > 0) {
|
||||||
for (const auto &name : swap_names) {
|
for (const auto &name : swap_names) {
|
||||||
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
|
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
|
||||||
@ -670,7 +695,7 @@ namespace Mem {
|
|||||||
double uptime = system_uptime();
|
double uptime = system_uptime();
|
||||||
auto &disks_filter = Config::getS("disks_filter");
|
auto &disks_filter = Config::getS("disks_filter");
|
||||||
bool filter_exclude = false;
|
bool filter_exclude = false;
|
||||||
// auto &only_physical = Config::getB("only_physical");
|
// auto only_physical = Config::getB("only_physical");
|
||||||
auto &disks = mem.disks;
|
auto &disks = mem.disks;
|
||||||
vector<string> filter;
|
vector<string> filter;
|
||||||
if (not disks_filter.empty()) {
|
if (not disks_filter.empty()) {
|
||||||
@ -688,7 +713,7 @@ namespace Mem {
|
|||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
auto fstype = string(stfs[i].f_fstypename);
|
auto fstype = string(stfs[i].f_fstypename);
|
||||||
if (fstype == "autofs" || fstype == "devfs" || fstype == "linprocfs" || fstype == "procfs" || fstype == "tmpfs" || fstype == "linsysfs" ||
|
if (fstype == "autofs" || fstype == "devfs" || fstype == "linprocfs" || fstype == "procfs" || fstype == "tmpfs" || fstype == "linsysfs" ||
|
||||||
fstype == "fdesckfs") {
|
fstype == "fdesckfs") {
|
||||||
// in memory filesystems -> not useful to show
|
// in memory filesystems -> not useful to show
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -803,11 +828,11 @@ namespace Net {
|
|||||||
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
auto collect(const bool no_update) -> net_info & {
|
auto collect(bool no_update) -> net_info & {
|
||||||
auto &net = current_net;
|
auto &net = current_net;
|
||||||
auto &config_iface = Config::getS("net_iface");
|
auto &config_iface = Config::getS("net_iface");
|
||||||
auto &net_sync = Config::getB("net_sync");
|
auto net_sync = Config::getB("net_sync");
|
||||||
auto &net_auto = Config::getB("net_auto");
|
auto net_auto = Config::getB("net_auto");
|
||||||
auto new_timestamp = time_ms();
|
auto new_timestamp = time_ms();
|
||||||
|
|
||||||
if (not no_update and errors < 3) {
|
if (not no_update and errors < 3) {
|
||||||
@ -820,45 +845,65 @@ namespace Net {
|
|||||||
return empty_net;
|
return empty_net;
|
||||||
}
|
}
|
||||||
int family = 0;
|
int family = 0;
|
||||||
char ip[NI_MAXHOST];
|
static_assert(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); // 46 >= 16, compile-time assurance.
|
||||||
|
enum { IPBUFFER_MAXSIZE = INET6_ADDRSTRLEN }; // manually using the known biggest value, guarded by the above static_assert
|
||||||
|
char ip[IPBUFFER_MAXSIZE];
|
||||||
interfaces.clear();
|
interfaces.clear();
|
||||||
string ipv4, ipv6;
|
string ipv4, ipv6;
|
||||||
|
|
||||||
//? Iteration over all items in getifaddrs() list
|
//? Iteration over all items in getifaddrs() list
|
||||||
for (auto *ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
for (auto *ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
|
||||||
if (ifa->ifa_addr == NULL) continue;
|
if (ifa->ifa_addr == nullptr) continue;
|
||||||
family = ifa->ifa_addr->sa_family;
|
family = ifa->ifa_addr->sa_family;
|
||||||
const auto &iface = ifa->ifa_name;
|
const auto &iface = ifa->ifa_name;
|
||||||
//? Get IPv4 address
|
|
||||||
if (family == AF_INET) {
|
|
||||||
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
|
||||||
net[iface].ipv4 = ip;
|
|
||||||
}
|
|
||||||
//? Get IPv6 address
|
|
||||||
// else if (family == AF_INET6) {
|
|
||||||
// if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
|
||||||
// net[iface].ipv6 = ip;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//? Update available interfaces vector and get status of interface
|
//? Update available interfaces vector and get status of interface
|
||||||
if (not v_contains(interfaces, iface)) {
|
if (not v_contains(interfaces, iface)) {
|
||||||
interfaces.push_back(iface);
|
interfaces.push_back(iface);
|
||||||
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
|
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
|
||||||
|
|
||||||
|
// An interface can have more than one IP of the same family associated with it,
|
||||||
|
// but we pick only the first one to show in the NET box.
|
||||||
|
// Note: Interfaces without any IPv4 and IPv6 set are still valid and monitorable!
|
||||||
|
net[iface].ipv4.clear();
|
||||||
|
net[iface].ipv6.clear();
|
||||||
}
|
}
|
||||||
|
//? Get IPv4 address
|
||||||
|
if (family == AF_INET) {
|
||||||
|
if (net[iface].ipv4.empty()) {
|
||||||
|
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||||
|
|
||||||
|
net[iface].ipv4 = ip;
|
||||||
|
} else {
|
||||||
|
int errsv = errno;
|
||||||
|
Logger::error("Net::collect() -> Failed to convert IPv4 to string for iface " + string(iface) + ", errno: " + strerror(errsv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//? Get IPv6 address
|
||||||
|
else if (family == AF_INET6) {
|
||||||
|
if (net[iface].ipv6.empty()) {
|
||||||
|
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||||
|
net[iface].ipv6 = ip;
|
||||||
|
} else {
|
||||||
|
int errsv = errno;
|
||||||
|
Logger::error("Net::collect() -> Failed to convert IPv6 to string for iface " + string(iface) + ", errno: " + strerror(errsv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //else, ignoring family==AF_LINK (see man 3 getifaddrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
||||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
|
||||||
size_t len;
|
size_t len;
|
||||||
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
|
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
|
||||||
Logger::error("failed getting network interfaces");
|
Logger::error("failed getting network interfaces");
|
||||||
} else {
|
} else {
|
||||||
std::unique_ptr<char[]> buf(new char[len]);
|
std::unique_ptr<char[]> buf(new char[len]);
|
||||||
if (sysctl(mib, 6, buf.get(), &len, NULL, 0) < 0) {
|
if (sysctl(mib, 6, buf.get(), &len, nullptr, 0) < 0) {
|
||||||
Logger::error("failed getting network interfaces");
|
Logger::error("failed getting network interfaces");
|
||||||
} else {
|
} else {
|
||||||
char *lim = buf.get() + len;
|
char *lim = buf.get() + len;
|
||||||
char *next = NULL;
|
char *next = nullptr;
|
||||||
for (next = buf.get(); next < lim;) {
|
for (next = buf.get(); next < lim;) {
|
||||||
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
||||||
next += ifm->ifm_msglen;
|
next += ifm->ifm_msglen;
|
||||||
@ -942,7 +987,7 @@ namespace Net {
|
|||||||
auto sorted_interfaces = interfaces;
|
auto sorted_interfaces = interfaces;
|
||||||
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
||||||
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
||||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||||
});
|
});
|
||||||
selected_iface.clear();
|
selected_iface.clear();
|
||||||
//? Try to set to a connected interface
|
//? Try to set to a connected interface
|
||||||
@ -964,9 +1009,9 @@ namespace Net {
|
|||||||
for (const auto &dir : {"download", "upload"}) {
|
for (const auto &dir : {"download", "upload"}) {
|
||||||
for (const auto &sel : {0, 1}) {
|
for (const auto &sel : {0, 1}) {
|
||||||
if (rescale or max_count[dir][sel] >= 5) {
|
if (rescale or max_count[dir][sel] >= 5) {
|
||||||
const uint64_t avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
const long long avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
||||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0) / 5
|
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||||
: net[selected_iface].stat[dir].speed);
|
: net[selected_iface].stat[dir].speed);
|
||||||
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
||||||
max_count[dir][0] = max_count[dir][1] = 0;
|
max_count[dir][0] = max_count[dir][1] = 0;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
@ -1035,7 +1080,7 @@ namespace Proc {
|
|||||||
|
|
||||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||||
struct timeval currentTime;
|
struct timeval currentTime;
|
||||||
gettimeofday(¤tTime, NULL);
|
gettimeofday(¤tTime, nullptr);
|
||||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
||||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||||
|
|
||||||
@ -1066,29 +1111,18 @@ namespace Proc {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
//* RAII wrapper for kvm_openfiles
|
|
||||||
class kvm_openfiles_wrapper {
|
|
||||||
kvm_t* kd = NULL;
|
|
||||||
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; }
|
|
||||||
};
|
|
||||||
|
|
||||||
//* Collects and sorts process information from /proc
|
//* Collects and sorts process information from /proc
|
||||||
auto collect(const bool no_update) -> vector<proc_info> & {
|
auto collect(bool no_update) -> vector<proc_info> & {
|
||||||
const auto &sorting = Config::getS("proc_sorting");
|
const auto &sorting = Config::getS("proc_sorting");
|
||||||
const auto &reverse = Config::getB("proc_reversed");
|
auto reverse = Config::getB("proc_reversed");
|
||||||
const auto &filter = Config::getS("proc_filter");
|
const auto &filter = Config::getS("proc_filter");
|
||||||
const auto &per_core = Config::getB("proc_per_core");
|
auto per_core = Config::getB("proc_per_core");
|
||||||
const auto &tree = Config::getB("proc_tree");
|
auto tree = Config::getB("proc_tree");
|
||||||
const auto &show_detailed = Config::getB("show_detailed");
|
auto show_detailed = Config::getB("show_detailed");
|
||||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||||
bool should_filter = current_filter != filter;
|
bool should_filter = current_filter != filter;
|
||||||
if (should_filter) current_filter = filter;
|
if (should_filter) current_filter = filter;
|
||||||
const bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||||
if (sorted_change) {
|
if (sorted_change) {
|
||||||
current_sort = sorting;
|
current_sort = sorting;
|
||||||
current_rev = reverse;
|
current_rev = reverse;
|
||||||
@ -1101,7 +1135,7 @@ namespace Proc {
|
|||||||
|
|
||||||
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
||||||
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
||||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
|
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, nullptr, 0) == -1) {
|
||||||
Logger::error("failed to get CPU times");
|
Logger::error("failed to get CPU times");
|
||||||
}
|
}
|
||||||
cputimes = 0;
|
cputimes = 0;
|
||||||
@ -1120,16 +1154,16 @@ namespace Proc {
|
|||||||
should_filter = true;
|
should_filter = true;
|
||||||
found.clear();
|
found.clear();
|
||||||
struct timeval currentTime;
|
struct timeval currentTime;
|
||||||
gettimeofday(¤tTime, NULL);
|
gettimeofday(¤tTime, nullptr);
|
||||||
const double timeNow = currentTime.tv_sec + (currentTime.tv_usec / 1'000'000);
|
const double timeNow = currentTime.tv_sec + (currentTime.tv_usec / 1'000'000);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
char buf[_POSIX2_LINE_MAX];
|
char buf[_POSIX2_LINE_MAX];
|
||||||
kvm_openfiles_wrapper kd(NULL, _PATH_DEVNULL, NULL, O_RDONLY, buf);
|
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);
|
const struct kinfo_proc* kprocs = kvm_getprocs(kd(), KERN_PROC_PROC, 0, &count);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
const struct kinfo_proc* kproc = &kprocs[i];
|
const struct kinfo_proc* kproc = &kprocs[i];
|
||||||
const size_t pid = (size_t)kproc->ki_pid;
|
const size_t pid = (size_t)kproc->ki_pid;
|
||||||
if (pid < 1) continue;
|
if (pid < 1) continue;
|
||||||
found.push_back(pid);
|
found.push_back(pid);
|
||||||
@ -1147,7 +1181,7 @@ namespace Proc {
|
|||||||
|
|
||||||
//? Get program name, command, username, parent pid, nice and status
|
//? Get program name, command, username, parent pid, nice and status
|
||||||
if (no_cache) {
|
if (no_cache) {
|
||||||
if (kproc->ki_comm == NULL or kproc->ki_comm == "idle"s) {
|
if (string(kproc->ki_comm) == "idle"s) {
|
||||||
current_procs.pop_back();
|
current_procs.pop_back();
|
||||||
found.pop_back();
|
found.pop_back();
|
||||||
continue;
|
continue;
|
||||||
@ -1310,8 +1344,8 @@ namespace Tools {
|
|||||||
struct timeval ts, currTime;
|
struct timeval ts, currTime;
|
||||||
std::size_t len = sizeof(ts);
|
std::size_t len = sizeof(ts);
|
||||||
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
||||||
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
|
if (sysctl(mib, 2, &ts, &len, nullptr, 0) != -1) {
|
||||||
gettimeofday(&currTime, NULL);
|
gettimeofday(&currTime, nullptr);
|
||||||
return currTime.tv_sec - ts.tv_sec;
|
return currTime.tv_sec - ts.tv_sec;
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -19,7 +19,6 @@ tab-size = 4
|
|||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <IOKit/IOKitLib.h>
|
#include <IOKit/IOKitLib.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <libproc.h>
|
#include <libproc.h>
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#include <mach/mach_host.h>
|
#include <mach/mach_host.h>
|
||||||
@ -28,7 +27,12 @@ tab-size = 4
|
|||||||
#include <mach/processor_info.h>
|
#include <mach/processor_info.h>
|
||||||
#include <mach/vm_statistics.h>
|
#include <mach/vm_statistics.h>
|
||||||
#include <mach/mach_time.h>
|
#include <mach/mach_time.h>
|
||||||
|
// BUGS
|
||||||
|
// If both <net/if.h> and <ifaddrs.h> are being included, <net/if.h> must be
|
||||||
|
// included before <ifaddrs.h>.
|
||||||
|
// from: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getifaddrs.3.html
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/tcp_fsm.h>
|
#include <netinet/tcp_fsm.h>
|
||||||
@ -37,12 +41,10 @@ tab-size = 4
|
|||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <netinet/in.h> // for inet_ntop
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <btop_config.hpp>
|
|
||||||
#include <btop_shared.hpp>
|
|
||||||
#include <btop_tools.hpp>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@ -50,6 +52,10 @@ tab-size = 4
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "../btop_config.hpp"
|
||||||
|
#include "../btop_shared.hpp"
|
||||||
|
#include "../btop_tools.hpp"
|
||||||
|
|
||||||
#include "sensors.hpp"
|
#include "sensors.hpp"
|
||||||
#include "smc.hpp"
|
#include "smc.hpp"
|
||||||
|
|
||||||
@ -64,10 +70,9 @@ using namespace Tools;
|
|||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
vector<long long> core_old_totals;
|
vector<long long> core_old_totals;
|
||||||
vector<long long> core_old_idles;
|
vector<long long> core_old_idles;
|
||||||
vector<string> available_fields = {"total"};
|
vector<string> available_fields = {"Auto", "total"};
|
||||||
vector<string> available_sensors = {"Auto"};
|
vector<string> available_sensors = {"Auto"};
|
||||||
cpu_info current_cpu;
|
cpu_info current_cpu;
|
||||||
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
|
|
||||||
bool got_sensors = false, cpu_temp_only = false;
|
bool got_sensors = false, cpu_temp_only = false;
|
||||||
int core_offset = 0;
|
int core_offset = 0;
|
||||||
|
|
||||||
@ -123,7 +128,7 @@ namespace Shared {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t physicalCoreCountSize = sizeof(physicalCoreCount);
|
size_t physicalCoreCountSize = sizeof(physicalCoreCount);
|
||||||
if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, NULL, 0) < 0) {
|
if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, nullptr, 0) < 0) {
|
||||||
Logger::error("Could not get physical core count");
|
Logger::error("Could not get physical core count");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +154,7 @@ namespace Shared {
|
|||||||
|
|
||||||
int64_t memsize = 0;
|
int64_t memsize = 0;
|
||||||
size_t size = sizeof(memsize);
|
size_t size = sizeof(memsize);
|
||||||
if (sysctlbyname("hw.memsize", &memsize, &size, NULL, 0) < 0) {
|
if (sysctlbyname("hw.memsize", &memsize, &size, nullptr, 0) < 0) {
|
||||||
Logger::warning("Could not get memory size");
|
Logger::warning("Could not get memory size");
|
||||||
}
|
}
|
||||||
totalMem = memsize;
|
totalMem = memsize;
|
||||||
@ -158,7 +163,6 @@ namespace Shared {
|
|||||||
arg_max = sysconf(_SC_ARG_MAX);
|
arg_max = sysconf(_SC_ARG_MAX);
|
||||||
|
|
||||||
//? Init for namespace Cpu
|
//? Init for namespace Cpu
|
||||||
if (not fs::exists(Cpu::freq_path) or access(Cpu::freq_path.c_str(), R_OK) == -1) Cpu::freq_path.clear();
|
|
||||||
Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
|
Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
|
||||||
Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
|
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_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
|
||||||
@ -188,19 +192,19 @@ namespace Cpu {
|
|||||||
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
||||||
|
|
||||||
unordered_flat_map<string, long long> cpu_old = {
|
unordered_flat_map<string, long long> cpu_old = {
|
||||||
{"totals", 0},
|
{"totals", 0},
|
||||||
{"idles", 0},
|
{"idles", 0},
|
||||||
{"user", 0},
|
{"user", 0},
|
||||||
{"nice", 0},
|
{"nice", 0},
|
||||||
{"system", 0},
|
{"system", 0},
|
||||||
{"idle", 0}
|
{"idle", 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
string get_cpuName() {
|
string get_cpuName() {
|
||||||
string name;
|
string name;
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
size_t size = sizeof(buffer);
|
size_t size = sizeof(buffer);
|
||||||
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, NULL, 0) < 0) {
|
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nullptr, 0) < 0) {
|
||||||
Logger::error("Failed to get CPU name");
|
Logger::error("Failed to get CPU name");
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -318,7 +322,7 @@ namespace Cpu {
|
|||||||
|
|
||||||
int mib[] = {CTL_HW, HW_CPU_FREQ};
|
int mib[] = {CTL_HW, HW_CPU_FREQ};
|
||||||
|
|
||||||
if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) {
|
if (sysctl(mib, 2, &freq, &size, nullptr, 0) < 0) {
|
||||||
// this fails on Apple Silicon macs. Apparently you're not allowed to know
|
// this fails on Apple Silicon macs. Apparently you're not allowed to know
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -437,23 +441,19 @@ namespace Cpu {
|
|||||||
return {percent, seconds, status};
|
return {percent, seconds, status};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto collect(const bool no_update) -> cpu_info & {
|
auto collect(bool no_update) -> cpu_info & {
|
||||||
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
||||||
return current_cpu;
|
return current_cpu;
|
||||||
auto &cpu = current_cpu;
|
auto &cpu = current_cpu;
|
||||||
|
|
||||||
double avg[3];
|
if (getloadavg(cpu.load_avg.data(), cpu.load_avg.size()) < 0) {
|
||||||
|
|
||||||
if (getloadavg(avg, sizeof(avg)) < 0) {
|
|
||||||
Logger::error("failed to get load averages");
|
Logger::error("failed to get load averages");
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.load_avg = { (float)avg[0], (float)avg[1], (float)avg[2]};
|
|
||||||
|
|
||||||
natural_t cpu_count;
|
natural_t cpu_count;
|
||||||
natural_t i;
|
natural_t i;
|
||||||
kern_return_t error;
|
kern_return_t error;
|
||||||
processor_cpu_load_info_data_t *cpu_load_info = NULL;
|
processor_cpu_load_info_data_t *cpu_load_info = nullptr;
|
||||||
|
|
||||||
MachProcessorInfo info{};
|
MachProcessorInfo info{};
|
||||||
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
||||||
@ -602,7 +602,7 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
/* Get the list of all drive objects. */
|
/* Get the list of all drive objects. */
|
||||||
if (IOServiceGetMatchingServices(libtop_master_port,
|
if (IOServiceGetMatchingServices(libtop_master_port,
|
||||||
IOServiceMatching("IOMediaBSDClient"), &drive_list)) {
|
IOServiceMatching("IOMediaBSDClient"), &drive_list)) {
|
||||||
Logger::error("Error in IOServiceGetMatchingServices()");
|
Logger::error("Error in IOServiceGetMatchingServices()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -662,15 +662,15 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto collect(const bool no_update) -> mem_info & {
|
auto collect(bool no_update) -> mem_info & {
|
||||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
||||||
return current_mem;
|
return current_mem;
|
||||||
|
|
||||||
auto &show_swap = Config::getB("show_swap");
|
auto show_swap = Config::getB("show_swap");
|
||||||
auto &show_disks = Config::getB("show_disks");
|
auto show_disks = Config::getB("show_disks");
|
||||||
auto &swap_disk = Config::getB("swap_disk");
|
auto swap_disk = Config::getB("swap_disk");
|
||||||
auto &mem = current_mem;
|
auto &mem = current_mem;
|
||||||
static const bool snapped = (getenv("BTOP_SNAPPED") != NULL);
|
static bool snapped = (getenv("BTOP_SNAPPED") != nullptr);
|
||||||
|
|
||||||
vm_statistics64 p;
|
vm_statistics64 p;
|
||||||
mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;
|
mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;
|
||||||
@ -685,7 +685,7 @@ namespace Mem {
|
|||||||
|
|
||||||
struct xsw_usage swap;
|
struct xsw_usage swap;
|
||||||
size_t len = sizeof(struct xsw_usage);
|
size_t len = sizeof(struct xsw_usage);
|
||||||
if (sysctl(mib, 2, &swap, &len, NULL, 0) == 0) {
|
if (sysctl(mib, 2, &swap, &len, nullptr, 0) == 0) {
|
||||||
mem.stats.at("swap_total") = swap.xsu_total;
|
mem.stats.at("swap_total") = swap.xsu_total;
|
||||||
mem.stats.at("swap_free") = swap.xsu_avail;
|
mem.stats.at("swap_free") = swap.xsu_avail;
|
||||||
mem.stats.at("swap_used") = swap.xsu_used;
|
mem.stats.at("swap_used") = swap.xsu_used;
|
||||||
@ -712,7 +712,7 @@ namespace Mem {
|
|||||||
double uptime = system_uptime();
|
double uptime = system_uptime();
|
||||||
auto &disks_filter = Config::getS("disks_filter");
|
auto &disks_filter = Config::getS("disks_filter");
|
||||||
bool filter_exclude = false;
|
bool filter_exclude = false;
|
||||||
// auto &only_physical = Config::getB("only_physical");
|
// auto only_physical = Config::getB("only_physical");
|
||||||
auto &disks = mem.disks;
|
auto &disks = mem.disks;
|
||||||
vector<string> filter;
|
vector<string> filter;
|
||||||
if (not disks_filter.empty()) {
|
if (not disks_filter.empty()) {
|
||||||
@ -842,11 +842,11 @@ namespace Net {
|
|||||||
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
auto collect(const bool no_update) -> net_info & {
|
auto collect(bool no_update) -> net_info & {
|
||||||
auto &net = current_net;
|
auto &net = current_net;
|
||||||
auto &config_iface = Config::getS("net_iface");
|
auto &config_iface = Config::getS("net_iface");
|
||||||
auto &net_sync = Config::getB("net_sync");
|
auto net_sync = Config::getB("net_sync");
|
||||||
auto &net_auto = Config::getB("net_auto");
|
auto net_auto = Config::getB("net_auto");
|
||||||
auto new_timestamp = time_ms();
|
auto new_timestamp = time_ms();
|
||||||
|
|
||||||
if (not no_update and errors < 3) {
|
if (not no_update and errors < 3) {
|
||||||
@ -859,45 +859,63 @@ namespace Net {
|
|||||||
return empty_net;
|
return empty_net;
|
||||||
}
|
}
|
||||||
int family = 0;
|
int family = 0;
|
||||||
char ip[NI_MAXHOST];
|
static_assert(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); // 46 >= 16, compile-time assurance.
|
||||||
|
enum { IPBUFFER_MAXSIZE = INET6_ADDRSTRLEN }; // manually using the known biggest value, guarded by the above static_assert
|
||||||
|
char ip[IPBUFFER_MAXSIZE];
|
||||||
interfaces.clear();
|
interfaces.clear();
|
||||||
string ipv4, ipv6;
|
string ipv4, ipv6;
|
||||||
|
|
||||||
//? Iteration over all items in getifaddrs() list
|
//? Iteration over all items in getifaddrs() list
|
||||||
for (auto *ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
for (auto *ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
|
||||||
if (ifa->ifa_addr == NULL) continue;
|
if (ifa->ifa_addr == nullptr) continue;
|
||||||
family = ifa->ifa_addr->sa_family;
|
family = ifa->ifa_addr->sa_family;
|
||||||
const auto &iface = ifa->ifa_name;
|
const auto &iface = ifa->ifa_name;
|
||||||
//? Get IPv4 address
|
|
||||||
if (family == AF_INET) {
|
|
||||||
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
|
||||||
net[iface].ipv4 = ip;
|
|
||||||
}
|
|
||||||
//? Get IPv6 address
|
|
||||||
else if (family == AF_INET6) {
|
|
||||||
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
|
||||||
net[iface].ipv6 = ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
//? Update available interfaces vector and get status of interface
|
//? Update available interfaces vector and get status of interface
|
||||||
if (not v_contains(interfaces, iface)) {
|
if (not v_contains(interfaces, iface)) {
|
||||||
interfaces.push_back(iface);
|
interfaces.push_back(iface);
|
||||||
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
|
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
|
||||||
|
// An interface can have more than one IP of the same family associated with it,
|
||||||
|
// but we pick only the first one to show in the NET box.
|
||||||
|
// Note: Interfaces without any IPv4 and IPv6 set are still valid and monitorable!
|
||||||
|
net[iface].ipv4.clear();
|
||||||
|
net[iface].ipv6.clear();
|
||||||
}
|
}
|
||||||
|
//? Get IPv4 address
|
||||||
|
if (family == AF_INET) {
|
||||||
|
if (net[iface].ipv4.empty()) {
|
||||||
|
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||||
|
net[iface].ipv4 = ip;
|
||||||
|
} else {
|
||||||
|
int errsv = errno;
|
||||||
|
Logger::error("Net::collect() -> Failed to convert IPv4 to string for iface " + string(iface) + ", errno: " + strerror(errsv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//? Get IPv6 address
|
||||||
|
else if (family == AF_INET6) {
|
||||||
|
if (net[iface].ipv6.empty()) {
|
||||||
|
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||||
|
net[iface].ipv6 = ip;
|
||||||
|
} else {
|
||||||
|
int errsv = errno;
|
||||||
|
Logger::error("Net::collect() -> Failed to convert IPv6 to string for iface " + string(iface) + ", errno: " + strerror(errsv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else, ignoring family==AF_LINK (see man 3 getifaddrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
||||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
|
||||||
size_t len;
|
size_t len;
|
||||||
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
|
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
|
||||||
Logger::error("failed getting network interfaces");
|
Logger::error("failed getting network interfaces");
|
||||||
} else {
|
} else {
|
||||||
std::unique_ptr<char[]> buf(new char[len]);
|
std::unique_ptr<char[]> buf(new char[len]);
|
||||||
if (sysctl(mib, 6, buf.get(), &len, NULL, 0) < 0) {
|
if (sysctl(mib, 6, buf.get(), &len, nullptr, 0) < 0) {
|
||||||
Logger::error("failed getting network interfaces");
|
Logger::error("failed getting network interfaces");
|
||||||
} else {
|
} else {
|
||||||
char *lim = buf.get() + len;
|
char *lim = buf.get() + len;
|
||||||
char *next = NULL;
|
char *next = nullptr;
|
||||||
for (next = buf.get(); next < lim;) {
|
for (next = buf.get(); next < lim;) {
|
||||||
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
||||||
next += ifm->ifm_msglen;
|
next += ifm->ifm_msglen;
|
||||||
@ -981,7 +999,7 @@ namespace Net {
|
|||||||
auto sorted_interfaces = interfaces;
|
auto sorted_interfaces = interfaces;
|
||||||
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
||||||
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
||||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||||
});
|
});
|
||||||
selected_iface.clear();
|
selected_iface.clear();
|
||||||
//? Try to set to a connected interface
|
//? Try to set to a connected interface
|
||||||
@ -1003,9 +1021,9 @@ namespace Net {
|
|||||||
for (const auto &dir : {"download", "upload"}) {
|
for (const auto &dir : {"download", "upload"}) {
|
||||||
for (const auto &sel : {0, 1}) {
|
for (const auto &sel : {0, 1}) {
|
||||||
if (rescale or max_count[dir][sel] >= 5) {
|
if (rescale or max_count[dir][sel] >= 5) {
|
||||||
const uint64_t avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
const long long avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
||||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0) / 5
|
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||||
: net[selected_iface].stat[dir].speed);
|
: net[selected_iface].stat[dir].speed);
|
||||||
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
||||||
max_count[dir][0] = max_count[dir][1] = 0;
|
max_count[dir][0] = max_count[dir][1] = 0;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
@ -1074,7 +1092,7 @@ namespace Proc {
|
|||||||
|
|
||||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||||
struct timeval currentTime;
|
struct timeval currentTime;
|
||||||
gettimeofday(¤tTime, NULL);
|
gettimeofday(¤tTime, nullptr);
|
||||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - (detailed.entry.cpu_s / 1'000'000));
|
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - (detailed.entry.cpu_s / 1'000'000));
|
||||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||||
|
|
||||||
@ -1106,17 +1124,17 @@ namespace Proc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//* Collects and sorts process information from /proc
|
//* Collects and sorts process information from /proc
|
||||||
auto collect(const bool no_update) -> vector<proc_info> & {
|
auto collect(bool no_update) -> vector<proc_info> & {
|
||||||
const auto &sorting = Config::getS("proc_sorting");
|
const auto &sorting = Config::getS("proc_sorting");
|
||||||
const auto &reverse = Config::getB("proc_reversed");
|
auto reverse = Config::getB("proc_reversed");
|
||||||
const auto &filter = Config::getS("proc_filter");
|
const auto &filter = Config::getS("proc_filter");
|
||||||
const auto &per_core = Config::getB("proc_per_core");
|
auto per_core = Config::getB("proc_per_core");
|
||||||
const auto &tree = Config::getB("proc_tree");
|
auto tree = Config::getB("proc_tree");
|
||||||
const auto &show_detailed = Config::getB("show_detailed");
|
auto show_detailed = Config::getB("show_detailed");
|
||||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||||
bool should_filter = current_filter != filter;
|
bool should_filter = current_filter != filter;
|
||||||
if (should_filter) current_filter = filter;
|
if (should_filter) current_filter = filter;
|
||||||
const bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||||
if (sorted_change) {
|
if (sorted_change) {
|
||||||
current_sort = sorting;
|
current_sort = sorting;
|
||||||
current_rev = reverse;
|
current_rev = reverse;
|
||||||
@ -1136,7 +1154,7 @@ namespace Proc {
|
|||||||
{ //* Get CPU totals
|
{ //* Get CPU totals
|
||||||
natural_t cpu_count;
|
natural_t cpu_count;
|
||||||
kern_return_t error;
|
kern_return_t error;
|
||||||
processor_cpu_load_info_data_t *cpu_load_info = NULL;
|
processor_cpu_load_info_data_t *cpu_load_info = nullptr;
|
||||||
MachProcessorInfo info{};
|
MachProcessorInfo info{};
|
||||||
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
||||||
if (error != KERN_SUCCESS) {
|
if (error != KERN_SUCCESS) {
|
||||||
@ -1158,13 +1176,13 @@ namespace Proc {
|
|||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
const auto timeNow = time_micros();
|
const auto timeNow = time_micros();
|
||||||
|
|
||||||
if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) {
|
if (sysctl(mib, 4, nullptr, &size, nullptr, 0) < 0 || size == 0) {
|
||||||
Logger::error("Unable to get size of kproc_infos");
|
Logger::error("Unable to get size of kproc_infos");
|
||||||
}
|
}
|
||||||
uint64_t cpu_t = 0;
|
uint64_t cpu_t = 0;
|
||||||
|
|
||||||
std::unique_ptr<kinfo_proc[]> processes(new kinfo_proc[size / sizeof(kinfo_proc)]);
|
std::unique_ptr<kinfo_proc[]> processes(new kinfo_proc[size / sizeof(kinfo_proc)]);
|
||||||
if (sysctl(mib, 4, processes.get(), &size, NULL, 0) == 0) {
|
if (sysctl(mib, 4, processes.get(), &size, nullptr, 0) == 0) {
|
||||||
size_t count = size / sizeof(struct kinfo_proc);
|
size_t count = size / sizeof(struct kinfo_proc);
|
||||||
for (size_t i = 0; i < count; i++) { //* iterate over all processes in kinfo_proc
|
for (size_t i = 0; i < count; i++) { //* iterate over all processes in kinfo_proc
|
||||||
struct kinfo_proc& kproc = processes.get()[i];
|
struct kinfo_proc& kproc = processes.get()[i];
|
||||||
@ -1195,7 +1213,7 @@ namespace Proc {
|
|||||||
std::unique_ptr<char[]> proc_chars(new char[Shared::arg_max]);
|
std::unique_ptr<char[]> proc_chars(new char[Shared::arg_max]);
|
||||||
int mib[] = {CTL_KERN, KERN_PROCARGS2, (int)pid};
|
int mib[] = {CTL_KERN, KERN_PROCARGS2, (int)pid};
|
||||||
size_t argmax = Shared::arg_max;
|
size_t argmax = Shared::arg_max;
|
||||||
if (sysctl(mib, 3, proc_chars.get(), &argmax, NULL, 0) == 0) {
|
if (sysctl(mib, 3, proc_chars.get(), &argmax, nullptr, 0) == 0) {
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
memcpy(&argc, &proc_chars.get()[0], sizeof(argc));
|
memcpy(&argc, &proc_chars.get()[0], sizeof(argc));
|
||||||
std::string_view proc_args(proc_chars.get(), argmax);
|
std::string_view proc_args(proc_chars.get(), argmax);
|
||||||
@ -1358,8 +1376,8 @@ namespace Tools {
|
|||||||
struct timeval ts, currTime;
|
struct timeval ts, currTime;
|
||||||
std::size_t len = sizeof(ts);
|
std::size_t len = sizeof(ts);
|
||||||
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
||||||
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
|
if (sysctl(mib, 2, &ts, &len, nullptr, 0) != -1) {
|
||||||
gettimeofday(&currTime, NULL);
|
gettimeofday(&currTime, nullptr);
|
||||||
return currTime.tv_sec - ts.tv_sec;
|
return currTime.tv_sec - ts.tv_sec;
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -74,8 +74,8 @@ double getValue(IOHIDServiceClientRef sc) {
|
|||||||
|
|
||||||
long long Cpu::ThermalSensors::getSensors() {
|
long long Cpu::ThermalSensors::getSensors() {
|
||||||
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
|
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
|
||||||
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
|
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
|
||||||
// can be checked by ioreg -lfx
|
// can be checked by ioreg -lfx
|
||||||
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
|
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
|
||||||
IOHIDEventSystemClientSetMatching(system, thermalSensors);
|
IOHIDEventSystemClientSetMatching(system, thermalSensors);
|
||||||
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
|
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -18,6 +18,9 @@ tab-size = 4
|
|||||||
|
|
||||||
#include "smc.hpp"
|
#include "smc.hpp"
|
||||||
|
|
||||||
|
static constexpr size_t MaxIndexCount = sizeof("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") - 1;
|
||||||
|
static constexpr const char *KeyIndexes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
static UInt32 _strtoul(char *str, int size, int base) {
|
static UInt32 _strtoul(char *str, int size, int base) {
|
||||||
UInt32 total = 0;
|
UInt32 total = 0;
|
||||||
int i;
|
int i;
|
||||||
@ -34,20 +37,18 @@ static UInt32 _strtoul(char *str, int size, int base) {
|
|||||||
|
|
||||||
static void _ultostr(char *str, UInt32 val) {
|
static void _ultostr(char *str, UInt32 val) {
|
||||||
str[0] = '\0';
|
str[0] = '\0';
|
||||||
sprintf(str, "%c%c%c%c",
|
snprintf(str, 5, "%c%c%c%c",
|
||||||
(unsigned int)val >> 24,
|
(unsigned int)val >> 24,
|
||||||
(unsigned int)val >> 16,
|
(unsigned int)val >> 16,
|
||||||
(unsigned int)val >> 8,
|
(unsigned int)val >> 8,
|
||||||
(unsigned int)val);
|
(unsigned int)val);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Cpu {
|
namespace Cpu {
|
||||||
|
|
||||||
SMCConnection::SMCConnection() {
|
SMCConnection::SMCConnection() {
|
||||||
IOMasterPort(kIOMasterPortDefault, &masterPort);
|
|
||||||
|
|
||||||
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
|
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
|
||||||
result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
|
result = IOServiceGetMatchingServices(0, matchingDictionary, &iterator);
|
||||||
if (result != kIOReturnSuccess) {
|
if (result != kIOReturnSuccess) {
|
||||||
throw std::runtime_error("failed to get AppleSMC");
|
throw std::runtime_error("failed to get AppleSMC");
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ namespace Cpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SMCConnection::~SMCConnection() {
|
SMCConnection::~SMCConnection() {
|
||||||
IOServiceClose(conn);
|
IOServiceClose(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
long long SMCConnection::getSMCTemp(char *key) {
|
long long SMCConnection::getSMCTemp(char *key) {
|
||||||
@ -92,12 +93,15 @@ namespace Cpu {
|
|||||||
long long SMCConnection::getTemp(int core) {
|
long long SMCConnection::getTemp(int core) {
|
||||||
char key[] = SMC_KEY_CPU_TEMP;
|
char key[] = SMC_KEY_CPU_TEMP;
|
||||||
if (core >= 0) {
|
if (core >= 0) {
|
||||||
snprintf(key, 5, "TC%1dc", core);
|
if ((size_t)core > MaxIndexCount) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
snprintf(key, 5, "TC%1cc", KeyIndexes[core]);
|
||||||
}
|
}
|
||||||
long long result = getSMCTemp(key);
|
long long result = getSMCTemp(key);
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
// try again with C
|
// try again with C
|
||||||
snprintf(key, 5, "TC%1dC", core);
|
snprintf(key, 5, "TC%1dC", KeyIndexes[core]);
|
||||||
result = getSMCTemp(key);
|
result = getSMCTemp(key);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -141,10 +145,10 @@ namespace Cpu {
|
|||||||
structureOutputSize = sizeof(SMCKeyData_t);
|
structureOutputSize = sizeof(SMCKeyData_t);
|
||||||
|
|
||||||
return IOConnectCallStructMethod(conn, index,
|
return IOConnectCallStructMethod(conn, index,
|
||||||
// inputStructure
|
// inputStructure
|
||||||
inputStructure, structureInputSize,
|
inputStructure, structureInputSize,
|
||||||
// ouputStructure
|
// ouputStructure
|
||||||
outputStructure, &structureOutputSize);
|
outputStructure, &structureOutputSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Cpu
|
} // namespace Cpu
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@ -104,7 +104,7 @@ namespace Cpu {
|
|||||||
long long getTemp(int core);
|
long long getTemp(int core);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
|
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
|
||||||
long long getSMCTemp(char *key);
|
long long getSMCTemp(char *key);
|
||||||
kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure);
|
kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure);
|
||||||
|
|
||||||
|
89
themes/adwaita.theme
Normal file
89
themes/adwaita.theme
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#Bashtop Adwaita theme
|
||||||
|
#by flipflop133
|
||||||
|
|
||||||
|
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
|
||||||
|
# example for white: "#FFFFFF", "#ff" or "255 255 255".
|
||||||
|
|
||||||
|
# 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]="#f6f5f4"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#2e3436"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#2e3436"
|
||||||
|
|
||||||
|
# Higlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#1a5fb4"
|
||||||
|
|
||||||
|
# Background color of selected item in processes box
|
||||||
|
theme[selected_bg]="#1c71d8"
|
||||||
|
|
||||||
|
# Foreground color of selected item in processes box
|
||||||
|
theme[selected_fg]="#ffffff"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#5e5c64"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#1a5fb4"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#2e3436"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#3d3c14"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#2e3436"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#2e3436"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#2e3436"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#1a5fb4"
|
||||||
|
theme[temp_mid]="#1a5fb4"
|
||||||
|
theme[temp_end]="#c01c28"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#1a5fb4"
|
||||||
|
theme[cpu_mid]="#1a5fb4"
|
||||||
|
theme[cpu_end]="#c01c28"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#1a5fb4"
|
||||||
|
theme[free_mid]="#1a5fb4"
|
||||||
|
theme[free_end]="#c01c28"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#1a5fb4"
|
||||||
|
theme[cached_mid]="#1a5fb4"
|
||||||
|
theme[cached_end]="#c01c28"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#1a5fb4"
|
||||||
|
theme[available_mid]="#1a5fb4"
|
||||||
|
theme[available_end]="#c01c28"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#1a5fb4"
|
||||||
|
theme[used_mid]="#1a5fb4"
|
||||||
|
theme[used_end]="#c01c28"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#1a5fb4"
|
||||||
|
theme[download_mid]="#1a5fb4"
|
||||||
|
theme[download_end]="#c01c28"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#1a5fb4"
|
||||||
|
theme[upload_mid]="#1a5fb4"
|
||||||
|
theme[upload_end]="#c01c28"
|
82
themes/elementarish.theme
Normal file
82
themes/elementarish.theme
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Theme: Elementarish
|
||||||
|
# (inspired by Elementary OS)
|
||||||
|
# By: Dennis Mayr
|
||||||
|
|
||||||
|
# Main bg
|
||||||
|
theme[main_bg]="#333333"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#eee8d5"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#eee8d5"
|
||||||
|
|
||||||
|
# Higlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#d1302c"
|
||||||
|
|
||||||
|
# Background color of selected item in processes box
|
||||||
|
theme[selected_bg]="#268ad0"
|
||||||
|
|
||||||
|
# Foreground color of selected item in processes box
|
||||||
|
theme[selected_fg]="#eee8d5"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#657b83"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#268ad0"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#657b83"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#657b83"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#657b83"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#657b83"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#657b83"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#859900"
|
||||||
|
theme[temp_mid]="#b28602"
|
||||||
|
theme[temp_end]="#d1302c"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#859900"
|
||||||
|
theme[cpu_mid]="#b28602"
|
||||||
|
theme[cpu_end]="#d1302c"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#268ad0"
|
||||||
|
theme[free_mid]="#6c71c4"
|
||||||
|
theme[free_end]="#2a9d95"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#268ad0"
|
||||||
|
theme[cached_mid]="#6c71c4"
|
||||||
|
theme[cached_end]="#d1302c"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#268ad0"
|
||||||
|
theme[available_mid]="#6c71c4"
|
||||||
|
theme[available_end]="#d1302c"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#859900"
|
||||||
|
theme[used_mid]="#b28602"
|
||||||
|
theme[used_end]="#d1302c"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#268ad0"
|
||||||
|
theme[download_mid]="#6c71c4"
|
||||||
|
theme[download_end]="#d1302c"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#268ad0"
|
||||||
|
theme[upload_mid]="#6c71c4"
|
||||||
|
theme[upload_end]="#d1302c"
|
@ -1,12 +1,10 @@
|
|||||||
# Btop everforest dark hard theme by u/archontop.
|
|
||||||
|
|
||||||
# All graphs and meters can be gradients
|
# All graphs and meters can be gradients
|
||||||
# For single color graphs leave "mid" and "end" variable empty.
|
# For single color graphs leave "mid" and "end" variable empty.
|
||||||
# Use "start" and "end" variables for two color gradient
|
# Use "start" and "end" variables for two color gradient
|
||||||
# Use "start", "mid" and "end" for three 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
|
# Main background, empty for terminal default, need to be empty if you want transparent background
|
||||||
theme[main_bg]="#2b3339"
|
theme[main_bg]="#272e33"
|
||||||
|
|
||||||
# Main text color
|
# Main text color
|
||||||
theme[main_fg]="#d3c6aa"
|
theme[main_fg]="#d3c6aa"
|
||||||
@ -18,13 +16,13 @@ theme[title]="#d3c6aa"
|
|||||||
theme[hi_fg]="#e67e80"
|
theme[hi_fg]="#e67e80"
|
||||||
|
|
||||||
# Background color of selected items
|
# Background color of selected items
|
||||||
theme[selected_bg]="#4b565c"
|
theme[selected_bg]="#374145"
|
||||||
|
|
||||||
# Foreground color of selected items
|
# Foreground color of selected items
|
||||||
theme[selected_fg]="#dbbc7f"
|
theme[selected_fg]="#dbbc7f"
|
||||||
|
|
||||||
# Color of inactive/disabled text
|
# Color of inactive/disabled text
|
||||||
theme[inactive_fg]="#2b3339"
|
theme[inactive_fg]="#272e33"
|
||||||
|
|
||||||
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
||||||
theme[graph_text]="#d3c6aa"
|
theme[graph_text]="#d3c6aa"
|
||||||
@ -33,19 +31,19 @@ theme[graph_text]="#d3c6aa"
|
|||||||
theme[proc_misc]="#a7c080"
|
theme[proc_misc]="#a7c080"
|
||||||
|
|
||||||
# Cpu box outline color
|
# Cpu box outline color
|
||||||
theme[cpu_box]="#4b565c"
|
theme[cpu_box]="#374145"
|
||||||
|
|
||||||
# Memory/disks box outline color
|
# Memory/disks box outline color
|
||||||
theme[mem_box]="#4b565c"
|
theme[mem_box]="#374145"
|
||||||
|
|
||||||
# Net up/down box outline color
|
# Net up/down box outline color
|
||||||
theme[net_box]="#4b565c"
|
theme[net_box]="#374145"
|
||||||
|
|
||||||
# Processes box outline color
|
# Processes box outline color
|
||||||
theme[proc_box]="#4b565c"
|
theme[proc_box]="#374145"
|
||||||
|
|
||||||
# Box divider line and small boxes line color
|
# Box divider line and small boxes line color
|
||||||
theme[div_line]="#4b565c"
|
theme[div_line]="#374145"
|
||||||
|
|
||||||
# Temperature graph colors
|
# Temperature graph colors
|
||||||
theme[temp_start]="#a7c080"
|
theme[temp_start]="#a7c080"
|
||||||
@ -78,14 +76,14 @@ theme[used_mid]="#dbbc7f"
|
|||||||
theme[used_end]="#f85552"
|
theme[used_end]="#f85552"
|
||||||
|
|
||||||
# Download graph colors
|
# Download graph colors
|
||||||
theme[download_start]="#8da101"
|
theme[download_start]="#a7c080"
|
||||||
theme[download_mid]="#83c092"
|
theme[download_mid]="#83c092"
|
||||||
theme[download_end]="#a7c080"
|
theme[download_end]="#7fbbb3"
|
||||||
|
|
||||||
# Upload graph colors
|
# Upload graph colors
|
||||||
theme[upload_start]="#f85552"
|
theme[upload_start]="#dbbc7f"
|
||||||
theme[upload_mid]="#dbbc7f"
|
theme[upload_mid]="#e69875"
|
||||||
theme[upload_end]="#a7c080"
|
theme[upload_end]="#e67e80"
|
||||||
|
|
||||||
# Process box color gradient for threads, mem and cpu usage
|
# Process box color gradient for threads, mem and cpu usage
|
||||||
theme[process_start]="#a7c080"
|
theme[process_start]="#a7c080"
|
||||||
|
86
themes/horizon.theme
Normal file
86
themes/horizon.theme
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# 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]="#1C1E26"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#f8f8f2"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#f8f8f2"
|
||||||
|
|
||||||
|
# Highlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#B877DB"
|
||||||
|
|
||||||
|
# Background color of selected items
|
||||||
|
theme[selected_bg]="#282b37"
|
||||||
|
|
||||||
|
# Foreground color of selected items
|
||||||
|
theme[selected_fg]="#f8f8f2"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#272e33"
|
||||||
|
|
||||||
|
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
||||||
|
theme[graph_text]="#f8f8f2"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#27D796"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#B877DB"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#27D796"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#E95678"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#25B2BC"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#272e33"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#27D796"
|
||||||
|
theme[temp_mid]="#FAC29A"
|
||||||
|
theme[temp_end]="#E95678"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#27D796"
|
||||||
|
theme[cpu_mid]="#FAC29A"
|
||||||
|
theme[cpu_end]="#E95678"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#E95678"
|
||||||
|
theme[free_mid]="#FAC29A"
|
||||||
|
theme[free_end]="#27D796"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#27D796"
|
||||||
|
theme[cached_mid]="#FAC29A"
|
||||||
|
theme[cached_end]="#E95678"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#27D796"
|
||||||
|
theme[available_mid]="#FAC29A"
|
||||||
|
theme[available_end]="#E95678"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#27D796"
|
||||||
|
theme[used_mid]="#FAC29A"
|
||||||
|
theme[used_end]="#E95678"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#27D796"
|
||||||
|
theme[download_mid]="#FAC29A"
|
||||||
|
theme[download_end]="#E95678"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#27D796"
|
||||||
|
theme[upload_mid]="#FAC29A"
|
||||||
|
theme[upload_end]="#E95678"
|
@ -2,13 +2,13 @@
|
|||||||
#by Kyli0x <kyli0x@protonmail.ch>
|
#by Kyli0x <kyli0x@protonmail.ch>
|
||||||
|
|
||||||
# Main background, empty for terminal default, need to be empty if you want transparent background
|
# Main background, empty for terminal default, need to be empty if you want transparent background
|
||||||
theme[main_bg]="#23252e"
|
theme[main_bg]="#222222"
|
||||||
|
|
||||||
# Main text color
|
# Main text color
|
||||||
theme[main_fg]="#f8f8f2"
|
theme[main_fg]="#e8f6f5"
|
||||||
|
|
||||||
# Title color for boxes
|
# Title color for boxes
|
||||||
theme[title]="#f8f8f2"
|
theme[title]="#e8f6f5"
|
||||||
|
|
||||||
# Highlight color for keyboard shortcuts
|
# Highlight color for keyboard shortcuts
|
||||||
theme[hi_fg]="#21d6c9"
|
theme[hi_fg]="#21d6c9"
|
||||||
@ -17,16 +17,16 @@ theme[hi_fg]="#21d6c9"
|
|||||||
theme[selected_bg]="#1aaba0"
|
theme[selected_bg]="#1aaba0"
|
||||||
|
|
||||||
# Foreground color of selected item in processes box
|
# Foreground color of selected item in processes box
|
||||||
theme[selected_fg]="#f8f8f2"
|
theme[selected_fg]="#e8f6f5"
|
||||||
|
|
||||||
# Color of inactive/disabled text
|
# Color of inactive/disabled text
|
||||||
theme[inactive_fg]="#497e7a"
|
theme[inactive_fg]="#5ec4bc"
|
||||||
|
|
||||||
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
||||||
theme[graph_text]="#21d6c9"
|
theme[graph_text]="#ba1a84"
|
||||||
|
|
||||||
# Background color of the percentage meters
|
# Background color of the percentage meters
|
||||||
theme[meter_bg]="#80638e"
|
theme[meter_bg]="#5ec4bc"
|
||||||
|
|
||||||
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
theme[proc_misc]="#21d6c9"
|
theme[proc_misc]="#21d6c9"
|
||||||
@ -49,44 +49,44 @@ theme[div_line]="#80638e"
|
|||||||
# Temperature graph colors
|
# Temperature graph colors
|
||||||
theme[temp_start]="#21d6c9"
|
theme[temp_start]="#21d6c9"
|
||||||
theme[temp_mid]="#1aaba0"
|
theme[temp_mid]="#1aaba0"
|
||||||
theme[temp_end]="#497e7a"
|
theme[temp_end]="#5ec4bc"
|
||||||
|
|
||||||
# CPU graph colors
|
# CPU graph colors
|
||||||
theme[cpu_start]="#21d6c9"
|
theme[cpu_start]="#21d6c9"
|
||||||
theme[cpu_mid]="#1aaba0"
|
theme[cpu_mid]="#1aaba0"
|
||||||
theme[cpu_end]="#497e7a"
|
theme[cpu_end]="#5ec4bc"
|
||||||
|
|
||||||
# Mem/Disk free meter
|
# Mem/Disk free meter
|
||||||
theme[free_start]="#21d6c9"
|
theme[free_start]="#21d6c9"
|
||||||
theme[free_mid]="#1aaba0"
|
theme[free_mid]="#1aaba0"
|
||||||
theme[free_end]="#497e7a"
|
theme[free_end]="#5ec4bc"
|
||||||
|
|
||||||
# Mem/Disk cached meter
|
# Mem/Disk cached meter
|
||||||
theme[cached_start]="#21d6c9"
|
theme[cached_start]="#21d6c9"
|
||||||
theme[cached_mid]="#1aaba0"
|
theme[cached_mid]="#1aaba0"
|
||||||
theme[cached_end]="#497e7a"
|
theme[cached_end]="#5ec4bc"
|
||||||
|
|
||||||
# Mem/Disk available meter
|
# Mem/Disk available meter
|
||||||
theme[available_start]="#21d6c9"
|
theme[available_start]="#21d6c9"
|
||||||
theme[available_mid]="#1aaba0"
|
theme[available_mid]="#1aaba0"
|
||||||
theme[available_end]="#497e7a"
|
theme[available_end]="#5ec4bc"
|
||||||
|
|
||||||
# Mem/Disk used meter
|
# Mem/Disk used meter
|
||||||
theme[used_start]="#21d6c9"
|
theme[used_start]="#21d6c9"
|
||||||
theme[used_mid]="#1aaba0"
|
theme[used_mid]="#1aaba0"
|
||||||
theme[used_end]="#497e7a"
|
theme[used_end]="#5ec4bc"
|
||||||
|
|
||||||
# Download graph colors
|
# Download graph colors
|
||||||
theme[download_start]="#21d6c9"
|
theme[download_start]="#21d6c9"
|
||||||
theme[download_mid]="#1aaba0"
|
theme[download_mid]="#1aaba0"
|
||||||
theme[download_end]="#497e7a"
|
theme[download_end]="#5ec4bc"
|
||||||
|
|
||||||
# Upload graph colors
|
# Upload graph colors
|
||||||
theme[upload_start]="#ec95ec"
|
theme[upload_start]="#ec95ec"
|
||||||
theme[upload_mid]="#1aaba0"
|
theme[upload_mid]="#1aaba0"
|
||||||
theme[upload_end]="#497e7a"
|
theme[upload_end]="#5ec4bc"
|
||||||
|
|
||||||
# Process box color gradient for threads, mem and cpu usage
|
# Process box color gradient for threads, mem and cpu usage
|
||||||
theme[process_start]="#21d6c9"
|
theme[process_start]="#21d6c9"
|
||||||
theme[process_mid]="#1aaba0"
|
theme[process_mid]="#1aaba0"
|
||||||
theme[process_end]="#d486d4"
|
theme[process_end]="#ba1a84"
|
||||||
|
83
themes/paper.theme
Normal file
83
themes/paper.theme
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Bashtop Paper theme
|
||||||
|
# c/o @s6muel
|
||||||
|
# inspired by @yorickpeterse's vim-paper theme at https://gitlab.com/yorickpeterse/vim-paper
|
||||||
|
#
|
||||||
|
|
||||||
|
# Main background, empty for terminal default, need to be empty if you want transparent background
|
||||||
|
theme[main_bg]="#F2EEDE"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#00"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#00"
|
||||||
|
|
||||||
|
# Higlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#CC3E28"
|
||||||
|
|
||||||
|
# Background color of selected item in processes box
|
||||||
|
theme[selected_bg]="#D8D5C7"
|
||||||
|
|
||||||
|
# Foreground color of selected item in processes box
|
||||||
|
theme[selected_fg]="#00"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#d8d5c7"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#00"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#00"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#00"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#00"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#00"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#00"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#55"
|
||||||
|
theme[temp_mid]="#00"
|
||||||
|
theme[temp_end]="#CC3E28"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#55"
|
||||||
|
theme[cpu_mid]="#00"
|
||||||
|
theme[cpu_end]="#CC3E28"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#216609"
|
||||||
|
theme[free_mid]=""
|
||||||
|
theme[free_end]="#216609"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#1e6fcc"
|
||||||
|
theme[cached_mid]=""
|
||||||
|
theme[cached_end]="#1e6fcc"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#216609"
|
||||||
|
theme[available_mid]=""
|
||||||
|
theme[available_end]="#216609"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#CC3E28"
|
||||||
|
theme[used_mid]=""
|
||||||
|
theme[used_end]="#CC3E28"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#55"
|
||||||
|
theme[download_mid]="#00"
|
||||||
|
theme[download_end]="#CC3E28"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#55"
|
||||||
|
theme[upload_mid]="#00"
|
||||||
|
theme[upload_end]="#CC3E28"
|
89
themes/solarized_light.theme
Normal file
89
themes/solarized_light.theme
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#solarized_light theme
|
||||||
|
#modified from solarized_dark theme
|
||||||
|
|
||||||
|
# Colors should be in 6 or 2 character hexadecimal or single spaced rgb decimal: "#RRGGBB", "#BW" or "0-255 0-255 0-255"
|
||||||
|
# example for white: "#FFFFFF", "#ff" or "255 255 255".
|
||||||
|
|
||||||
|
# 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]="#fdf6e3"
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]="#586e75"
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]="#002b36"
|
||||||
|
|
||||||
|
# Higlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]="#b58900"
|
||||||
|
|
||||||
|
# Background color of selected items
|
||||||
|
theme[selected_bg]="#eee8d5"
|
||||||
|
|
||||||
|
# Foreground color of selected items
|
||||||
|
theme[selected_fg]="#b58900"
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]="#eee8d5"
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]="#d33682"
|
||||||
|
|
||||||
|
# Cpu box outline color
|
||||||
|
theme[cpu_box]="#93a1a1"
|
||||||
|
|
||||||
|
# Memory/disks box outline color
|
||||||
|
theme[mem_box]="#93a1a1"
|
||||||
|
|
||||||
|
# Net up/down box outline color
|
||||||
|
theme[net_box]="#93a1a1"
|
||||||
|
|
||||||
|
# Processes box outline color
|
||||||
|
theme[proc_box]="#93a1a1"
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]="#93a1a1"
|
||||||
|
|
||||||
|
# Temperature graph colors
|
||||||
|
theme[temp_start]="#268bd2"
|
||||||
|
theme[temp_mid]="#ccb5f7"
|
||||||
|
theme[temp_end]="#fc5378"
|
||||||
|
|
||||||
|
# CPU graph colors
|
||||||
|
theme[cpu_start]="#adc700"
|
||||||
|
theme[cpu_mid]="#d6a200"
|
||||||
|
theme[cpu_end]="#e65317"
|
||||||
|
|
||||||
|
# Mem/Disk free meter
|
||||||
|
theme[free_start]="#4e5900"
|
||||||
|
theme[free_mid]=""
|
||||||
|
theme[free_end]="#bad600"
|
||||||
|
|
||||||
|
# Mem/Disk cached meter
|
||||||
|
theme[cached_start]="#114061"
|
||||||
|
theme[cached_mid]=""
|
||||||
|
theme[cached_end]="#268bd2"
|
||||||
|
|
||||||
|
# Mem/Disk available meter
|
||||||
|
theme[available_start]="#705500"
|
||||||
|
theme[available_mid]=""
|
||||||
|
theme[available_end]="#edb400"
|
||||||
|
|
||||||
|
# Mem/Disk used meter
|
||||||
|
theme[used_start]="#6e1718"
|
||||||
|
theme[used_mid]=""
|
||||||
|
theme[used_end]="#e02f30"
|
||||||
|
|
||||||
|
# Download graph colors
|
||||||
|
theme[download_start]="#3d4070"
|
||||||
|
theme[download_mid]="#6c71c4"
|
||||||
|
theme[download_end]="#a3a8f7"
|
||||||
|
|
||||||
|
# Upload graph colors
|
||||||
|
theme[upload_start]="#701c45"
|
||||||
|
theme[upload_mid]="#d33682"
|
||||||
|
theme[upload_end]="#f56caf"
|
Loading…
Reference in New Issue
Block a user