Merge pull request #2 from kz6fittycent/testing

Desktop UI now working in snap
This commit is contained in:
kz6fittycent 2022-08-21 15:50:31 -05:00 committed by GitHub
commit 0cd636a3a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 8218 additions and 729 deletions

View file

@ -1,3 +1,7 @@
[*.{cpp,h,sh,md,cfg,sample}]
indent_style = tab
indent_size = 4
[*.{yml,yaml}]
indent_style = space
indent_size = 2

View file

@ -7,6 +7,10 @@ assignees: aristocratos
---
**Read the README.md and search for similar issues before posting a bug report!**
Any bug that can be solved by just reading the [prerequisites](https://github.com/aristocratos/btop#prerequisites) section of the README will likely be ignored.
**Describe the bug**
[A clear and concise description of what the bug is.]
@ -24,7 +28,7 @@ assignees: aristocratos
[If applicable, add screenshots to help explain your problem.]
**Info (please complete the following information):**
- btop++ version: `bpytop -v`
- btop++ version: `btop -v`
- Binary: [self compiled or static binary from release]
- (If compiled) Compiler and version:
- Architecture: [x86_64, aarch64, etc.] `uname -m`
@ -38,16 +42,18 @@ assignees: aristocratos
contents of `~/.config/btop/btop.log`
(try running btop with `--debug` flag if error.log is empty)
(try running btop with `--debug` flag if btop.log is empty)
**GDB Backtrace**
If btop++ is crashing at start the following steps could be helpful:
1. run `gdb btop`
(Extra helpful if compiled with `make OPTFLAGS="-O0 -g"`)
2. `r` to run, wait for crash and press enter
1. run (linux): `gdb btop` (macos): `lldb btop`
3. `bt` to get backtrace
2. `r` to run, wait for crash and press enter if prompted, CTRL+L to clear screen if needed.
3. (gdb): `thread apply all bt` (lldb): `bt all` to get backtrace for all threads
4. Copy and paste the backtrace here:

View file

@ -0,0 +1,113 @@
name: Continuous Build Linux
on:
workflow_dispatch:
push:
branches:
- main
tags-ignore:
- '*.*'
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build.yml'
jobs:
static-build:
continue-on-error: true
strategy:
matrix:
toolchain:
- aarch64-linux-musl
- aarch64_be-linux-musl
- arm-linux-musleabi
- arm-linux-musleabihf
- armeb-linux-musleabi
- armeb-linux-musleabihf
- armel-linux-musleabi
- armel-linux-musleabihf
- armv5l-linux-musleabi
- armv5l-linux-musleabihf
- armv6-linux-musleabi
- armv6-linux-musleabihf
- armv7l-linux-musleabihf
- armv7m-linux-musleabi
- armv7r-linux-musleabihf
- i486-linux-musl
- i686-linux-musl
- m68k-linux-musl
- mips-linux-musl
- mips-linux-musln32sf
- mips-linux-muslsf
- mips64-linux-musl
- mips64-linux-musln32
- mips64-linux-musln32sf
- mips64el-linux-musl
- mips64el-linux-musln32
- mips64el-linux-musln32sf
- mipsel-linux-musl
- mipsel-linux-musln32
- mipsel-linux-musln32sf
- mipsel-linux-muslsf
- powerpc-linux-musl
- powerpc-linux-muslsf
- powerpc64-linux-musl
- powerpc64le-linux-musl
- powerpcle-linux-musl
- powerpcle-linux-muslsf
- riscv32-linux-musl
- riscv64-linux-musl
- s390x-linux-musl
- x86_64-linux-musl
- x86_64-linux-muslx32
# - or1k-linux-musl
# - sh2-linux-musl
# - sh2-linux-muslfdpic
# - sh2eb-linux-musl
# - sh2eb-linux-muslfdpic
# - sh4-linux-musl
# - sh4eb-linux-musl
runs-on: ubuntu-latest
container: muslcc/x86_64:${{ matrix.toolchain }}
steps:
- name: Install build tools
run: apk add --no-cache coreutils git make tar zstd
- name: Fix - Unsafe repository stop
run: git config --global --add safe.directory /__w/btop/btop
- name: Checkout source
uses: actions/checkout@v2
- name: Fix - Stopping at filesystem boundary
run: git init # [fix Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).]
- name: Build
run: make STATIC=true STRIP=true
- name: Make executable
run: chmod +x bin/*
- name: Set up directories
run: |
mkdir .artifacts
mkdir .package
- name: Create binary atrifacts
run: |
TOOLCHAIN=${{ matrix.toolchain }}
GIT_HASH=$(git rev-parse --short "${{ github.sha }}")
FILENAME=btop-${TOOLCHAIN/linux-musl/}-$GIT_HASH
cp bin/btop .artifacts/$FILENAME
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: btop-${{ matrix.toolchain }}
path: '.artifacts/**'

View file

@ -0,0 +1,34 @@
name: Continuous Build MacOS
on:
push:
branches:
- main
tags-ignore:
- '*.*'
paths:
- 'src/**'
- '!src/linux/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/*'
jobs:
build-osx:
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: Compile
run: |
make CXX=g++-11 ARCH=x86_64 STATIC=true STRIP=true
GIT_HASH=$(git rev-parse --short "$GITHUB_SHA")
mv bin/btop bin/btop-x86_64-BigSur-$GIT_HASH
ls -alh bin
- uses: actions/upload-artifact@v2
with:
name: btop-x86_64-macos-BigSur
path: 'bin/*'

6
.gitignore vendored
View file

@ -49,4 +49,8 @@ stage/
build
bin
btop
.*/
.*/
#do not ignore .github directory
!.github

View file

@ -1,3 +1,291 @@
## v1.2.8
* Added: Support for ZFS pool io stats monitoring, by @simplepad
* Added: Filtering of kernel processes, by @0xJoeMama
* Added: New theme everforest-dark-hard, by @iambeingtracked
* Added: New theme tomorrow-night, by @appuchias
* Changed: Disable battery monitoring if it fails instead of exiting
## v1.2.7
* Fixed: Disk IO stats for individual partitions instead of whole disk (Linux)
* Added: Case insensitive process filtering, by @abrasumente233
* Added: Include ZFS ARC in cached/available memory on Linux, by @mattico
* Added: Desktop entry and icons, by @yonatan8070
* Fixed: Net sync scale bug
* Added: tokyo-night & tokyo-storm themes, by @Schievel1
## v1.2.6
* Fixed: Wrong memory unit when shorten and size is less than 10, by @mohi001
* Fixed: Use cpu cores avarage temp if missing cpu package temp for FreeBSD
* Changed: Enter symbol to a more common variant
## v1.2.5
* Fixed: Fallback to less accurate UTF8 char count if conversion to wstring fails
* Fixed: Small ui fixes for mem and disks
* Added: New theme HotPurpleTrafficLight, by @pallebone
* Fixed: title_left symbol between auto and zero in the net box is not displayed, by @mrdotx
* Fixed: Mouse mappings for net box
## v1.2.4
* Optimization: Proc::draw()
* Fixed: Ignore duplicate disks with same mountpoint
* Changed: Restrict command line for processes to 1000 characters to fix utf8 conversion errors
* Added: add "g" and "G" to vim keys, by @mohi001
## v1.2.3
* Changed: floating_humanizer() now show fractions when shortened and value is < 10
* Fixed: Process tree not redrawing properly
* Fixed: string to wstring conversion crash when string is too big
## v1.2.2
* Changed: Reverted uncolor() back to using regex to fix delay in opening menu when compiled with musl
* Added: Toggle for showing free disk space for privileged or normal users
* Added: Clarification on signal screen that number can be manually entered
## v1.2.1
* Added: Arrow only after use of "f" when filtering processes, by @NavigationHazard
* Fixed: Fx::uncolor not removing all escapes
* Fixed: Text alignment for popup boxes
* Fixed: Terminal resize warning getting stuck
* Removed: Unnecessary counter for atomic_lock
* Added: Percentage progress to Makefile
* Fixed: Alignment of columns in proc box when wide UTF8 characters are used
* Fixed: Battery meter draw fix
## v1.2.0
* Added: Support for FreeBSD, by @joske and @aristocratos
* Fixed (again): Account for system rolling over net speeds in Net::collect()
* Added: Theme gruvbox_material_dark, by @marcoradocchia
* Added: Option for base 10 bytes/bits
## v1.1.5
* Fixed: Account for system rolling over net speeds in Net::collect()
## v1.1.4
* Fixed: Create dependency files in build directory when compiling, by @stwnt
* Fixed: fix CPU temp fallback on macOS, by @joske
* Changed: From rng::sort() to rng::stable_sort() for more stability
* Fixed: in_avail() can always be zero, by @pg83
## v1.1.3
* Added: New theme ayu, by @AlphaNecron
* Added: New theme gruvbox_dark_v2, by @pietryszak
* Fixed: Macos cpu coretemp for Intel, by @joske
* Added: New theme OneDark, by @vtmx
* Fixed: Fixed network graph scale int rollover
* Fixed: Suspected possibility of very rare stall in Input::clear()
## v1.1.2
* Fixed: SISEGV on macos Mojave, by @mgradowski
* Fixed: Small optimizations and fixes to Mem::collect() and Input::get()
* Fixed: Wrong unit for net_upload and net_download in config menu
* Fixed: UTF-8 detection on macos
* Fixed: coretemp iteration due to missing tempX_input, by @KFilipek
* Fixed: coretemp ordering
## v1.1.1
* Added: Partial static build (libgcc, libstdc++) for macos
* Changed: Continuous build macos switched to OSX 11.6 (Big Sur) and partial static build
* Changed: Release binaries for macos switched to OSX 12 (Monterey) and partial static build
## v1.1.0
* Added: Support for OSX, by @joske and @aristocratos
## v1.0.24
* Changed: Collection ordering
* Fixed: Restore all escape seq mouse modes on exit
* Fixed: SIGINT not cleaning up on exit
## v1.0.23
* Fixed: Config parser missing first value when not including version header
* Fixed: Vim keys menu lists selection
* Fixed: Stall when clearing input queue on exit and queue is >1
* Fixed: Inconsistent behaviour of "q" key in the menus
## v1.0.22
* Fixed: Bad values for disks and network on 32-bit
## v1.0.21
* Fixed: Removed extra spaces in cpu name
* Added: / as alternative bind for filter
* Fixed: Security issue when running with SUID bit set
## v1.0.20
* Added: Improved cpu sensor detection for Ryzen Mobile, by @adnanpri
* Changed: Updated makefile
* Changed: Regex for Fx::uncolor() changed to string search and replace
* Changed: Removed all use of regex with dedicated string functions
## v1.0.19
* Fixed: Makefile now tests compiler flag compatibility
## v1.0.18
* Fixed: Makefile g++ -dumpmachine failure to get platform on some distros
## v1.0.17
* Changed: Reverted mutexes back to custom atomic bool based locks
* Added: Static binaries switched to building with musl + more platforms, by @jan-guenter
* Fixed: Improved battery detection, by @jan-guenter
* Added: Displayed battery selectable in options menu
* Fixed: Battery error if non existent battery named is entered
## v1.0.16
* Fixed: atomic_wait() and atomic_lock{} use cpu pause instructions instead of thread sleep
* Fixed: Swapped from atomic bool spinlocks to mutexes to fix rare deadlock
* Added: Continuous Build workflow for OSX branch, by @ShrirajHegde
* Changed: Reverted thread mutex lock to atomic bool with wait and timeout
* Changed: Removed unnecessary async threads in Runner thread
* Added: Try to restart secondary thread in case of stall and additional error checks for ifstream in Proc::collect()
* Fixed: change [k]ill to [K]ill when enabling vim keys, by @jlopezcur
## v1.0.15
* Fixed: Extra "root" partition when running in snap
* Changed: Limit atomic_wait() to 1000ms to fix rare stall
* Fixed: Removed unneeded lock in Runner::run()
* Added: Toggle in options for enabling directional vim keys "h,j,k,l"
## v1.0.14
* Changed: Total system memory is checked at every update instead of once at start
* Added: Continuous Build workflow, by @ShrirajHegde
* Fixed: Uid -> User fallback to getpwuid() if failure for non static builds
* Fixed: snap root disk and changed to compiler flags instead of env variables for detection
* Added: Development branch for OSX, by @joske
## v1.0.13
* Changed: Graph empty symbol is now regular whitespace
## v1.0.12
* Fixed: Exception handling for faulty net download/upload speed
* Fixed: Cpu percent formatting if over 10'000
## v1.0.11
* Changed: atomic_wait to use while loop instead of wait() because of rare stall when a signal handler is triggered while waiting
* Fixed: Get real / mountpoint when running inside snap
* Fixed: UTF8 set LANG and LC_ALL to empty before UTF8 search and fixed empty error msg on exit before signal handler init
* Changed: Init will continue with a warning if UTF-8 locale are detected and it fails to set the locale
## v1.0.10
* Added: Wait for terminal size properties to be available at start
* Changed: Stop second thread before updating terminal size variables
* Changed: Moved check for valid terminal dimensions to before platform init
* Added: Check for empty percentage deques
* Changed: Cpu temp values check for existing values
* Fixed: Cpu percent cutting off above 1000 percent and added scaling with "k" prefix above 10'000
* Fixed: Crash when rapidly resizing terminal at start
## v1.0.9
* Added: ifstream check and try-catch for stod() in Tools::system_uptime()

BIN
Img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

111
Img/icon.svg Normal file
View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="90"
height="90"
version="1.1"
id="svg70"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs26">
<pattern
xlink:href="#gray-stripes"
id="pattern1888" />
<pattern
xlink:href="#red-stripes"
id="pattern1870" />
<pattern
id="red-stripes"
width="341"
height="90"
patternUnits="userSpaceOnUse">
<rect
width="341"
height="16"
fill="#f00"
id="rect2" />
<rect
y="16"
width="341"
height="16"
fill="#d70000"
id="rect4" />
<rect
y="32"
width="341"
height="16"
fill="#af0000"
id="rect6" />
<rect
y="48"
width="341"
height="16"
fill="#870000"
id="rect8" />
<rect
y="64"
width="341"
height="16"
fill="#5f0000"
id="rect10" />
</pattern>
<pattern
id="gray-stripes"
width="341"
height="90"
patternUnits="userSpaceOnUse">
<rect
width="341"
height="16"
fill="#585858"
id="rect13" />
<rect
y="16"
width="341"
height="16"
fill="#4e4e4e"
id="rect15" />
<rect
y="32"
width="341"
height="16"
fill="#444"
id="rect17" />
<rect
y="48"
width="341"
height="16"
fill="#3a3a3a"
id="rect19" />
<rect
y="64"
width="341"
height="16"
fill="#303030"
id="rect21" />
<rect
y="80"
width="341"
height="10"
fill="#262626"
id="rect23" />
</pattern>
</defs>
<g
id="g2074"
transform="translate(18)">
<path
d="M 2,7 V 90 H 47 V 74 h 7 V 55 H 47 V 42 h 7 V 23 H 47 V 7 Z M 19,26 H 37 V 39 H 19 Z m 0,32 H 37 V 71 H 19 Z"
id="path28"
style="fill:#080808" />
<path
d="M 2,7 V 90 H 47 V 74 h 7 V 55 H 47 V 42 h 7 V 23 H 47 V 7 Z m 1,1 h 43 v 16 h 7 v 17 h -7 v 15 h 7 V 73 H 46 V 89 H 3 Z M 18,25 H 38 V 40 H 18 Z m 1,1 V 39 H 37 V 26 Z M 18,57 H 38 V 72 H 18 Z m 1,1 V 71 H 37 V 58 Z M 4,9 V 88 H 45 V 72 h 7 V 57 H 45 V 40 h 7 V 25 H 45 V 9 Z m 1,1 h 39 v 16 h 7 v 13 h -7 v 19 h 7 V 71 H 44 V 87 H 5 Z M 16,23 H 40 V 42 H 16 Z m 1,1 V 41 H 39 V 24 Z M 16,55 H 40 V 74 H 16 Z m 1,1 V 73 H 39 V 56 Z"
id="path42"
style="fill:url(#pattern1888)" />
<path
d="M 0,0 V 80 H 42 V 64 h 7 V 48 H 42 V 32 h 7 V 16 H 42 V 0 Z M 14,16 H 35 V 32 H 14 Z m 0,32 H 35 V 64 H 14 Z"
id="path56"
style="fill:url(#pattern1870)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1,012 B

44
Img/logo.svg Normal file
View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="341" height="90" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="red-stripes" width="341" height="90" patternUnits="userSpaceOnUse">
<rect width="341" height="16" fill="#f00"/>
<rect y="16" width="341" height="16" fill="#d70000"/>
<rect y="32" width="341" height="16" fill="#af0000"/>
<rect y="48" width="341" height="16" fill="#870000"/>
<rect y="64" width="341" height="16" fill="#5f0000"/>
</pattern>
<pattern id="gray-stripes" width="341" height="90" patternUnits="userSpaceOnUse">
<rect width="341" height="16" fill="#585858"/>
<rect y="16" width="341" height="16" fill="#4e4e4e"/>
<rect y="32" width="341" height="16" fill="#444"/>
<rect y="48" width="341" height="16" fill="#3a3a3a"/>
<rect y="64" width="341" height="16" fill="#303030"/>
<rect y="80" width="341" height="10" fill="#262626"/>
</pattern>
</defs>
<g fill="#080808">
<path d="m2,7v83h45v-16h7v-19h-7v-13h7v-19h-7v-16h-45zm17,19h18v13h-18v-13zm0,32h18v13h-18v-13z"/>
<path d="m58,7v19h21v64h17v-64h21v-19h-59z"/>
<path d="m128,7v16h-7v51h7v16h45v-16h7v-51h-7v-16h-45zm10,19h25v45h-25v-45z"/>
<path d="m184,7v83h17v-32h28v-16h7v-19h-7v-16h-45zm17,19h18v13h-18v-13z"/>
<path d="m261,23v16h-14v19h14v16h17v-16h14v-19h-14v-16h-17z"/>
<path d="m310,23v16h-14v19h14v16h17v-16h14v-19h-14v-16h-17z"/>
</g>
<g fill="url(#gray-stripes)">
<path d="m2,7v83h45v-16h7v-19h-7v-13h7v-19h-7v-16h-45zm1,1h43v16h7v17h-7v15h7v17h-7v16h-43v-81zm15,17h20v15h-20v-15zm1,1v13h18v-13h-18zm-1,31h20v15h-20v-15zm1,1v13h18v-13h-18zm-15-49v79h41v-16h7v-15h-7v-17h7v-15h-7v-16h-41zm1,1h39v16h7v13h-7v19h7v13h-7v16h-39v-77zm11,13h24v19h-24v-19zm1,1v17h22v-17h-22zm-1,31h24v19h-24v-19zm1,1v17h22v-17h-22z"/>
<path d="m58,7v19h21v64h17v-64h21v-19h-59zm1,1h57v17h-21v64h-15v-64h-21v-17zm1,1v15h21v64h13v-64h21v-15h-55zm1,1h53v13h-21v64h-11v-64h-21v-13z"/>
<path d="m128,7v16h-7v51h7v16h45v-16h7v-51h-7v-16h-45zm1,1h43v16h7v49h-7v16h-43v-16h-7v-49h7v-16zm9,18v45h25v-45h-25zm-1-1h27v47h-27v-47zm-7-16v16h-7v47h7v16h41v-16h7v-47h-7v-16h-41zm1,1h39v16h7v45h-7v16h-39v-16h-7v-45h7v-16zm5,14v49h29v-49h-29zm-1-1h31v51h-31v-51z"/>
<path d="m184,7v83h17v-32h28v-16h7v-19h-7v-16h-45zm1,1h43v16h7v17h-7v16h-28v32h-15v-81zm16,18v13h18v-13h-18zm-1-1h20v15h-20v-15zm-14-16v79h13v-32h28v-16h7v-15h-7v-16h-41zm1,1h39v16h7v13h-7v16h-28v32h-11v-77zm12,14v17h22v-17h-22zm-1-1h24v19h-24v-19z"/>
<path d="m261,23v16h-14v19h14v16h17v-16h14v-19h-14v-16h-17zm1,1h15v16h14v17h-14v16h-15v-16h-14v-17h14v-16zm1,1v16h-14v15h14v16h13v-16h14v-15h-14v-16h-13zm1,1h11v16h14v13h-14v16h-11v-16h-14v-13h14v-16z"/>
<path d="m310,23v16h-14v19h14v16h17v-16h14v-19h-14v-16h-17zm1,1h15v16h14v17h-14v16h-15v-16h-14v-17h14v-16zm1,1v16h-14v15h14v16h13v-16h14v-15h-14v-16h-13zm1,1h11v16h14v13h-14v16h-11v-16h-14v-13h14v-16z"/>
</g>
<g fill="url(#red-stripes)">
<path d="m0,0v80h42v-16h7v-16h-7v-16h7v-16h-7v-16h-42zm14,16h21v16h-21v-16zm0,32h21v16h-21v-16z"/>
<path d="m56,0v16h21v64h14v-64h21v-16h-56z"/>
<path d="m126,0v16h-7v48h7v16h42v-16h7v-48h-7v-16h-42zm7,16h28v48h-28v-48z"/>
<path d="m182,0v80h14v-32h28v-16h7v-16h-7v-16h-42zm14,16h21v16h-21v-16z"/>
<path d="m259,16v16h-14v16h14v16h14v-16h14v-16h-14v-16h-14z"/>
<path d="m308,16v16h-14v16h14v16h14v-16h14v-16h-14v-16h-14z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

149
Makefile
View file

@ -1,12 +1,18 @@
#* Btop++ makefile v1.2
#* Btop++ makefile v1.5
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.2\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.5\033[0m
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 DATESTAMP := $(shell date '+%Y-%m-%d %H:%M:%S' || echo "5 minutes ago")
ifeq ($(shell command -v gdate >/dev/null; echo $$?),0)
DATE_CMD := gdate
else
DATE_CMD := date
endif
ifneq ($(QUIET),true)
override PRE := info
override PRE := info info-quiet
override QUIET := false
else
override PRE := info-quiet
@ -14,33 +20,56 @@ endif
PREFIX ?= /usr/local
#? NOTICE! Manually set PLATFORM and ARCH if not compiling for host system
#? Detect PLATFORM and ARCH from uname/gcc if not set
PLATFORM ?= $(shell uname -s || echo unknown)
ARCH ?= $(shell uname -m || echo unknown)
ifneq ($(filter unknown Darwin, $(PLATFORM)),)
override PLATFORM := $(shell $(CXX) -dumpmachine | awk -F"-" '{ print (NF==4) ? $$3 : $$2 }')
ifeq ($(PLATFORM),apple)
override PLATFORM := macos
endif
endif
ifeq ($(shell uname -v | grep ARM64 >/dev/null 2>&1; echo $$?),0)
ARCH ?= arm64
else
ARCH ?= $(shell $(CXX) -dumpmachine | cut -d "-" -f 1)
endif
#? Only enable fcf-protection if on x86_64
ifeq ($(ARCH),x86_64)
override ADDFLAGS += -fcf-protection
override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]')
#? Any flags added to TESTFLAGS must not contain whitespace for the testing to work
override TESTFLAGS := -fexceptions -fstack-clash-protection -fcf-protection
ifneq ($(PLATFORM) $(ARCH),macos arm64)
override TESTFLAGS += -fstack-protector
endif
ifeq ($(STATIC),true)
override ADDFLAGS += -static -static-libgcc -static-libstdc++
endif
#? Make sure PLATFORM Darwin is OSX and not Darwin
ifeq ($(PLATFORM),Darwin)
ifeq ($(shell sw_vers >/dev/null 2>&1; echo $$?),0)
PLATFORM := OSX
override ADDFLAGS += -static-libgcc -static-libstdc++
ifneq ($(PLATFORM),macos)
override ADDFLAGS += -DSTATIC_BUILD -static -Wl,--fatal-warnings
endif
endif
ifeq ($(STRIP),true)
override ADDFLAGS += -s
endif
#? Compiler and Linker
CXX ?= g++
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++
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++)
V_MAJOR := $(shell echo $(CXX_VERSION) | cut -f1 -d".")
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
@ -50,21 +79,27 @@ ifeq ($(CXX),g++)
endif
#? Pull in platform specific source files and get thread count
ifeq ($(PLATFORM),Linux)
ifeq ($(PLATFORM_LC),linux)
PLATFORM_DIR := linux
THREADS := $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
else ifeq ($(PLATFORM),FreeBSD)
SU_GROUP := root
else ifeq ($(PLATFORM_LC),freebsd)
PLATFORM_DIR := freebsd
THREADS := $(shell getconf NPROCESSORS_ONLN 2>/dev/null || echo 1)
else ifeq ($(PLATFORM),OSX)
SU_GROUP := wheel
override ADDFLAGS += -lstdc++ -lm -lkvm -ldevstat -Wl,-rpath=/usr/local/lib/gcc11
export MAKE = gmake
else ifeq ($(PLATFORM_LC),macos)
PLATFORM_DIR := osx
THREADS := $(shell sysctl -n hw.ncpu || echo 1)
override ADDFLAGS += -framework IOKit -framework CoreFoundation -Wno-format-truncation
SU_GROUP := wheel
else
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
endif
#? Use all CPU cores (will only be set if using Make 4.3+)
MAKEFLAGS := --jobs=$(THREADS)
MAKEFLAGS := --jobs=$(THREADS)
ifeq ($(THREADS),1)
override THREADS := auto
endif
@ -78,23 +113,47 @@ SRCEXT := cpp
DEPEXT := d
OBJEXT := o
#? Filter out unsupported compiler flags
override GOODFLAGS := $(foreach flag,$(TESTFLAGS),$(strip $(shell echo "int main() {}" | $(CXX) -o /dev/null $(flag) -x c++ - >/dev/null 2>&1 && echo $(flag) || true)))
#? Flags, Libraries and Includes
override REQFLAGS := -std=c++20
WARNFLAGS := -Wall -Wextra -pedantic
OPTFLAGS ?= -O2 -ftree-loop-vectorize -flto=$(THREADS)
LDCXXFLAGS := -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector -fstack-clash-protection $(ADDFLAGS)
OPTFLAGS := -O2 -ftree-loop-vectorize -flto=$(THREADS)
LDCXXFLAGS := -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS $(GOODFLAGS) $(ADDFLAGS)
override CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
INC := -I$(INCDIR) -I$(SRCDIR)
SU_USER := root
SU_GROUP := root
SOURCES := $(shell find $(SRCDIR) -maxdepth 1 -type f -name *.$(SRCEXT))
ifdef DEBUG
override OPTFLAGS := -O0 -g
endif
SOURCES += $(shell find $(SRCDIR)/$(PLATFORM_DIR) -type f -name *.$(SRCEXT))
SOURCES := $(sort $(shell find $(SRCDIR) -maxdepth 1 -type f -name *.$(SRCEXT)))
SOURCES += $(sort $(shell find $(SRCDIR)/$(PLATFORM_DIR) -type f -name *.$(SRCEXT)))
#? Setup percentage progress
SOURCE_COUNT := $(words $(SOURCES))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
ifeq ($(shell find $(BUILDDIR) -type f -newermt "$(DATESTAMP)" -name *.o >/dev/null 2>&1; echo $$?),0)
ifneq ($(wildcard $(BUILDDIR)/.*),)
SKIPPED_SOURCES := $(foreach fname,$(SOURCES),$(shell find $(BUILDDIR) -type f -newer $(fname) -name *.o | grep "$(basename $(notdir $(fname))).o" 2>/dev/null))
override SOURCE_COUNT := $(shell expr $(SOURCE_COUNT) - $(words $(SKIPPED_SOURCES)))
ifeq ($(SOURCE_COUNT),0)
override SOURCE_COUNT = $(words $(SOURCES))
endif
endif
PROGRESS = expr $$(find $(BUILDDIR) -type f -newermt "$(DATESTAMP)" -name *.o | wc -l || echo 1) '*' 90 / $(SOURCE_COUNT) | cut -c1-2
else
PROGRESS = expr $$(find $(BUILDDIR) -type f -name *.o | wc -l || echo 1) '*' 90 / $(SOURCE_COUNT) | cut -c1-2
endif
P := %%
#? Default Make
all: $(PRE) directories btop
@ -111,10 +170,8 @@ info:
@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;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m)\n"
@printf "\n\033[1;92mBuilding btop++ \033[93m(\033[97mv$(BTOP_VERSION)\033[93m)\033[0m\n"
info-quiet:
@sleep 0.1 2>/dev/null || true
@printf "\n\033[1;92mBuilding btop++ \033[91m(\033[97mv$(BTOP_VERSION)\033[91m) \033[93m$(PLATFORM) \033[96m$(ARCH)\033[0m\n"
help:
@ -128,6 +185,7 @@ help:
@printf " install Install btop++ to \$$PREFIX ($(PREFIX))\n"
@printf " setuid Set installed binary owner/group to \$$SU_USER/\$$SU_GROUP ($(SU_USER)/$(SU_GROUP)) and set SUID bit\n"
@printf " uninstall Uninstall btop++ from \$$PREFIX\n"
@printf " info Display information about Environment,compiler and linker flags\n"
#? Make the Directories
directories:
@ -154,6 +212,16 @@ install:
@cp -p README.md $(DESTDIR)$(PREFIX)/share/btop
@printf "\033[1;92mInstalling themes to: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop/themes\033[0m\n"
@cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
@printf "\033[1;92mInstalling desktop entry to: \033[1;97m$(DESTDIR)$(PREFIX)/share/applications/btop.desktop\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/applications/
@cp -p btop.desktop $(DESTDIR)$(PREFIX)/share/applications/btop.desktop
@printf "\033[1;92mInstalling PNG icon to: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps
@cp -p Img/icon.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png
@printf "\033[1;92mInstalling SVG icon to: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps
@cp -p Img/icon.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg
#? Set SUID bit for btop as $SU_USER in $SU_GROUP
setuid:
@ -168,6 +236,12 @@ uninstall:
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/btop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/applications/btop.desktop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/applications/btop.desktop
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/btop.png
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/btop.svg
#? Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
@ -175,26 +249,21 @@ uninstall:
#? Link
.ONESHELL:
btop: $(OBJECTS)
@sleep 0.1 2>/dev/null || true
@sleep 0.2 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
@$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS) || exit 1
@printf "\033[1;92m-> \033[1;37m$(TARGETDIR)/btop \033[100D\033[35C\033[1;93m(\033[1;97m$$(du -ah $(TARGETDIR)/btop | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$(date -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 -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 "\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"
#? Compile
.ONESHELL:
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
@sleep 0.1 2>/dev/null || true
@sleep 0.3 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\033[1;97mCompiling $<\033[0m\n"
@$(CXX) $(CXXFLAGS) $(INC) -c -o $@ $< || exit 1
@$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) >/dev/null || exit 1
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
@sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
@sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
@printf "\033[1;92m-> \033[1;37m$@ \033[100D\033[35C\033[1;93m(\033[1;97m$$(du -ah $@ | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$(date -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"
@$(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"
#? Non-File Targets
.PHONY: all msg help pre

327
README.md
View file

@ -5,17 +5,20 @@
</a>
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
![OSX](https://img.shields.io/badge/-OSX-black?logo=apple)
![FreeBSD](https://img.shields.io/badge/-FreeBSD-red?logo=freebsd)
![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow)
![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green)
![btop_version](https://img.shields.io/github/v/tag/aristocratos/btop?label=version)
![latest_release](https://img.shields.io/github/v/tag/aristocratos/btop?label=release)
[![Donate](https://img.shields.io/badge/-Donate-yellow?logo=paypal)](https://paypal.me/aristocratos)
[![Sponsor](https://img.shields.io/badge/-Sponsor-red?logo=github)](https://github.com/sponsors/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)
[![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)
## Index
* [News](#news)
* [Documents](#documents)
* [Description](#description)
@ -25,15 +28,67 @@
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
* [Screenshots](#screenshots)
* [Keybindings](#help-menu)
* [Installation](#installation)
* [Manual compilation](#compilation)
* [Install the snap](#install-the-snap)
* [Installation Linux/OSX](#installation)
* [Compilation Linux](#compilation-linux)
* [Compilation OSX](#compilation-osx)
* [Compilation FreeBSD](#compilation-freebsd)
* [Installing the snap](#installing-the-snap)
* [Configurability](#configurability)
* [License](#license)
## News
### Under development
##### 16 January 2022
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!
Since compatibility with Linux, MacOS and FreeBSD are done, the focus going forward will be on new features like GPU monitoring.
##### 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.
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!
<details>
<summary>More...</summary>
##### 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.
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.
If you want to help out, test for bugs/fix bugs or just try out the branches:
**OSX**
```bash
# Install and use Homebrew or MacPorts package managers for easy dependency installation
brew install coreutils make gcc@11
git clone https://github.com/aristocratos/btop.git
cd btop
git checkout OSX
gmake
```
**FreeBSD**
```bash
sudo pkg install gmake gcc11 coreutils git
git clone https://github.com/aristocratos/btop.git
cd btop
git checkout freebsd
gmake
```
Note that GNU make (`gmake`) is recommended but not required for OSX but it is required on FreeBSD.
##### 6 October 2021
OsX 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.
##### 18 September 2021
@ -59,6 +114,8 @@ Windows support is not in the plans as of now, but if anyone else wants to take
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.
</details>
## Documents
**[CHANGELOG.md](CHANGELOG.md)**
@ -114,7 +171,7 @@ Any support is greatly appreciated!
For best experience, a terminal with support for:
* 24-bit truecolor ([See list of terminals with truecolor support](https://gist.github.com/XVilka/8346728))
* 24-bit truecolor ([See list of terminals with truecolor support](https://github.com//termstandard/colors))
* 256-color terminals are supported through 24-bit to 256-color conversion when setting "truecolor" to False in the options or with "-lc/--low-color" arguments.
* 16 color TTY mode will be activated if a real tty device is detected. Can be forced with "-t/--tty_on" arguments.
* Wide characters (Are sometimes problematic in web-based terminals)
@ -167,9 +224,11 @@ Also needs a UTF8 locale and a font that covers:
## Installation
**Binary release (statically compiled, for kernel 3.2.0 and newer)**
**Binaries for Linux are statically compiled with musl and works on kernel 2.6.39 and newer**
1. **Download btop-(VERSION)-(PLATFORM)-(ARCH).tbz from [latest release](https://github.com/aristocratos/btop/releases/latest) and unpack to a new folder**
1. **Download btop-(VERSION)-(ARCH)-(PLATFORM).tbz from [latest release](https://github.com/aristocratos/btop/releases/latest) and unpack to a new folder**
**Notice! Use x86_64 for 64-bit x86 systems, i486 and i686 are 32-bit!**
2. **Install (from created folder)**
@ -207,17 +266,37 @@ Also needs a UTF8 locale and a font that covers:
make help
```
## Compilation
**Binary release (from native os repo)**
* **openSUSE**
* **Tumbleweed:**
```bash
sudo zypper in btop
```
* For all other versions, see [openSUSE Software: btop](https://software.opensuse.org/package/btop)
**Binary release on Homebrew (macOS (x86_64 & ARM64) / Linux (x86_64))**
* **[Homebrew](https://formulae.brew.sh/formula/btop)**
```bash
brew install btop
```
## Compilation Linux
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
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
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
Use gcc-10 g++-10 if gcc-11 isn't available
``` bash
sudo apt install coreutils sed git build-essential gcc-11 g++-11
# use gcc-10 g++-10 if gcc-11 isn't available
```
2. **Clone repository**
@ -231,13 +310,20 @@ Also needs a UTF8 locale and a font that covers:
Append `STATIC=true` to `make` command for static compilation.
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.
Notice! Manually set `$ARCH` variable if cross-compiling
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: `make ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
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
@ -245,9 +331,11 @@ Also needs a UTF8 locale and a font that covers:
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
``` bash
# use "make install PREFIX=/target/dir" to set target, default: /usr/local
# only use "sudo" when installing to a NON user owned directory
sudo make install
```
@ -255,9 +343,11 @@ Also needs a UTF8 locale and a font that covers:
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.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `root`
``` bash
# 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
sudo make setuid
```
@ -285,7 +375,177 @@ Also needs a UTF8 locale and a font that covers:
make help
```
## Install the snap
## Compilation OSX
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
The makefile also needs GNU coreutils and `sed`.
Install and use Homebrew or MacPorts package managers for easy dependency installation
1. **Install dependencies (example for Homebrew)**
``` bash
brew install coreutils make gcc@11
```
2. **Clone repository**
``` bash
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
Append `STATIC=true` to `make` command for static compilation (only libgcc and libstdc++ will be static!).
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
```
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
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)**
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
``` bash
sudo gmake setuid
```
* **Uninstall**
``` bash
sudo gmake uninstall
```
* **Remove any object files from source dir**
```bash
gmake clean
```
* **Remove all object files, binaries and created directories in source dir**
```bash
gmake distclean
```
* **Show help**
```bash
gmake help
```
## Compilation FreeBSD
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
Note that GNU make (`gmake`) is required to compile on FreeBSD.
1. **Install dependencies**
``` bash
sudo pkg install gmake gcc11 coreutils git
```
2. **Clone repository**
``` bash
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
Append `STATIC=true` to `make` command for static compilation.
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
```
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
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)**
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
``` bash
sudo gmake setuid
```
* **Uninstall**
``` bash
sudo gmake uninstall
```
* **Remove any object files from source dir**
```bash
gmake clean
```
* **Remove all object files, binaries and created directories in source dir**
```bash
gmake distclean
```
* **Show help**
```bash
gmake help
```
## Installing the snap
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
* **Install the snap**
@ -297,7 +557,7 @@ Also needs a UTF8 locale and a font that covers:
```
sudo snap install btop --edge
```
* **Connect the interface**
```bash
@ -310,17 +570,17 @@ Also needs a UTF8 locale and a font that covers:
All options changeable from within UI.
Config and log files stored in `$XDG_CONFIG_HOME/btop` or `$HOME/.config/btop` folder
#### btop.cfg: (auto generated if not found)
#### btop.conf: (auto generated if not found)
```bash
#? Config file for btop v. 0.1.0
#? Config file for btop v. 1.2.2
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "Default"
#* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = False
theme_background = True
#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
truecolor = True
@ -330,11 +590,15 @@ truecolor = True
force_tty = False
#* 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 positons, G=graph symbol to use for box.
#* Use withespace " " as seprator between different presets.
#* 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.
#* 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"
#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
vim_keys = False
#* Rounded corners on boxes, is ignored if TTY mode is ON.
rounded_corners = True
@ -358,10 +622,10 @@ graph_symbol_net = "default"
graph_symbol_proc = "default"
#* Manually set which boxes to show. Available values are "cpu mem net proc", separate values with whitespace.
shown_boxes = "cpu mem net proc"
shown_boxes = "proc cpu mem net"
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
update_ms = 2000
update_ms = 1500
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive",
#* "cpu lazy" sorts top process over time (easier to follow), "cpu responsive" updates top process directly.
@ -429,6 +693,9 @@ cpu_core_map = ""
#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
temp_scale = "celsius"
#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
base_10_sizes = False
#* Show CPU frequency.
show_cpu_freq = True
@ -467,6 +734,9 @@ only_physical = True
#* Read disks list from /etc/fstab. This also disables only_physical.
use_fstab = False
#* Set to true to show available disk space for privileged users.
disk_free_priv = False
#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
show_io_stat = True
@ -497,6 +767,9 @@ net_iface = "br0"
#* Show battery stats in top right if battery is present.
show_battery = True
#* Which battery to use if multiple are present. "Auto" for auto detection.
selected_battery = "Auto"
#* Set loglevel for "~/.config/btop/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
log_level = "DEBUG"

13
btop.desktop Normal file
View file

@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=btop++
GenericName=System Monitor
GenericName[it]=Monitor di sistema
Comment=Resource monitor that shows usage and stats for processor, memory, disks, network and processes
Comment[it]=Monitoraggio delle risorse: mostra utilizzo e statistiche per CPU, dischi, rete e processi
Icon=btop
Exec=btop
Terminal=true
Categories=System;Monitor;ConsoleOnly;
Keywords=system;process;task

View file

@ -36,7 +36,7 @@
// see https://semver.org/
#define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes
#define ROBIN_HOOD_VERSION_MINOR 11 // for adding functionality in a backwards-compatible manner
#define ROBIN_HOOD_VERSION_PATCH 3 // for backwards-compatible bug fixes
#define ROBIN_HOOD_VERSION_PATCH 5 // for backwards-compatible bug fixes
#include <algorithm>
#include <cstdlib>
@ -1820,6 +1820,12 @@ public:
InsertionState::key_found != idxAndState.second);
}
template <typename... Args>
iterator emplace_hint(const_iterator position, Args&&... args) {
(void)position;
return emplace(std::forward<Args>(args)...).first;
}
template <typename... Args>
std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) {
return try_emplace_impl(key, std::forward<Args>(args)...);
@ -1831,16 +1837,15 @@ public:
}
template <typename... Args>
std::pair<iterator, bool> try_emplace(const_iterator hint, const key_type& key,
Args&&... args) {
iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args) {
(void)hint;
return try_emplace_impl(key, std::forward<Args>(args)...);
return try_emplace_impl(key, std::forward<Args>(args)...).first;
}
template <typename... Args>
std::pair<iterator, bool> try_emplace(const_iterator hint, key_type&& key, Args&&... args) {
iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) {
(void)hint;
return try_emplace_impl(std::move(key), std::forward<Args>(args)...);
return try_emplace_impl(std::move(key), std::forward<Args>(args)...).first;
}
template <typename Mapped>
@ -1854,16 +1859,15 @@ public:
}
template <typename Mapped>
std::pair<iterator, bool> insert_or_assign(const_iterator hint, const key_type& key,
Mapped&& obj) {
iterator insert_or_assign(const_iterator hint, const key_type& key, Mapped&& obj) {
(void)hint;
return insertOrAssignImpl(key, std::forward<Mapped>(obj));
return insertOrAssignImpl(key, std::forward<Mapped>(obj)).first;
}
template <typename Mapped>
std::pair<iterator, bool> insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) {
iterator insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) {
(void)hint;
return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj));
return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj)).first;
}
std::pair<iterator, bool> insert(const value_type& keyval) {
@ -1871,10 +1875,20 @@ public:
return emplace(keyval);
}
iterator insert(const_iterator hint, const value_type& keyval) {
(void)hint;
return emplace(keyval).first;
}
std::pair<iterator, bool> insert(value_type&& keyval) {
return emplace(std::move(keyval));
}
iterator insert(const_iterator hint, value_type&& keyval) {
(void)hint;
return emplace(std::move(keyval)).first;
}
// Returns 1 if key is found, 0 otherwise.
size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
ROBIN_HOOD_TRACE(this)
@ -2308,13 +2322,14 @@ private:
auto const numElementsWithBuffer = calcNumElementsWithBuffer(max_elements);
// calloc also zeroes everything
// malloc & zero mInfo. Faster than calloc everything.
auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer);
ROBIN_HOOD_LOG("std::calloc " << numBytesTotal << " = calcNumBytesTotal("
<< numElementsWithBuffer << ")")
mKeyVals = reinterpret_cast<Node*>(
detail::assertNotNull<std::bad_alloc>(std::calloc(1, numBytesTotal)));
detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal)));
mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer);
std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node));
// set sentinel
mInfo[numElementsWithBuffer] = 1;

1491
include/widechar_width.hpp Normal file

File diff suppressed because it is too large Load diff

11
snap/gui/btop.desktop Normal file
View file

@ -0,0 +1,11 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=btop++
GenericName=System Monitor
Comment=Resource monitor that shows usage and stats for processor, memory, disks, network and processes
Icon=${SNAP}/meta/gui/icon.svg
Exec=btop
Terminal=true
Categories=System;Monitor;ConsoleOnly;
Keywords=system;process;task

111
snap/gui/icon.svg Normal file
View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="90"
height="90"
version="1.1"
id="svg70"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs26">
<pattern
xlink:href="#gray-stripes"
id="pattern1888" />
<pattern
xlink:href="#red-stripes"
id="pattern1870" />
<pattern
id="red-stripes"
width="341"
height="90"
patternUnits="userSpaceOnUse">
<rect
width="341"
height="16"
fill="#f00"
id="rect2" />
<rect
y="16"
width="341"
height="16"
fill="#d70000"
id="rect4" />
<rect
y="32"
width="341"
height="16"
fill="#af0000"
id="rect6" />
<rect
y="48"
width="341"
height="16"
fill="#870000"
id="rect8" />
<rect
y="64"
width="341"
height="16"
fill="#5f0000"
id="rect10" />
</pattern>
<pattern
id="gray-stripes"
width="341"
height="90"
patternUnits="userSpaceOnUse">
<rect
width="341"
height="16"
fill="#585858"
id="rect13" />
<rect
y="16"
width="341"
height="16"
fill="#4e4e4e"
id="rect15" />
<rect
y="32"
width="341"
height="16"
fill="#444"
id="rect17" />
<rect
y="48"
width="341"
height="16"
fill="#3a3a3a"
id="rect19" />
<rect
y="64"
width="341"
height="16"
fill="#303030"
id="rect21" />
<rect
y="80"
width="341"
height="10"
fill="#262626"
id="rect23" />
</pattern>
</defs>
<g
id="g2074"
transform="translate(18)">
<path
d="M 2,7 V 90 H 47 V 74 h 7 V 55 H 47 V 42 h 7 V 23 H 47 V 7 Z M 19,26 H 37 V 39 H 19 Z m 0,32 H 37 V 71 H 19 Z"
id="path28"
style="fill:#080808" />
<path
d="M 2,7 V 90 H 47 V 74 h 7 V 55 H 47 V 42 h 7 V 23 H 47 V 7 Z m 1,1 h 43 v 16 h 7 v 17 h -7 v 15 h 7 V 73 H 46 V 89 H 3 Z M 18,25 H 38 V 40 H 18 Z m 1,1 V 39 H 37 V 26 Z M 18,57 H 38 V 72 H 18 Z m 1,1 V 71 H 37 V 58 Z M 4,9 V 88 H 45 V 72 h 7 V 57 H 45 V 40 h 7 V 25 H 45 V 9 Z m 1,1 h 39 v 16 h 7 v 13 h -7 v 19 h 7 V 71 H 44 V 87 H 5 Z M 16,23 H 40 V 42 H 16 Z m 1,1 V 41 H 39 V 24 Z M 16,55 H 40 V 74 H 16 Z m 1,1 V 73 H 39 V 56 Z"
id="path42"
style="fill:url(#pattern1888)" />
<path
d="M 0,0 V 80 H 42 V 64 h 7 V 48 H 42 V 32 h 7 V 16 H 42 V 0 Z M 14,16 H 35 V 32 H 14 Z m 0,32 H 35 V 64 H 14 Z"
id="path56"
style="fill:url(#pattern1870)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -4,7 +4,6 @@ summary: Resource monitor that shows usage and stats
description: |
Resource monitor that shows usage and stats for processor, memory, disks, network and processes.
C++ version and continuation of bashtop and bpytop.
license: Apache-2.0
base: core20
@ -26,6 +25,8 @@ package-repositories:
apps:
btop:
command: usr/local/bin/btop
extensions:
- gnome-3-38
environment:
LC_ALL: C.UTF-8
LANG: C.UTF-8
@ -38,6 +39,11 @@ apps:
- network-observe
- home
- removable-media
- desktop
- desktop-legacy
- x11
- wayland
- unity7
parts:
btop:

View file

@ -17,10 +17,12 @@ tab-size = 4
*/
#include <csignal>
#include <clocale>
#include <pthread.h>
#ifdef __FreeBSD__
#include <pthread_np.h>
#endif
#include <thread>
#include <future>
#include <bitset>
#include <numeric>
#include <ranges>
#include <unistd.h>
@ -30,6 +32,11 @@ tab-size = 4
#include <tuple>
#include <regex>
#include <chrono>
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <limits.h>
#endif
#include <btop_shared.hpp>
#include <btop_tools.hpp>
@ -40,7 +47,7 @@ tab-size = 4
#include <btop_menu.hpp>
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, std::future, std::async, std::bitset, std::future_status;
using std::string_literals::operator""s, std::to_string;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
@ -55,7 +62,7 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const string Version = "1.0.9";
const string Version = "1.2.8";
int coreCount;
string overlay;
@ -66,6 +73,7 @@ namespace Global {
string fg_green = "\x1b[1;92m";
string fg_red = "\x1b[0;91m";
uid_t real_uid, set_uid;
fs::path self_path;
@ -79,8 +87,9 @@ namespace Global {
uint64_t start_time;
atomic<bool> resized (false);
atomic<bool> resizing (false);
atomic<bool> quitting (false);
atomic<bool> should_quit (false);
atomic<bool> should_sleep (false);
atomic<bool> _runner_started (false);
bool arg_tty = false;
@ -88,7 +97,6 @@ namespace Global {
int arg_preset = -1;
}
//* A simple argument parser
void argumentParser(const int& argc, char **argv) {
for(int i = 1; i < argc; i++) {
@ -150,9 +158,14 @@ void argumentParser(const int& argc, char **argv) {
//* Handler for SIGWINCH and general resizing events, does nothing if terminal hasn't been resized unless force=true
void term_resize(bool force) {
if (Global::resizing) return;
atomic_lock lck(Global::resizing);
if (auto refreshed = Term::refresh(); refreshed or force) {
static atomic<bool> resizing (false);
if (Input::polling) {
Global::resized = true;
Input::interrupt = true;
return;
}
atomic_lock lck(resizing, true);
if (auto refreshed = Term::refresh(true); refreshed or force) {
if (force and refreshed) force = false;
}
else return;
@ -160,6 +173,7 @@ void term_resize(bool force) {
static const array<string, 4> all_boxes = {"cpu", "mem", "net", "proc"};
Global::resized = true;
if (Runner::active) Runner::stop();
Term::refresh();
Config::unlock();
auto boxes = Config::getS("shown_boxes");
@ -175,11 +189,12 @@ void term_resize(bool force) {
<< Mv::to((Term::height / 2) + 1, (Term::width / 2) - 12) << Global::fg_white
<< "Needed for current config:" << Mv::to((Term::height / 2) + 2, (Term::width / 2) - 10)
<< "Width = " << min_size.at(0) << " Height = " << min_size.at(1) << flush;
while (not Term::refresh() and not Input::poll()) sleep_ms(10);
if (Input::poll()) {
bool got_key = false;
for (; not Term::refresh() and not got_key; got_key = Input::poll(10));
if (got_key) {
auto key = Input::get();
if (key == "q")
exit(0);
clean_quit(0);
else if (is_in(key, "1", "2", "3", "4")) {
Config::current_preset = -1;
Config::toggle_box(all_boxes.at(std::stoi(key) - 1));
@ -199,19 +214,26 @@ void clean_quit(int sig) {
if (Global::quitting) return;
Global::quitting = true;
Runner::stop();
if (Global::_runner_started and pthread_join(Runner::runner_id, NULL) != 0) {
Logger::error("Failed to join _runner thread!");
if (Global::_runner_started) {
#ifdef __APPLE__
if (pthread_join(Runner::runner_id, NULL) != 0) {
Logger::warning("Failed to join _runner thread on exit!");
pthread_cancel(Runner::runner_id);
}
#else
struct timespec ts;
ts.tv_sec = 5;
if (pthread_timedjoin_np(Runner::runner_id, NULL, &ts) != 0) {
Logger::warning("Failed to join _runner thread on exit!");
pthread_cancel(Runner::runner_id);
}
#endif
}
Config::write();
Input::clear();
//? Wait for any remaining Tools::atomic_lock destructors to finish for max 1000ms
for (int i = 0; Tools::active_locks > 0 and i < 100; i++) {
sleep_ms(10);
}
if (Term::initialized) {
Input::clear();
Term::restore();
}
@ -222,12 +244,13 @@ void clean_quit(int sig) {
}
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
//? Assume error if still not cleaned up and call quick_exit to avoid a segfault from Tools::atomic_lock destructor
if (Tools::active_locks > 0) {
quick_exit((sig != -1 ? sig : 0));
}
const auto excode = (sig != -1 ? sig : 0);
if (sig != -1) exit(sig);
#ifdef __APPLE__
_Exit(excode);
#else
quick_exit(excode);
#endif
}
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
@ -250,10 +273,24 @@ void _exit_handler() {
void _signal_handler(const int sig) {
switch (sig) {
case SIGINT:
clean_quit(0);
if (Runner::active) {
Global::should_quit = true;
Runner::stopping = true;
Input::interrupt = true;
}
else {
clean_quit(0);
}
break;
case SIGTSTP:
_sleep();
if (Runner::active) {
Global::should_sleep = true;
Runner::stopping = true;
Input::interrupt = true;
}
else {
_sleep();
}
break;
case SIGCONT:
_resume();
@ -291,10 +328,22 @@ namespace Runner {
pthread_mutex_t& pt_mutex;
public:
int status;
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) { status = pthread_mutex_lock(&pt_mutex); }
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) { pthread_mutex_init(&pt_mutex, NULL); status = pthread_mutex_lock(&pt_mutex); }
~thread_lock() { if (status == 0) pthread_mutex_unlock(&pt_mutex); }
};
//* Wrapper for raising priviliges when using SUID bit
class gain_priv {
int status = -1;
public:
gain_priv() {
if (Global::real_uid != Global::set_uid) this->status = seteuid(Global::set_uid);
}
~gain_priv() {
if (status == 0) status = seteuid(Global::real_uid);
}
};
string output;
string empty_bg;
bool pause_output = false;
@ -302,20 +351,6 @@ namespace Runner {
pthread_t runner_id;
pthread_mutex_t mtx;
const unordered_flat_map<string, uint_fast8_t> box_bits = {
{"proc", 0b0000'0001},
{"net", 0b0000'0100},
{"mem", 0b0001'0000},
{"cpu", 0b0100'0000},
};
enum bit_pos {
proc_present, proc_running,
net_present, net_running,
mem_present, mem_running,
cpu_present, cpu_running
};
enum debug_actions {
collect_begin,
draw_begin,
@ -327,16 +362,11 @@ namespace Runner {
draw
};
const uint_fast8_t proc_done = 0b0000'0011;
const uint_fast8_t net_done = 0b0000'1100;
const uint_fast8_t mem_done = 0b0011'0000;
const uint_fast8_t cpu_done = 0b1100'0000;
string debug_bg;
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
struct runner_conf {
bitset<8> box_mask;
vector<string> boxes;
bool no_update;
bool force_redraw;
bool background_update;
@ -366,10 +396,10 @@ namespace Runner {
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
void * _runner(void * _) {
(void)_;
//? Block all 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);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTSTP);
// sigaddset(&mask, SIGINT);
// sigaddset(&mask, SIGTSTP);
sigaddset(&mask, SIGWINCH);
sigaddset(&mask, SIGTERM);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
@ -386,13 +416,24 @@ namespace Runner {
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
while (not Global::quitting) {
thread_wait();
atomic_wait_for(active, true, 5000);
if (active) {
Global::exit_error_msg = "Runner thread failed to get active lock!";
Global::thread_exception = true;
Input::interrupt = true;
stopping = true;
}
if (stopping or Global::resized) {
sleep_ms(1);
continue;
}
//? Atomic lock used for blocking non thread-safe actions in main thread
atomic_lock lck(active);
//? Set effective user if SUID bit is set
gain_priv powers{};
auto& conf = current_conf;
//! DEBUG stats
@ -404,128 +445,85 @@ namespace Runner {
output.clear();
//* Start collection functions for all boxes in async threads and draw in this thread when finished
//? Starting order below based on mean time to finish
//* Run collection and draw functions for all boxes
try {
future<Cpu::cpu_info&> cpu;
future<Mem::mem_info&> mem;
future<Net::net_info&> net;
future<vector<Proc::proc_info>&> proc;
//? CPU
if (v_contains(conf.boxes, "cpu")) {
try {
if (Global::debug) debug_timer("cpu", collect_begin);
//? Loop until all box flags present in bitmask have been zeroed
while (conf.box_mask.count() > 0) {
if (stopping) break;
//? Start collect
auto cpu = Cpu::collect(conf.no_update);
//? PROC
if (conf.box_mask.test(proc_present)) {
if (not conf.box_mask.test(proc_running)) {
if (Global::debug) debug_timer("proc", collect_begin);
if (Global::debug) debug_timer("cpu", draw_begin);
//? Start async collect
proc = async(Proc::collect, conf.no_update);
conf.box_mask.set(proc_running);
}
else if (not proc.valid())
throw std::runtime_error("Proc::collect() future not valid.");
//? Draw box
if (not pause_output) output += Cpu::draw(cpu, conf.force_redraw, conf.no_update);
else if (proc.wait_for(10us) == future_status::ready) {
try {
if (Global::debug) debug_timer("proc", draw_begin);
//? Draw box
if (not pause_output) output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("proc", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Proc:: -> " + (string)e.what());
}
conf.box_mask ^= proc_done;
}
if (Global::debug) debug_timer("cpu", draw_done);
}
//? NET
if (conf.box_mask.test(net_present)) {
if (not conf.box_mask.test(net_running)) {
if (Global::debug) debug_timer("net", collect_begin);
//? Start async collect
net = async(Net::collect, conf.no_update);
conf.box_mask.set(net_running);
}
else if (not net.valid())
throw std::runtime_error("Net::collect() future not valid.");
else if (net.wait_for(10us) == future_status::ready) {
try {
if (Global::debug) debug_timer("net", draw_begin);
//? Draw box
if (not pause_output) output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("net", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Net:: -> " + (string)e.what());
}
conf.box_mask ^= net_done;
}
catch (const std::exception& e) {
throw std::runtime_error("Cpu:: -> " + (string)e.what());
}
}
//? MEM
if (conf.box_mask.test(mem_present)) {
if (not conf.box_mask.test(mem_running)) {
if (Global::debug) debug_timer("mem", collect_begin);
//? MEM
if (v_contains(conf.boxes, "mem")) {
try {
if (Global::debug) debug_timer("mem", collect_begin);
//? Start async collect
mem = async(Mem::collect, conf.no_update);
conf.box_mask.set(mem_running);
}
else if (not mem.valid())
throw std::runtime_error("Mem::collect() future not valid.");
//? Start collect
auto mem = Mem::collect(conf.no_update);
else if (mem.wait_for(10us) == future_status::ready) {
try {
if (Global::debug) debug_timer("mem", draw_begin);
if (Global::debug) debug_timer("mem", draw_begin);
//? Draw box
if (not pause_output) output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
//? Draw box
if (not pause_output) output += Mem::draw(mem, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("mem", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Mem:: -> " + (string)e.what());
}
conf.box_mask ^= mem_done;
}
if (Global::debug) debug_timer("mem", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Mem:: -> " + (string)e.what());
}
}
//? CPU
if (conf.box_mask.test(cpu_present)) {
if (not conf.box_mask.test(cpu_running)) {
if (Global::debug) debug_timer("cpu", collect_begin);
//? NET
if (v_contains(conf.boxes, "net")) {
try {
if (Global::debug) debug_timer("net", collect_begin);
//? Start async collect
cpu = async(Cpu::collect, conf.no_update);
conf.box_mask.set(cpu_running);
}
else if (not cpu.valid())
throw std::runtime_error("Cpu::collect() future not valid.");
//? Start collect
auto net = Net::collect(conf.no_update);
else if (cpu.wait_for(10us) == future_status::ready) {
try {
if (Global::debug) debug_timer("cpu", draw_begin);
if (Global::debug) debug_timer("net", draw_begin);
//? Draw box
if (not pause_output) output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
//? Draw box
if (not pause_output) output += Net::draw(net, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("cpu", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Cpu:: -> " + (string)e.what());
}
conf.box_mask ^= cpu_done;
}
if (Global::debug) debug_timer("net", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Net:: -> " + (string)e.what());
}
}
//? PROC
if (v_contains(conf.boxes, "proc")) {
try {
if (Global::debug) debug_timer("proc", collect_begin);
//? Start collect
auto proc = Proc::collect(conf.no_update);
if (Global::debug) debug_timer("proc", draw_begin);
//? Draw box
if (not pause_output) output += Proc::draw(proc, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("proc", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Proc:: -> " + (string)e.what());
}
}
}
@ -581,15 +579,23 @@ namespace Runner {
<< Term::sync_end << flush;
}
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
pthread_exit(NULL);
}
//? ------------------------------------------ Secondary thread end -----------------------------------------------
//* 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) {
atomic_lock lck(waiting);
atomic_wait(active);
atomic_wait_for(active, true, 5000);
if (active) {
Logger::error("Stall in Runner thread, restarting!");
active = false;
// exit(1);
pthread_cancel(Runner::runner_id);
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
Global::exit_error_msg = "Failed to re-create _runner thread!";
clean_quit(1);
}
}
if (stopping or Global::resized) return;
if (box == "overlay") {
@ -602,21 +608,21 @@ namespace Runner {
Config::unlock();
Config::lock();
//? Setup bitmask for selected boxes and pass to _runner thread
bitset<8> box_mask;
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
box_mask |= box_bits.at(box);
}
current_conf = {box_mask, no_update, force_redraw, (not Config::getB("tty_mode") and Config::getB("background_update")), Global::overlay, Global::clock};
current_conf = {
(box == "all" ? Config::current_boxes : vector{box}),
no_update, force_redraw,
(not Config::getB("tty_mode") and Config::getB("background_update")),
Global::overlay,
Global::clock
};
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
thread_trigger();
//? Wait for _runner thread to be active before returning
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
atomic_wait_for(active, false, 10);
}
}
//* Stops any work being done in runner thread and checks for thread errors
@ -626,12 +632,23 @@ namespace Runner {
if (ret != EBUSY and not Global::quitting) {
if (active) active = false;
Global::exit_error_msg = "Runner thread died unexpectedly!";
exit(1);
clean_quit(1);
}
else if (ret == EBUSY) {
atomic_wait(active);
atomic_wait_for(active, true, 5000);
if (active) {
active = false;
if (Global::quitting) {
return;
}
else {
Global::exit_error_msg = "No response from Runner thread, quitting!";
clean_quit(1);
}
}
thread_trigger();
sleep_ms(1);
atomic_wait_for(active, false, 100);
atomic_wait_for(active, true, 100);
}
stopping = false;
}
@ -646,16 +663,20 @@ int main(int argc, char **argv) {
Global::start_time = time_s();
//? Save real and effective userid's and drop priviliges until needed if running with SUID bit set
Global::real_uid = getuid();
Global::set_uid = geteuid();
if (Global::real_uid != Global::set_uid) {
if (seteuid(Global::real_uid) != 0) {
Global::real_uid = Global::set_uid;
Global::exit_error_msg = "Failed to change effective user ID. Unset btop SUID bit to ensure security on this system. Quitting!";
clean_quit(1);
}
}
//? Call argument parser if launched with arguments
if (argc > 1) argumentParser(argc, argv);
//? Setup signal handlers for CTRL-C, CTRL-Z, resume and terminal resize
std::atexit(_exit_handler);
std::signal(SIGINT, _signal_handler);
std::signal(SIGTSTP, _signal_handler);
std::signal(SIGCONT, _signal_handler);
std::signal(SIGWINCH, _signal_handler);
//? Setup paths for config, log and user themes
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
if (std::getenv(env) != NULL and access(std::getenv(env), W_OK) != -1) {
@ -680,10 +701,17 @@ int main(int argc, char **argv) {
}
}
//? Try to find global btop theme path relative to binary path
#if defined(__linux__)
#ifdef __linux__
{ std::error_code ec;
Global::self_path = fs::read_symlink("/proc/self/exe", ec).remove_filename();
}
#elif __APPLE__
{
char buf [PATH_MAX];
uint32_t bufsize = PATH_MAX;
if(!_NSGetExecutablePath(buf, &bufsize))
Global::self_path = fs::path(buf).remove_filename();
}
#endif
if (std::error_code ec; not Global::self_path.empty()) {
Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec);
@ -718,44 +746,76 @@ int main(int argc, char **argv) {
}
//? Try to find and set a UTF-8 locale
if (bool found = false; std::setlocale(LC_ALL, NULL) == NULL or not str_to_upper(s_replace((string)std::setlocale(LC_ALL, NULL), "-", "")).ends_with("UTF8")) {
if (std::getenv("LANG") != NULL and str_to_upper(s_replace((string)std::getenv("LANG"), "-", "")).ends_with("UTF8")) {
if (std::setlocale(LC_ALL, std::getenv("LANG")) != NULL) {
found = true;
if (std::setlocale(LC_ALL, "") != NULL and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
and str_to_upper(s_replace((string)std::setlocale(LC_ALL, ""), "-", "")).ends_with("UTF8")) {
Logger::debug("Using locale " + (string)std::setlocale(LC_ALL, ""));
}
else {
string found;
bool set_failure = false;
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")) {
found = std::getenv(loc_env);
if (std::setlocale(LC_ALL, found.c_str()) == NULL) {
set_failure = true;
Logger::warning("Failed to set locale " + found + " continuing anyway.");
}
}
}
else {
if (setenv("LANG", "", 1) == 0) {
if (found.empty()) {
if (setenv("LC_ALL", "", 1) == 0 and setenv("LANG", "", 1) == 0) {
try {
if (const auto loc = std::locale("").name(); not loc.empty() and loc != "*") {
for (auto& l : ssplit(loc, ';')) {
if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
if (std::setlocale(LC_ALL, l.substr(l.find('=') + 1).c_str()) != NULL) {
found = true;
found = l.substr(l.find('=') + 1);
if (std::setlocale(LC_ALL, found.c_str()) != NULL) {
break;
}
break;
}
}
}
}
catch (...) { found = false; }
catch (...) { found.clear(); }
}
}
if (not found and Global::utf_force)
Logger::warning("No UTF-8 locale detected! Forcing start with --utf-force argument.");
else if (not found) {
Global::exit_error_msg = "No UTF-8 locale detected!\nUse --utf-force argument to force start if you're sure your terminal can handle it.";
exit(1);
#ifdef __APPLE__
if (found.empty()) {
CFLocaleRef cflocale = CFLocaleCopyCurrent();
CFStringRef id_value = (CFStringRef)CFLocaleGetValue(cflocale, kCFLocaleIdentifier);
auto loc_id = CFStringGetCStringPtr(id_value, kCFStringEncodingUTF8);
CFRelease(cflocale);
std::string cur_locale = (loc_id != nullptr ? loc_id : "");
if (cur_locale.empty()) {
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) {
Logger::debug("Setting LC_ALL=" + cur_locale + ".UTF-8");
}
else if(std::setlocale(LC_ALL, "en_US.UTF-8") != NULL) {
Logger::debug("Setting LC_ALL=en_US.UTF-8");
}
else {
Logger::warning("Failed to set macos locale, continuing anyway.");
}
}
else
Logger::debug("Setting LC_ALL=" + (string)std::setlocale(LC_ALL, NULL));
#else
if (found.empty() and Global::utf_force)
Logger::warning("No UTF-8 locale detected! Forcing start with --utf-force argument.");
else if (found.empty()) {
Global::exit_error_msg = "No UTF-8 locale detected!\nUse --utf-force argument to force start if you're sure your terminal can handle it.";
clean_quit(1);
}
#endif
else if (not set_failure)
Logger::debug("Setting LC_ALL=" + found);
}
//? Initialize terminal and set options
if (not Term::init()) {
Global::exit_error_msg = "No tty detected!\nbtop++ needs an interactive shell to run.";
exit(1);
clean_quit(1);
}
if (Term::current_tty != "unknown") Logger::info("Running on " + Term::current_tty);
@ -763,10 +823,25 @@ int main(int argc, char **argv) {
Config::set("tty_mode", true);
Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols");
}
#ifndef __APPLE__
else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) {
Config::set("tty_mode", true);
Logger::info("Real tty detected: setting 16 color mode and using tty friendly graph symbols");
}
#endif
//? Check for valid terminal dimensions
{
int t_count = 0;
while (Term::width <= 0 or Term::width > 10000 or Term::height <= 0 or Term::height > 10000) {
sleep_ms(10);
Term::refresh();
if (++t_count == 100) {
Global::exit_error_msg = "Failed to get size of terminal!";
clean_quit(1);
}
}
}
//? Platform dependent init and error check
try {
@ -774,18 +849,25 @@ int main(int argc, char **argv) {
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what();
exit(1);
clean_quit(1);
}
//? Update list of available themes and generate the selected theme
Theme::updateThemes();
Theme::setTheme();
//? Setup signal handlers for CTRL-C, CTRL-Z, resume and terminal resize
std::atexit(_exit_handler);
std::signal(SIGINT, _signal_handler);
std::signal(SIGTSTP, _signal_handler);
std::signal(SIGCONT, _signal_handler);
std::signal(SIGWINCH, _signal_handler);
//? Start runner thread
Runner::thread_sem_init();
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
Global::exit_error_msg = "Failed to create _runner thread!";
exit(1);
clean_quit(1);
}
else {
Global::_runner_started = true;
@ -808,8 +890,9 @@ int main(int argc, char **argv) {
}
//? Print out box outlines
Draw::calcSizes();
//? Print out box outlines
cout << Term::sync_start << Cpu::box << Mem::box << Net::box << Proc::box << Term::sync_end << flush;
@ -821,10 +904,12 @@ int main(int argc, char **argv) {
try {
while (not true not_eq not false) {
//? Check for exceptions in secondary thread and exit with fail signal if true
if (Global::thread_exception) exit(1);
if (Global::thread_exception) clean_quit(1);
else if (Global::should_quit) clean_quit(0);
else if (Global::should_sleep) { Global::should_sleep = false; _sleep(); }
//? Make sure terminal size hasn't changed (in case of SIGWINCH not working properly)
term_resize();
term_resize(Global::resized);
//? Trigger secondary thread to redraw if terminal has been resized
if (Global::resized) {
@ -833,7 +918,7 @@ int main(int argc, char **argv) {
Global::resized = false;
if (Menu::active) Menu::process();
else Runner::run("all", true, true);
atomic_wait(Runner::active);
atomic_wait_for(Runner::active, true, 1000);
}
//? Update clock if needed
@ -876,7 +961,7 @@ int main(int argc, char **argv) {
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
exit(1);
clean_quit(1);
}
}

View file

@ -50,10 +50,13 @@ namespace Config {
"#* Will force 16-color mode and TTY theme, set all graph symbols to \"tty\" and swap out other non tty friendly symbols."},
{"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 positons, G=graph symbol to use for box.\n"
"#* Use withespace \" \" as seprator between different 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"
"#* Use withespace \" \" as separator between different presets.\n"
"#* 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"
"#* Conflicting keys for h:\"help\" and k:\"kill\" is accessible while holding shift."},
{"rounded_corners", "#* Rounded corners on boxes, is ignored if TTY mode is ON."},
{"graph_symbol", "#* Default symbols to use for graph creation, \"braille\", \"block\" or \"tty\".\n"
@ -74,8 +77,8 @@ namespace Config {
{"update_ms", "#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs."},
{"proc_sorting", "#* Processes sorting, \"pid\" \"program\" \"arguments\" \"threads\" \"user\" \"memory\" \"cpu lazy\" \"cpu responsive\",\n"
"#* \"cpu lazy\" sorts top process over time (easier to follow), \"cpu responsive\" updates top process directly."},
{"proc_sorting", "#* Processes sorting, \"pid\" \"program\" \"arguments\" \"threads\" \"user\" \"memory\" \"cpu lazy\" \"cpu direct\",\n"
"#* \"cpu lazy\" sorts top process over time (easier to follow), \"cpu direct\" updates top process directly."},
{"proc_reversed", "#* Reverse sorting order, True or False."},
@ -89,10 +92,14 @@ namespace Config {
{"proc_mem_bytes", "#* Show process memory as bytes instead of percent."},
{"proc_cpu_graphs", "#* Show cpu graph for each process."},
{"proc_info_smaps", "#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)"},
{"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)."},
{"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."},
@ -120,6 +127,8 @@ namespace Config {
{"temp_scale", "#* Which temperature scale to use, available values: \"celsius\", \"fahrenheit\", \"kelvin\" and \"rankine\"."},
{"base_10_sizes", "#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024."},
{"show_cpu_freq", "#* Show CPU frequency."},
{"clock_format", "#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.\n"
@ -136,6 +145,8 @@ namespace Config {
{"mem_below_net", "#* Show mem box below net box instead of above."},
{"zfs_arc_cached", "#* Count ZFS ARC in cached and available memory."},
{"show_swap", "#* If swap memory should be shown in memory box."},
{"swap_disk", "#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk."},
@ -146,6 +157,10 @@ namespace Config {
{"use_fstab", "#* Read disks list from /etc/fstab. This also disables only_physical."},
{"zfs_hide_datasets", "#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)"},
{"disk_free_priv", "#* Set to true to show available disk space for privileged users."},
{"show_io_stat", "#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view."},
{"io_mode", "#* Toggles io mode for disks, showing big graphs for disk read/write speeds."},
@ -167,6 +182,8 @@ namespace Config {
{"show_battery", "#* Show battery stats in top right if battery is present."},
{"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"
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."}
};
@ -184,6 +201,7 @@ namespace Config {
{"cpu_graph_upper", "total"},
{"cpu_graph_lower", "total"},
{"cpu_sensor", "Auto"},
{"selected_battery", "Auto"},
{"cpu_core_map", ""},
{"temp_scale", "celsius"},
{"clock_format", "%X"},
@ -206,10 +224,12 @@ namespace Config {
{"proc_tree", false},
{"proc_colors", true},
{"proc_gradient", true},
{"proc_per_core", true},
{"proc_per_core", false},
{"proc_mem_bytes", true},
{"proc_cpu_graphs", true},
{"proc_info_smaps", false},
{"proc_left", false},
{"proc_filter_kernel", false},
{"cpu_invert_lower", true},
{"cpu_single_graph", false},
{"cpu_bottom", false},
@ -220,18 +240,23 @@ namespace Config {
{"background_update", true},
{"mem_graphs", true},
{"mem_below_net", false},
{"zfs_arc_cached", true},
{"show_swap", true},
{"swap_disk", true},
{"show_disks", true},
{"only_physical", true},
{"use_fstab", true},
{"zfs_hide_datasets", false},
{"show_io_stat", true},
{"io_mode", false},
{"base_10_sizes", false},
{"io_graph_combined", false},
{"net_auto", true},
{"net_sync", false},
{"net_sync", true},
{"show_battery", true},
{"vim_keys", false},
{"tty_mode", false},
{"disk_free_priv", false},
{"force_tty", false},
{"lowcolor", false},
{"show_detailed", false},
@ -245,6 +270,7 @@ namespace Config {
{"net_upload", 100},
{"detailed_pid", 0},
{"selected_pid", 0},
{"selected_depth", 0},
{"proc_start", 0},
{"proc_selected", 0},
{"proc_last_selected", 0},
@ -252,7 +278,7 @@ namespace Config {
unordered_flat_map<string, int> intsTmp;
bool _locked(const string& name) {
atomic_wait(writelock);
atomic_wait(writelock, true);
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
write_new = true;
return locked.load();
@ -261,6 +287,8 @@ namespace Config {
fs::path conf_dir;
fs::path conf_file;
vector<string> available_batteries = {"Auto"};
vector<string> current_boxes;
vector<string> preset_list = {"cpu:0:default,mem:0:default,net:0:default,proc:0:default"};
int current_preset = -1;
@ -444,13 +472,14 @@ namespace Config {
void unlock() {
if (not locked) return;
atomic_wait(Runner::active);
atomic_lock lck(writelock);
atomic_lock lck(writelock, true);
try {
if (Proc::shown) {
ints.at("selected_pid") = Proc::selected_pid;
strings.at("selected_name") = Proc::selected_name;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
ints.at("selected_depth") = Proc::selected_depth;
}
for (auto& item : stringsTmp) {
@ -470,7 +499,7 @@ namespace Config {
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception during Config::unlock() : " + (string)e.what();
exit(1);
clean_quit(1);
}
locked = false;
@ -521,9 +550,7 @@ namespace Config {
vector<string> valid_names;
for (auto &n : descriptions)
valid_names.push_back(n[0]);
string v_string;
getline(cread, v_string, '\n');
if (not s_contains(v_string, Global::Version))
if (string v_string; cread.peek() != '#' or (getline(cread, v_string, '\n') and not s_contains(v_string, Global::Version)))
write_new = true;
while (not cread.eof()) {
cread >> std::ws;
@ -580,6 +607,7 @@ namespace Config {
void write() {
if (conf_file.empty() or not write_new) return;
Logger::debug("Writing new config file");
if (geteuid() != Global::real_uid and seteuid(Global::real_uid) != 0) return;
std::ofstream cwrite(conf_file, std::ios::trunc);
if (cwrite.good()) {
cwrite << "#? Config file for btop v. " << Global::Version;
@ -595,4 +623,4 @@ namespace Config {
}
}
}
}
}

View file

@ -45,9 +45,10 @@ namespace Config {
extern vector<string> current_boxes;
extern vector<string> preset_list;
extern vector<string> available_batteries;
extern int current_preset;
//* Check if string only contains space seperated valid names for boxes
//* Check if string only contains space separated valid names for boxes
bool check_boxes(const string& boxes);
//* Toggle box and update config string shown_boxes

View file

@ -43,42 +43,42 @@ namespace Symbols {
const unordered_flat_map<string, vector<string>> graph_symbols = {
{ "braille_up", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", ""
}},
{"braille_down", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", ""
}},
{"block_up", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", ""
}},
{"block_down", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", ""
}},
{"tty_up", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", ""
}},
{"tty_down", {
"", "", "", "", "",
" ", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
@ -111,7 +111,7 @@ namespace Draw {
}
for (size_t i = 0; i < line[1].size(); i += 3) {
if (line[1][i] == ' ') {
letter = ' ';
letter = Mv::r(1);
i -= 2;
}
else
@ -388,15 +388,22 @@ namespace Draw {
}
}
//? Generate graph symbol from 5x5 2D vector
graphs.at(current).at(horizon) += (height == 1 and result.at(0) + result.at(1) == 0) ? Mv::r(1) : graph_symbol.at((result.at(0) * 5 + result.at(1)));
if (height == 1) {
if (result.at(0) + result.at(1) == 0) graphs.at(current).at(horizon) += Mv::r(1);
else {
if (not color_gradient.empty()) graphs.at(current).at(horizon) += Theme::g(color_gradient).at(clamp(max(last, data_value), 0ll, 100ll));
graphs.at(current).at(horizon) += graph_symbol.at((result.at(0) * 5 + result.at(1)));
}
}
else graphs.at(current).at(horizon) += graph_symbol.at((result.at(0) * 5 + result.at(1)));
}
if (mult and i >= 0) last = data_value;
}
last = data_value;
out.clear();
if (height == 1) {
if (not color_gradient.empty())
out += (last < 1 and not color_gradient.empty() ? Theme::c("inactive_fg") : Theme::g(color_gradient).at(clamp(last, 0ll, 100ll)));
//if (not color_gradient.empty())
// out += (last < 1 ? Theme::c("inactive_fg") : Theme::g(color_gradient).at(clamp(last, 0ll, 100ll)));
out += graphs.at(current).at(0);
}
else {
@ -431,7 +438,7 @@ namespace Draw {
//? Populate the two switching graph vectors and fill empty space if data size < width
for (const int& i : iota(0, height * 2)) {
if (tty_mode and i % 2 != current) continue;
graphs[(i % 2 != 0)].push_back((value_width < width) ? ((height == 1) ? Mv::r(1) : ""s) * (width - value_width) : "");
graphs[(i % 2 != 0)].push_back((value_width < width) ? ((height == 1) ? Mv::r(1) : " "s) * (width - value_width) : "");
}
if (data.size() == 0) return;
this->_create(data, data_offset);
@ -443,7 +450,11 @@ namespace Draw {
//? Make room for new characters on graph
if (not tty_mode) current = not current;
for (const int& i : iota(0, height)) {
if (graphs.at(current).at(i).at(1) == '[') graphs.at(current).at(i).erase(0, 4);
if (height == 1 and graphs.at(current).at(i).at(1) == '[') {
if (graphs.at(current).at(i).at(3) == 'C') graphs.at(current).at(i).erase(0, 4);
else graphs.at(current).at(i).erase(0, graphs.at(current).at(i).find_first_of('m') + 4);
}
else if (graphs.at(current).at(i).at(0) == ' ') graphs.at(current).at(i).erase(0, 1);
else graphs.at(current).at(i).erase(0, 3);
}
this->_create(data, (int)data.size() - 1);
@ -472,21 +483,6 @@ namespace Cpu {
vector<Draw::Graph> core_graphs;
vector<Draw::Graph> temp_graphs;
unsigned long fastrand(void) {
static unsigned long x=123456789, y=362436069, z=521288629;
unsigned long t;
x ^= x << 16;
x ^= x >> 5;
x ^= x << 1;
t = x;
x = y;
y = z;
z = t ^ x ^ y;
return z;
}
string draw(const cpu_info& cpu, const bool force_redraw, const bool data_same) {
if (Runner::stopping) return "";
if (force_redraw) redraw = true;
@ -504,6 +500,7 @@ namespace Cpu {
const string& title_left = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
const string& title_right = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
static int bat_pos = 0, bat_len = 0;
if (cpu.cpu_percent.at("total").empty() or cpu.core_percent.at(0).empty() or (show_temps and cpu.temp.at(0).empty())) return "";
string out;
out.reserve(width * height);
@ -541,15 +538,15 @@ namespace Cpu {
if (b_column_size > 0 or extra_width > 0) {
core_graphs.clear();
for (const auto& core_data : cpu.core_percent) {
core_graphs.emplace_back(5 * b_column_size + extra_width, 1, "", core_data, graph_symbol);
core_graphs.emplace_back(5 * b_column_size + extra_width, 1, "cpu", core_data, graph_symbol);
}
}
if (show_temps) {
temp_graphs.clear();
temp_graphs.emplace_back(5, 1, "", cpu.temp.at(0), graph_symbol, false, false, cpu.temp_max, -23);
temp_graphs.emplace_back(5, 1, "temp", cpu.temp.at(0), graph_symbol, false, false, cpu.temp_max, -23);
if (not hide_cores and b_column_size > 1) {
for (const auto& i : iota((size_t)1, cpu.temp.size())) {
temp_graphs.emplace_back(5, 1, "", cpu.temp.at(i), graph_symbol, false, false, cpu.temp_max, -23);
temp_graphs.emplace_back(5, 1, "temp", cpu.temp.at(i), graph_symbol, false, false, cpu.temp_max, -23);
}
}
}
@ -587,7 +584,7 @@ namespace Cpu {
out += Mv::to(y, bat_pos) + title_left + Theme::c("title") + Fx::b + "BAT" + bat_symbol + ' ' + str_percent
+ (Term::width >= 100 ? Fx::ub + ' ' + bat_meter(percent) + Fx::b : "")
+ (not str_time.empty() ? ' ' + Theme::c("title") + str_time : "") + Fx::ub + title_right;
+ (not str_time.empty() ? ' ' + Theme::c("title") + str_time : " ") + Fx::ub + title_right;
}
}
else if (bat_pos > 0) {
@ -639,16 +636,16 @@ namespace Cpu {
+ ljust(to_string(n), core_width);
if (b_column_size > 0 or extra_width > 0)
out += Theme::c("inactive_fg") + graph_bg * (5 * b_column_size + extra_width) + Mv::l(5 * b_column_size + extra_width)
+ Theme::g("cpu").at(clamp(cpu.core_percent.at(n).back(), 0ll, 100ll)) + core_graphs.at(n)(cpu.core_percent.at(n), data_same or redraw);
else
out += Theme::g("cpu").at(clamp(cpu.core_percent.at(n).back(), 0ll, 100ll));
+ core_graphs.at(n)(cpu.core_percent.at(n), data_same or redraw);
out += Theme::g("cpu").at(clamp(cpu.core_percent.at(n).back(), 0ll, 100ll));
out += rjust(to_string(cpu.core_percent.at(n).back()), (b_column_size < 2 ? 3 : 4)) + Theme::c("main_fg") + '%';
if (show_temps and not hide_cores) {
const auto [temp, unit] = celsius_to(cpu.temp.at(n+1).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(cpu.temp.at(n+1).back() * 100 / cpu.temp_max, 0ll, 100ll));
if (b_column_size > 1)
out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5) + temp_color
out += ' ' + Theme::c("inactive_fg") + graph_bg * 5 + Mv::l(5)
+ temp_graphs.at(n+1)(cpu.temp.at(n+1), data_same or redraw);
out += temp_color + rjust(to_string(temp), 4) + Theme::c("main_fg") + unit;
}
@ -711,6 +708,7 @@ namespace Mem {
auto& tty_mode = Config::getB("tty_mode");
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_mem"));
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
auto totalMem = Mem::get_totalMem();
string out;
out.reserve(height * width);
@ -765,7 +763,7 @@ namespace Mem {
for (const auto& [name, disk] : mem.disks) {
if (disk.io_read.empty()) continue;
io_graphs[name + "_activity"] = Draw::Graph{disks_width - 6, 1, "", disk.io_activity, graph_symbol};
io_graphs[name + "_activity"] = Draw::Graph{disks_width - 6, 1, "available", disk.io_activity, graph_symbol};
if (io_mode) {
//? Create one combined graph for IO read/write if enabled
@ -804,7 +802,7 @@ namespace Mem {
string up = (graph_height >= 2 ? Mv::l(mem_width - 2) + Mv::u(graph_height - 1) : "");
bool big_mem = mem_width > 21;
out += Mv::to(y + 1, x + 2) + Theme::c("title") + Fx::b + "Total:" + rjust(floating_humanizer(Shared::totalMem), mem_width - 9) + Fx::ub + Theme::c("main_fg");
out += Mv::to(y + 1, x + 2) + Theme::c("title") + Fx::b + "Total:" + rjust(floating_humanizer(totalMem), mem_width - 9) + Fx::ub + Theme::c("main_fg");
vector<string> comb_names (mem_names.begin(), mem_names.end());
if (show_swap and has_swap and not swap_disk) comb_names.insert(comb_names.end(), swap_names.begin(), swap_names.end());
for (auto name : comb_names) {
@ -828,7 +826,7 @@ namespace Mem {
const string humanized = floating_humanizer(mem.stats.at(name));
const string graphics = (use_graphs ? mem_graphs.at(name)(mem.percent.at(name), redraw or data_same) : mem_meters.at(name)(mem.percent.at(name).back()));
if (mem_size > 2) {
out += Mv::to(y+1+cy, x+1+cx) + divider + ljust(title, 4, false, false, not big_mem) + ljust(":", (big_mem ? 1 : 6))
out += Mv::to(y+1+cy, x+1+cx) + divider + title.substr(0, big_mem ? 10 : 5) + ":"
+ Mv::to(y+1+cy, x+cx + mem_width - 2 - humanized.size()) + trans(humanized)
+ Mv::to(y+2+cy, x+cx + (graph_height >= 2 ? 0 : 1)) + graphics + up + rjust(to_string(mem.percent.at(name).back()) + "%", 4);
cy += (graph_height == 0 ? 2 : graph_height + 1);
@ -848,6 +846,7 @@ namespace Mem {
cx = mem_width; cy = 0;
const bool big_disk = disks_width >= 25;
divider = Mv::l(1) + Theme::c("div_line") + Symbols::div_left + Symbols::h_line * disks_width + Theme::c("mem_box") + Fx::ub + Symbols::div_right + Mv::l(disks_width);
const string hu_div = Theme::c("div_line") + Symbols::h_line + Theme::c("main_fg");
if (io_mode) {
for (const auto& mount : mem.disks_order) {
if (not disks.contains(mount)) continue;
@ -855,13 +854,13 @@ namespace Mem {
const auto& disk = disks.at(mount);
if (disk.io_read.empty()) continue;
const string total = floating_humanizer(disk.total, not big_disk);
out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 2) + Mv::to(y+1+cy, x+cx + disks_width - total.size())
out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 8) + Mv::to(y+1+cy, x+cx + disks_width - total.size())
+ trans(total) + Fx::ub;
if (big_disk) {
const string used_percent = to_string(disk.used_percent);
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2)) + Theme::c("main_fg") + used_percent + '%';
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2) - 1) + hu_div + used_percent + '%' + hu_div;
}
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6) + Theme::g("available").at(clamp(disk.io_activity.back(), 50ll, 100ll))
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6)
+ Mv::l(disks_width - 6) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same) + Theme::c("main_fg");
if (++cy > height - 3) break;
if (io_graph_combined) {
@ -890,21 +889,21 @@ namespace Mem {
if (cy > height - 3) break;
const auto& disk = disks.at(mount);
auto comb_val = (not disk.io_read.empty() ? disk.io_read.back() + disk.io_write.back() : 0ll);
const string human_io = (comb_val > 0 and big_disk ? (disk.io_write.back() > 0 ? ""s : ""s) + (disk.io_read.back() > 0 ? ""s : ""s)
const string human_io = (comb_val > 0 ? (disk.io_write.back() > 0 and big_disk ? ""s : ""s) + (disk.io_read.back() > 0 and big_disk ? ""s : ""s)
+ floating_humanizer(comb_val, true) : "");
const string human_total = floating_humanizer(disk.total, not big_disk);
const string human_used = floating_humanizer(disk.used, not big_disk);
const string human_free = floating_humanizer(disk.free, not big_disk);
out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 2) + Mv::to(y+1+cy, x+cx + disks_width - human_total.size())
out += Mv::to(y+1+cy, x+1+cx) + divider + Theme::c("title") + Fx::b + uresize(disk.name, disks_width - 8) + Mv::to(y+1+cy, x+cx + disks_width - human_total.size())
+ trans(human_total) + Fx::ub + Theme::c("main_fg");
if (big_disk and not human_io.empty())
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)human_io.size() / 2)) + Theme::c("main_fg") + human_io;
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)human_io.size() / 2) - 1) + hu_div + human_io + hu_div;
if (++cy > height - 3) break;
if (show_io_stat and io_graphs.contains(mount + "_activity")) {
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + Theme::c("inactive_fg") + graph_bg * (disks_width - 6) + Theme::g("available").at(clamp(disk.io_activity.back(), 50ll, 100ll))
+ Mv::l(disks_width - 6) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same) + Theme::c("main_fg");
if (not big_disk) out += Mv::to(y+1+cy, x+cx) + Theme::c("main_fg") + human_io;
if (not big_disk) out += Mv::to(y+1+cy, x+cx+1) + Theme::c("main_fg") + human_io;
if (++cy > height - 3) break;
}
@ -959,8 +958,8 @@ namespace Net {
const string title_left = Theme::c("net_box") + Fx::ub + Symbols::title_left;
const string title_right = Theme::c("net_box") + Fx::ub + Symbols::title_right;
const int i_size = min((int)selected_iface.size(), 10);
const long long down_max = (net_auto ? graph_max.at("download") : (long long)(Config::getI("net_download") << 20) / 8);
const long long up_max = (net_auto ? graph_max.at("upload") : (long long)(Config::getI("net_upload") << 20) / 8);
const long long down_max = (net_auto ? graph_max.at("download") : ((long long)(Config::getI("net_download")) << 20) / 8);
const long long up_max = (net_auto ? graph_max.at("upload") : ((long long)(Config::getI("net_upload")) << 20) / 8);
//* Redraw elements not needed to be updated every cycle
if (redraw) {
@ -974,13 +973,13 @@ namespace Net {
//? Interface selector and buttons
out += Mv::to(y, x+width - i_size - 10) + title_left + Fx::b + Theme::c("hi_fg") + "<b " + Theme::c("title")
out += Mv::to(y, x+width - i_size - 9) + title_left + Fx::b + Theme::c("hi_fg") + "<b " + Theme::c("title")
+ uresize(selected_iface, 10) + Theme::c("hi_fg") + " n>" + title_right
+ Mv::to(y, x+width - i_size - 16) + title_left + Theme::c("hi_fg") + (net.stat.at("download").offset + net.stat.at("upload").offset > 0 ? Fx::b : "") + 'z'
+ Mv::to(y, x+width - i_size - 15) + title_left + Theme::c("hi_fg") + (net.stat.at("download").offset + net.stat.at("upload").offset > 0 ? Fx::b : "") + 'z'
+ Theme::c("title") + "ero" + title_right;
Input::mouse_mappings["b"] = {y, x+width - i_size - 9, 1, 3};
Input::mouse_mappings["b"] = {y, x+width - i_size - 8, 1, 3};
Input::mouse_mappings["n"] = {y, x+width - 6, 1, 3};
Input::mouse_mappings["z"] = {y, x+width - i_size - 15, 1, 4};
Input::mouse_mappings["z"] = {y, x+width - i_size - 14, 1, 4};
if (width - i_size - 20 > 6) {
out += Mv::to(y, x+width - i_size - 21) + title_left + Theme::c("hi_fg") + (net_auto ? Fx::b : "") + 'a' + Theme::c("title") + "uto" + title_right;
Input::mouse_mappings["a"] = {y, x+width - i_size - 20, 1, 4};
@ -1033,9 +1032,10 @@ namespace Proc {
int x, y, width = 20, height;
int start, selected, select_max;
bool shown = true, redraw = true;
int selected_pid = 0;
int selected_pid = 0, selected_depth = 0;
string selected_name;
unordered_flat_map<size_t, Draw::Graph> p_graphs;
unordered_flat_map<size_t, bool> p_wide_cmd;
unordered_flat_map<size_t, int> p_counters;
int counter = 0;
Draw::TextEdit filter;
@ -1051,9 +1051,10 @@ namespace Proc {
auto selected = Config::getI("proc_selected");
auto last_selected = Config::getI("proc_last_selected");
const int select_max = (Config::getB("show_detailed") ? Proc::select_max - 8 : Proc::select_max);
auto& vim_keys = Config::getB("vim_keys");
int numpids = Proc::numpids;
if (cmd_key == "up" and selected > 0) {
if ((cmd_key == "up" or (vim_keys and cmd_key == "k")) and selected > 0) {
if (start > 0 and selected == 1) start--;
else selected--;
if (Config::getI("proc_last_selected") > 0) Config::set("proc_last_selected", 0);
@ -1064,7 +1065,7 @@ namespace Proc {
else if (cmd_key == "mouse_scroll_down" and start < numpids - select_max) {
start = min(numpids - select_max, start + 3);
}
else if (cmd_key == "down") {
else if (cmd_key == "down" or (vim_keys and cmd_key == "j")) {
if (start < numpids - select_max and selected == select_max) start++;
else if (selected == 0 and last_selected > 0) {
selected = last_selected;
@ -1080,11 +1081,11 @@ namespace Proc {
if (selected > 0 and start >= numpids - select_max) selected = select_max;
else start = clamp(start + select_max, 0, max(0, numpids - select_max));
}
else if (cmd_key == "home") {
else if (cmd_key == "home" or (vim_keys and cmd_key == "g")) {
start = 0;
if (selected > 0) selected = 1;
}
else if (cmd_key == "end") {
else if (cmd_key == "end" or (vim_keys and cmd_key == "G")) {
start = max(0, numpids - select_max);
if (selected > 0) selected = select_max;
}
@ -1115,11 +1116,14 @@ namespace Proc {
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_proc"));
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
auto& mem_bytes = Config::getB("proc_mem_bytes");
auto& vim_keys = Config::getB("vim_keys");
auto& show_graphs = Config::getB("proc_cpu_graphs");
start = Config::getI("proc_start");
selected = Config::getI("proc_selected");
const int y = show_detailed ? Proc::y + 8 : Proc::y;
const int height = show_detailed ? Proc::height - 8 : Proc::height;
const int select_max = show_detailed ? Proc::select_max - 8 : Proc::select_max;
auto totalMem = Mem::get_totalMem();
int numpids = Proc::numpids;
if (force_redraw) redraw = true;
string out;
@ -1141,6 +1145,10 @@ namespace Proc {
prog_size = (width > 70 ? 16 : ( width > 55 ? 8 : width - user_size - thread_size - 33));
cmd_size = (width > 55 ? width - prog_size - user_size - thread_size - 33 : -1);
tree_size = width - user_size - thread_size - 23;
if (not show_graphs) {
cmd_size += 5;
tree_size += 5;
}
//? Detailed box
if (show_detailed) {
@ -1178,7 +1186,7 @@ namespace Proc {
if (alive and selected == 0) Input::mouse_mappings["t"] = {d_y, mouse_x, 1, 9};
mouse_x += 11;
}
out += title_left + hi_color + Fx::b + 'k' + t_color + "ill" + Fx::ub + title_right
out += title_left + hi_color + Fx::b + (vim_keys ? 'K' : 'k') + t_color + "ill" + Fx::ub + title_right
+ title_left + hi_color + Fx::b + 's' + t_color + "ignals" + Fx::ub + title_right
+ Mv::to(d_y, d_x + d_width - 10) + title_left + t_color + Fx::b + hide + Symbols::enter + Fx::ub + title_right;
if (alive and selected == 0) {
@ -1271,7 +1279,7 @@ namespace Proc {
mouse_x += 11;
}
if (width > 55) {
out += title_left_down + Fx::b + hi_color + 'k' + t_color + "ill" + Fx::ub + title_right_down;
out += title_left_down + Fx::b + hi_color + (vim_keys ? 'K' : 'k') + t_color + "ill" + Fx::ub + title_right_down;
if (selected > 0) Input::mouse_mappings["k"] = {y + height - 1, mouse_x, 1, 4};
mouse_x += 6;
}
@ -1291,7 +1299,7 @@ namespace Proc {
out += (thread_size > 0 ? Mv::l(4) + "Threads: " : "")
+ ljust("User:", user_size) + ' '
+ rjust((mem_bytes ? "MemB" : "Mem%"), 5) + ' '
+ rjust("Cpu%", 10) + Fx::ub;
+ rjust("Cpu%", (show_graphs ? 10 : 5)) + Fx::ub;
}
//* End of redraw block
@ -1325,7 +1333,7 @@ namespace Proc {
if (item_fit >= 8) out += cjust(to_string(detailed.entry.p_nice), item_width);
const double mem_p = (double)detailed.mem_bytes.back() * 100 / Shared::totalMem;
const double mem_p = (double)detailed.mem_bytes.back() * 100 / totalMem;
string mem_str = to_string(mem_p);
mem_str.resize((mem_p < 10 or mem_p >= 100 ? 3 : 4));
out += Mv::to(d_y + 4, d_x + 1) + Theme::c("title") + Fx::b + rjust((item_fit > 4 ? "Memory: " : "M:") + mem_str + "% ", (d_width / 3) - 2)
@ -1352,11 +1360,12 @@ namespace Proc {
if (is_selected) {
selected_pid = (int)p.pid;
selected_name = p.name;
selected_depth = p.depth;
}
//? Update graphs for processes with above 0.0% cpu usage, delete if below 0.1% 10x times
const bool has_graph = p_counters.contains(p.pid);
if ((p.cpu_p > 0 and not has_graph) or (not data_same and has_graph)) {
const bool has_graph = show_graphs ? p_counters.contains(p.pid) : false;
if (show_graphs and ((p.cpu_p > 0 and not has_graph) or (not data_same and has_graph))) {
if (not has_graph) {
p_graphs[p.pid] = Draw::Graph{5, 1, "", {}, graph_symbol};
p_counters[p.pid] = 0;
@ -1383,7 +1392,7 @@ namespace Proc {
if (proc_colors) {
end = Theme::c("main_fg") + Fx::ub;
array<string, 3> colors;
for (int i = 0; int v : {(int)round(p.cpu_p), (int)round(p.mem * 100 / Shared::totalMem), (int)p.threads / 3}) {
for (int i = 0; int v : {(int)round(p.cpu_p), (int)round(p.mem * 100 / totalMem), (int)p.threads / 3}) {
if (proc_gradient) {
int val = (min(v, 100) + 100) - calc * 100 / select_max;
if (val < 100) colors[i++] = Theme::g("proc_color").at(max(0, val));
@ -1403,12 +1412,14 @@ namespace Proc {
}
}
if (not p_wide_cmd.contains(p.pid)) p_wide_cmd[p.pid] = ulen(p.cmd) != ulen(p.cmd, true);
//? Normal view line
if (not proc_tree) {
out += Mv::to(y+2+lc, x+1)
+ g_color + rjust(to_string(p.pid), 8) + ' '
+ c_color + ljust(p.name, prog_size, true) + ' ' + end
+ (cmd_size > 0 ? g_color + ljust(p.cmd, cmd_size, true, true) + ' ' : "");
+ (cmd_size > 0 ? g_color + ljust(p.cmd, cmd_size, true, p_wide_cmd[p.pid]) + Mv::to(y+2+lc, x+11+prog_size+cmd_size) + ' ' : "");
}
//? Tree view line
else {
@ -1421,17 +1432,23 @@ namespace Proc {
width_left -= (ulen(p.name) + 1);
}
if (width_left > 7 and p.short_cmd != p.name) {
out += g_color + '(' + uresize(p.short_cmd, width_left - 3, true) + ") ";
out += g_color + '(' + uresize(p.short_cmd, width_left - 3, p_wide_cmd[p.pid]) + ") ";
width_left -= (ulen(p.short_cmd, true) + 3);
}
out += string(max(0, width_left), ' ');
out += string(max(0, width_left), ' ') + Mv::to(y+2+lc, x+2+tree_size);
}
//? Common end of line
string cpu_str = to_string(p.cpu_p);
if (p.cpu_p < 10 or p.cpu_p >= 100) cpu_str.resize(3);
if (p.cpu_p < 10 or (p.cpu_p >= 100 and p.cpu_p < 1000)) cpu_str.resize(3);
else if (p.cpu_p >= 10'000) {
cpu_str = to_string(p.cpu_p / 1000);
cpu_str.resize(3);
if (cpu_str.ends_with('.')) cpu_str.pop_back();
cpu_str += "k";
}
string mem_str = (mem_bytes ? floating_humanizer(p.mem, true) : "");
if (not mem_bytes) {
double mem_p = clamp((double)p.mem * 100 / Shared::totalMem, 0.0, 100.0);
double mem_p = clamp((double)p.mem * 100 / totalMem, 0.0, 100.0);
mem_str = to_string(mem_p);
if (mem_str.size() < 4) mem_str = "0";
else mem_str.resize((mem_p < 10 or mem_p >= 100 ? 3 : 4));
@ -1440,7 +1457,7 @@ namespace Proc {
out += (thread_size > 0 ? t_color + rjust(to_string(min(p.threads, (size_t)9999)), thread_size) + ' ' + end : "" )
+ g_color + ljust((cmp_greater(p.user.size(), user_size) ? p.user.substr(0, user_size - 1) + '+' : p.user), user_size) + ' '
+ m_color + rjust(mem_str, 5) + end + ' '
+ (is_selected ? "" : Theme::c("inactive_fg")) + graph_bg * 5
+ (is_selected ? "" : Theme::c("inactive_fg")) + (show_graphs ? graph_bg * 5: "")
+ (p_graphs.contains(p.pid) ? Mv::l(5) + c_color + p_graphs.at(p.pid)({(p.cpu_p >= 0.1 and p.cpu_p < 5 ? 5ll : (long long)round(p.cpu_p))}, data_same) : "") + end + ' '
+ c_color + rjust(cpu_str, 4) + " " + end;
if (lc++ > height - 5) break;
@ -1476,6 +1493,15 @@ namespace Proc {
}
p_graphs.compact();
p_counters.compact();
for (auto element = p_wide_cmd.begin(); element != p_wide_cmd.end();) {
if (rng::find(plist, element->first, &proc_info::pid) == plist.end()) {
element = p_wide_cmd.erase(element);
}
else
++element;
}
p_wide_cmd.compact();
}
if (selected == 0 and selected_pid != 0) {
@ -1653,4 +1679,4 @@ namespace Draw {
box = createBox(x, y, width, height, Theme::c("proc_box"), true, "proc", "", 4);
}
}
}
}

View file

@ -52,7 +52,7 @@ namespace Symbols {
const string down = "";
const string left = "";
const string right = "";
const string enter = "";
const string enter = "";
}
namespace Draw {

View file

@ -19,6 +19,8 @@ tab-size = 4
#include <iostream>
#include <ranges>
#include <vector>
#include <thread>
#include <mutex>
#include <btop_input.hpp>
#include <btop_tools.hpp>
@ -72,20 +74,74 @@ namespace Input {
};
std::atomic<bool> interrupt (false);
std::atomic<bool> polling (false);
array<int, 2> mouse_pos;
unordered_flat_map<string, Mouse_loc> mouse_mappings;
deque<string> history(50, "");
string old_filter;
struct InputThr {
InputThr() : thr(run, this) {
}
static void run(InputThr* that) {
that->runImpl();
}
void runImpl() {
char ch = 0;
// TODO(pg83): read whole buffer
while (cin.get(ch)) {
std::lock_guard<std::mutex> g(lock);
current.push_back(ch);
if (current.size() > 100) {
current.clear();
}
}
}
size_t avail() {
std::lock_guard<std::mutex> g(lock);
return current.size();
}
std::string get() {
std::string res;
{
std::lock_guard<std::mutex> g(lock);
res.swap(current);
}
return res;
}
static InputThr& instance() {
// intentional memory leak, to simplify shutdown process
static InputThr* input = new InputThr();
return *input;
}
std::string current;
// TODO(pg83): use std::conditional_variable instead of sleep
std::mutex lock;
std::thread thr;
};
bool poll(int timeout) {
if (timeout < 1) return cin.rdbuf()->in_avail() > 0;
atomic_lock lck(polling);
if (timeout < 1) return InputThr::instance().avail() > 0;
while (timeout > 0) {
if (interrupt) {
interrupt = false;
return false;
}
if (cin.rdbuf()->in_avail() > 0) return true;
if (InputThr::instance().avail() > 0) return true;
sleep_ms(timeout < 10 ? timeout : 10);
timeout -= 10;
}
@ -93,9 +149,7 @@ namespace Input {
}
string get() {
string key;
while (cin.rdbuf()->in_avail() > 0 and key.size() < 100) key += cin.get();
if (cin.rdbuf()->in_avail() > 0) cin.ignore(cin.rdbuf()->in_avail());
string key = InputThr::instance().get();
if (not key.empty()) {
//? Remove escape code prefix if present
if (key.substr(0, 2) == Fx::e) {
@ -105,14 +159,14 @@ namespace Input {
if (key.starts_with("[<")) {
std::string_view key_view = key;
string mouse_event;
if (key_view.starts_with("[<0;") and key_view.ends_with('M')) {
if (key_view.starts_with("[<0;") and key_view.find('M') != std::string_view::npos) {
mouse_event = "mouse_click";
key_view.remove_prefix(4);
}
else if (key_view.starts_with("[<0;") and key_view.ends_with('m')) {
mouse_event = "mouse_release";
key_view.remove_prefix(4);
}
// else if (key_view.starts_with("[<0;") and key_view.ends_with('m')) {
// mouse_event = "mouse_release";
// key_view.remove_prefix(4);
// }
else if (key_view.starts_with("[<64;")) {
mouse_event = "mouse_scroll_up";
key_view.remove_prefix(5);
@ -168,32 +222,34 @@ namespace Input {
}
string wait() {
while (cin.rdbuf()->in_avail() < 1) {
while (InputThr::instance().avail() < 1) {
sleep_ms(10);
}
return get();
}
void clear() {
if (cin.rdbuf()->in_avail() > 0) cin.ignore(SSmax);
// do not need it, actually
}
void process(const string& key) {
if (key.empty()) return;
try {
auto& filtering = Config::getB("proc_filtering");
auto& vim_keys = Config::getB("vim_keys");
auto help_key = (vim_keys ? "H" : "h");
auto kill_key = (vim_keys ? "K" : "k");
//? Global input actions
if (not filtering) {
bool keep_going = false;
if (str_to_lower(key) == "q") {
exit(0);
clean_quit(0);
}
else if (is_in(key, "escape", "m")) {
Menu::show(Menu::Menus::Main);
return;
}
else if (is_in(key, "F1", "h")) {
else if (is_in(key, "F1", "?", help_key)) {
Menu::show(Menu::Menus::Help);
return;
}
@ -235,11 +291,15 @@ namespace Input {
bool no_update = true;
bool redraw = true;
if (filtering) {
if (key == "enter") {
if (key == "enter" or key == "down") {
Config::set("proc_filter", Proc::filter.text);
Config::set("proc_filtering", false);
old_filter.clear();
}
old_filter.clear();
if(key == "down"){
process("down");
return;
}
}
else if (key == "escape" or key == "mouse_click") {
Config::set("proc_filter", old_filter);
Config::set("proc_filtering", false);
@ -252,19 +312,19 @@ namespace Input {
else
return;
}
else if (key == "left") {
else if (key == "left" or (vim_keys and key == "h")) {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (--cur_i < 0)
cur_i = Proc::sort_vector.size() - 1;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "right") {
else if (key == "right" or (vim_keys and key == "l")) {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (std::cmp_greater(++cur_i, Proc::sort_vector.size() - 1))
cur_i = 0;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "f") {
else if (is_in(key, "f", "/")) {
Config::flip("proc_filtering");
Proc::filter = { Config::getS("proc_filter") };
old_filter = Proc::filter.text;
@ -294,7 +354,16 @@ namespace Input {
const auto& current_selection = Config::getI("proc_selected");
if (current_selection == line - y - 1) {
redraw = true;
goto proc_mouse_enter;
if (Config::getB("proc_tree")) {
const int x_pos = col - Proc::x;
const int offset = Config::getI("selected_depth") * 3;
if (x_pos > offset and x_pos < 4 + offset) {
process("space");
return;
}
}
process("enter");
return;
}
else if (current_selection == 0 or line - y - 1 == 0)
redraw = true;
@ -320,7 +389,6 @@ namespace Input {
keep_going = true;
}
else if (key == "enter") {
proc_mouse_enter:
if (Config::getI("proc_selected") == 0 and not Config::getB("show_detailed")) {
return;
}
@ -344,7 +412,7 @@ namespace Input {
if (key == "-" or key == "space") Proc::collapse = pid;
no_update = false;
}
else if (is_in(key, "t", "k") and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
else if (is_in(key, "t", kill_key) and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
atomic_wait(Runner::active);
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
Menu::show(Menu::Menus::SignalSend, (key == "t" ? SIGTERM : SIGKILL));
@ -356,7 +424,7 @@ namespace Input {
Menu::show(Menu::Menus::SignalChoose);
return;
}
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end") or (vim_keys and is_in(key, "j", "k", "g", "G"))) {
proc_mouse_scroll:
redraw = false;
auto old_selected = Config::getI("proc_selected");
@ -463,8 +531,8 @@ namespace Input {
ndev.stat.at("upload").offset = 0;
}
else {
ndev.stat.at("download").offset = ndev.stat.at("download").last;
ndev.stat.at("upload").offset = ndev.stat.at("upload").last;
ndev.stat.at("download").offset = ndev.stat.at("download").last + ndev.stat.at("download").rollover;
ndev.stat.at("upload").offset = ndev.stat.at("upload").last + ndev.stat.at("upload").rollover;
}
no_update = false;
}

View file

@ -42,6 +42,7 @@ namespace Input {
extern unordered_flat_map<string, Mouse_loc> mouse_mappings;
extern atomic<bool> interrupt;
extern atomic<bool> polling;
//* Mouse column and line position
extern array<int, 2> mouse_pos;

View file

@ -111,7 +111,7 @@ namespace Menu {
{"4", "Toggle PROC box."},
{"d", "Toggle disks view in MEM box."},
{"F2, o", "Shows options."},
{"F1, h", "Shows this window."},
{"F1, ?, h", "Shows this window."},
{"ctrl + z", "Sleep program and put in background."},
{"q, ctrl + c", "Quits program."},
{"+, -", "Add/Subtract 100ms to/from update timer."},
@ -126,7 +126,7 @@ namespace Menu {
{"z", "Toggle totals reset for current network device"},
{"a", "Toggle auto scaling for the network graphs."},
{"y", "Toggle synced scaling mode for network graphs."},
{"f", "To enter a process filter."},
{"f, /", "To enter a process filter."},
{"delete", "Clear any entered filter."},
{"c", "Toggle per-core cpu usage of processes."},
{"r", "Reverse sorting order in processes box."},
@ -177,6 +177,15 @@ namespace Menu {
"Will force 16-color mode and TTY theme,",
"set all graph symbols to \"tty\" and swap",
"out other non tty friendly symbols."},
{"vim_keys",
"Enable vim keys.",
"Set to True to enable \"h,j,k,l\" keys for",
"directional control in lists.",
"",
"Conflicting keys for",
"h (help) and k (kill)",
"is accessible while holding shift."},
{"presets",
"Define presets for the layout of the boxes.",
"",
@ -185,10 +194,10 @@ namespace Menu {
"Max 9 presets.",
"",
"Format: \"box_name:P:G,box_name:P:G\"",
"P=(0 or 1) for alternate positons.",
"P=(0 or 1) for alternate positions.",
"G=graph symbol to use for box.",
"",
"Use withespace \" \" as seprator between",
"Use withespace \" \" as separator between",
"different presets.",
"",
"Example:",
@ -197,7 +206,7 @@ namespace Menu {
"Manually set which boxes to show.",
"",
"Available values are \"cpu mem net proc\".",
"Seperate values with whitespace.",
"Separate values with whitespace.",
"",
"Toggle between presets with key \"p\"."},
{"update_ms",
@ -248,6 +257,14 @@ namespace Menu {
"\"%H\" = 24h hour, \"%I\" = 12h hour",
"\"%M\" = minute, \"%S\" = second",
"\"%d\" = day, \"%m\" = month, \"%y\" = year"},
{"base_10_sizes",
"Use base 10 for bits and bytes sizes.",
"",
"Uses KB = 1000 instead of KiB = 1024,",
"MB = 1000KB instead of MiB = 1024KiB,",
"and so on.",
"",
"True or False."},
{"background_update",
"Update main ui when menus are showing.",
"",
@ -261,6 +278,13 @@ namespace Menu {
"",
"Show battery stats in the top right corner",
"if a battery is present."},
{"selected_battery",
"Select battery.",
"",
"Which battery to use if multiple are present.",
"Can be both batteries and UPS.",
"",
"\"Auto\" for auto detection."},
{"log_level",
"Set loglevel for error.log",
"",
@ -419,7 +443,7 @@ namespace Menu {
"equals 100 percent in the io graphs.",
"(100 MiB/s by default).",
"",
"Format: \"device:speed\" seperate disks with",
"Format: \"device:speed\" separate disks with",
"whitespace \" \".",
"",
"Example: \"/dev/sda:100, /dev/sdb:20\"."},
@ -440,12 +464,28 @@ namespace Menu {
"",
"True or False."},
{"use_fstab",
"Read disks list from /etc/fstab.",
"(Has no effect on macOS X)",
"(Linux) Read disks list from /etc/fstab.",
"",
"This also disables only_physical.",
"",
"True or False."},
{"zfs_hide_datasets",
"(Linux) Hide ZFS datasets in disks list.",
"",
"Setting this to True will hide all datasets,",
"and only show ZFS pools.",
"",
"(IO stats will be calculated per-pool)",
"",
"True or False."},
{"disk_free_priv",
"(Linux) Type of available disk space.",
"",
"Set to true to show how much disk space is",
"available for privileged users.",
"",
"Set to false to show available for normal",
"users."},
{"disks_filter",
"Optional filter for shown disks.",
"",
@ -459,6 +499,15 @@ namespace Menu {
"",
"Example:",
"\"exclude=/boot /home/user\""},
{"zfs_arc_cached",
"(Linux) Count ZFS ARC as cached memory.",
"",
"Add ZFS ARC used to cached memory and",
"ZFS ARC available to available memory.",
"These are otherwise reported by the Linux",
"kernel as used memory.",
"",
"True or False."},
},
{
{"graph_symbol_net",
@ -470,13 +519,13 @@ namespace Menu {
{"net_download",
"Fixed network graph download value.",
"",
"Value in Mebibytes, default \"100\".",
"Value in Mebibits, default \"100\".",
"",
"Can be toggled with auto button."},
{"net_upload",
"Fixed network graph upload value.",
"",
"Value in Mebibytes, default \"100\".",
"Value in Mebibits, default \"100\".",
"",
"Can be toggled with auto button."},
{"net_auto",
@ -519,10 +568,10 @@ namespace Menu {
"Possible values:",
"\"pid\", \"program\", \"arguments\", \"threads\",",
"\"user\", \"memory\", \"cpu lazy\" and",
"\"cpu responsive\".",
"\"cpu direct\".",
"",
"\"cpu lazy\" updates top process over time.",
"\"cpu responsive\" updates top process",
"\"cpu direct\" updates top process",
"directly."},
{"proc_reversed",
"Reverse processes sorting order.",
@ -558,6 +607,15 @@ namespace Menu {
" ",
"Will show percentage of total memory",
"if False."},
{"proc_cpu_graphs",
"Show cpu graph for each process.",
"",
"True or False"},
{"proc_filter_kernel",
"(Linux) Filter kernel processes from output.",
"",
"Set to 'True' to filter out internal",
"processes started by the Linux kernel."},
}
};
@ -581,7 +639,7 @@ namespace Menu {
box_contents = Draw::createBox(x, y, width, height, Theme::c("hi_fg"), true, title) + Mv::d(1);
for (const auto& line : content) {
box_contents += Mv::save + Mv::r(width / 2 - Fx::uncolor(line).size() / 2 - 1) + line + Mv::restore + Mv::d(1);
box_contents += Mv::save + Mv::r(max((size_t)0, (width / 2) - (Fx::uncolor(line).size() / 2) - 1)) + line + Mv::restore + Mv::d(1);
}
}
@ -663,7 +721,7 @@ namespace Menu {
if (redraw) {
x = Term::width/2 - 40;
y = Term::height/2 - 9;
bg = Draw::createBox(x + 2, y, 78, 18, Theme::c("hi_fg"), true, "signals");
bg = Draw::createBox(x + 2, y, 78, 19, Theme::c("hi_fg"), true, "signals");
bg += Mv::to(y+2, x+3) + Theme::c("title") + Fx::b + cjust("Send signal to PID " + to_string(s_pid) + " ("
+ uresize((s_pid == Config::getI("detailed_pid") ? Proc::detailed.entry.name : Config::getS("selected_name")), 30) + ")", 76);
}
@ -695,7 +753,7 @@ namespace Menu {
else if (key == "backspace" and selected_signal != -1) {
selected_signal = (selected_signal < 10 ? -1 : selected_signal / 10);
}
else if (key == "up" and selected_signal != 16) {
else if (is_in(key, "up", "k") and selected_signal != 16) {
if (selected_signal == 1) selected_signal = 31;
else if (selected_signal < 6) selected_signal += 25;
else {
@ -704,7 +762,7 @@ namespace Menu {
if (selected_signal <= 16 and offset) selected_signal--;
}
}
else if (key == "down") {
else if (is_in(key, "down", "j")) {
if (selected_signal == 31) selected_signal = 1;
else if (selected_signal < 1 or selected_signal == 16) selected_signal = 1;
else if (selected_signal > 26) selected_signal -= 25;
@ -715,11 +773,11 @@ namespace Menu {
if (selected_signal > 31) selected_signal = 31;
}
}
else if (key == "left" and selected_signal > 0 and selected_signal != 16) {
else if (is_in(key, "left", "h") and selected_signal > 0 and selected_signal != 16) {
if (--selected_signal < 1) selected_signal = 31;
else if (selected_signal == 16) selected_signal--;
}
else if (key == "right" and selected_signal <= 31 and selected_signal != 16) {
else if (is_in(key, "right", "l") and selected_signal <= 31 and selected_signal != 16) {
if (++selected_signal > 31) selected_signal = 1;
else if (selected_signal == 16) selected_signal++;
}
@ -745,11 +803,12 @@ namespace Menu {
}
cy++;
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust("ENTER", 38) + Theme::c("main_fg") + Fx::ub + " | To send signal.";
mouse_mappings["enter"] = {cy, x, 1, 78};
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust( "↑ ↓ ← →", 38, true) + Theme::c("main_fg") + Fx::ub + " | To choose signal.";
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust("ESC or \"q\"", 38) + Theme::c("main_fg") + Fx::ub + " | To abort.";
mouse_mappings["escape"] = {cy, x, 1, 78};
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust( "↑ ↓ ← →", 33, true) + Theme::c("main_fg") + Fx::ub + " | To choose signal.";
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust("0-9", 33) + Theme::c("main_fg") + Fx::ub + " | Enter manually.";
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust("ENTER", 33) + Theme::c("main_fg") + Fx::ub + " | To send signal.";
mouse_mappings["enter"] = {cy, x, 1, 73};
out += Mv::to(++cy, x+3) + Fx::b + Theme::c("hi_fg") + rjust("ESC or \"q\"", 33) + Theme::c("main_fg") + Fx::ub + " | To abort.";
mouse_mappings["escape"] = {cy, x, 1, 73};
out += Fx::reset;
}
@ -876,10 +935,7 @@ namespace Menu {
};
}
}
else if (key == "q") {
exit(0);
}
else if (is_in(key, "escape", "m", "mouse_click")) {
else if (is_in(key, "escape", "q", "m", "mouse_click")) {
return Closed;
}
else if (key.starts_with("button_")) {
@ -900,13 +956,13 @@ namespace Menu {
currentMenu = Menus::Help;
return Switch;
case Quit:
exit(0);
clean_quit(0);
}
}
else if (is_in(key, "down", "tab", "mouse_scroll_down")) {
else if (is_in(key, "down", "tab", "mouse_scroll_down", "j")) {
if (++selected > 2) selected = 0;
}
else if (is_in(key, "up", "shift_tab", "mouse_scroll_up")) {
else if (is_in(key, "up", "shift_tab", "mouse_scroll_up", "k")) {
if (--selected < 0) selected = 2;
}
else {
@ -953,9 +1009,11 @@ namespace Menu {
{"graph_symbol_proc", std::cref(Config::valid_graph_symbols_def)},
{"cpu_graph_upper", 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)},
};
auto& tty_mode = Config::getB("tty_mode");
auto& vim_keys = Config::getB("vim_keys");
if (max_items == 0) {
for (const auto& cat : categories) {
if ((int)cat.size() > max_items) max_items = cat.size();
@ -1054,14 +1112,14 @@ namespace Menu {
else if (is_in(key, "escape", "q", "o", "backspace")) {
return Closed;
}
else if (is_in(key, "down", "mouse_scroll_down")) {
else if (is_in(key, "down", "mouse_scroll_down") or (vim_keys and key == "j")) {
if (++selected > select_max or selected >= item_height) {
if (page < pages - 1) page++;
else if (pages > 1) page = 0;
selected = 0;
}
}
else if (is_in(key, "up", "mouse_scroll_up")) {
else if (is_in(key, "up", "mouse_scroll_up") or (vim_keys and key == "k")) {
if (--selected < 0) {
if (page > 0) page--;
else if (pages > 1) page = pages - 1;
@ -1089,12 +1147,12 @@ namespace Menu {
selected_cat = key.back() - '0' - 1;
page = selected = 0;
}
else if (is_in(key, "left", "right")) {
else if (is_in(key, "left", "right") or (vim_keys and is_in(key, "h", "l"))) {
const auto& option = categories[selected_cat][item_height * page + selected][0];
if (selPred.test(isInt)) {
const int mod = (option == "update_ms" ? 100 : 1);
long value = Config::getI(option);
if (key == "right") value += mod;
if (key == "right" or (vim_keys and key == "l")) value += mod;
else value -= mod;
if (Config::intValid(option, to_string(value)))
@ -1119,13 +1177,16 @@ namespace Menu {
else if (option == "background_update") {
Runner::pause_output = false;
}
else if (option == "base_10_sizes") {
recollect = true;
}
}
else if (selPred.test(isBrowseable)) {
auto& optList = optionsList.at(option).get();
int i = v_index(optList, Config::getS(option));
if (key == "right" and ++i >= (int)optList.size()) i = 0;
else if (key == "left" and --i < 0) i = optList.size() - 1;
if ((key == "right" or (vim_keys and key == "l")) and ++i >= (int)optList.size()) i = 0;
else if ((key == "left" or (vim_keys and key == "h")) and --i < 0) i = optList.size() - 1;
Config::set(option, optList.at(i));
if (option == "color_theme")

174
src/btop_shared.cpp Normal file
View file

@ -0,0 +1,174 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <ranges>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
using std::string_literals::operator""s;
namespace rng = std::ranges;
using namespace Tools;
namespace Proc {
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, const bool reverse, const bool tree) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_c); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < proc_vec.size(); i++) {
if (i <= 5 and proc_vec.at(i).cpu_p > max)
max = proc_vec.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and proc_vec.at(i).cpu_p > 30.0)
offset++;
else if (proc_vec.at(i).cpu_p > target) {
rotate(proc_vec.begin() + offset, proc_vec.begin() + i, proc_vec.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, const bool reverse, int& c_index, const int index_max, const bool collapsed) {
if (proc_vec.size() > 1) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads < b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem < b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p < b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c < b.entry.get().cpu_c; }); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads > b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem > b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p > b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c > b.entry.get().cpu_c; }); break;
}
}
}
for (auto& r : proc_vec) {
r.entry.get().tree_index = (collapsed or r.entry.get().filtered ? index_max : c_index++);
if (not r.children.empty()) {
tree_sort(r.children, sorting, reverse, c_index, (collapsed or r.entry.get().collapsed or r.entry.get().tree_index == (size_t)index_max));
}
}
}
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) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter)
and not s_contains(cur_proc.name, filter)
and not s_contains(cur_proc.cmd, filter)
and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
}
else {
found = true;
cur_depth = 0;
}
}
else if (cur_proc.filtered) cur_proc.filtered = false;
cur_proc.depth = cur_depth;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
out_procs.push_back({ cur_proc, {} });
if (not collapsed and not filtering) {
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
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(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
}
else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (collapsed and not filtering) {
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);
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
//auto& parent = cur_proc;
cur_proc.cpu_p += p.cpu_p;
cur_proc.cpu_c += p.cpu_c;
cur_proc.mem += p.mem;
cur_proc.threads += p.threads;
filter_found++;
p.filtered = true;
}
}
if (collapsed or filtering) {
return;
}
//? 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("]─"))
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
out_procs.at(cur_pos).entry.get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
}

View file

@ -27,12 +27,15 @@ tab-size = 4
#include <array>
#include <ifaddrs.h>
#include <tuple>
#include <unistd.h>
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array, std::tuple;
void term_resize(bool force=false);
void banner_gen();
extern void clean_quit(int sig);
namespace Global {
extern const vector<array<string, 2>> Banner_src;
extern const string Version;
@ -43,6 +46,7 @@ namespace Global {
extern atomic<bool> resized;
extern string overlay;
extern string clock;
extern uid_t real_uid, set_uid;
}
namespace Runner {
@ -70,8 +74,6 @@ namespace Shared {
void init();
extern long coreCount, page_size, clk_tck;
extern int totalMem_len;
extern uint64_t totalMem;
}
@ -129,6 +131,7 @@ namespace Mem {
struct disk_info {
std::filesystem::path dev;
string name;
string fstype = "";
std::filesystem::path stat = "";
int64_t total = 0, used = 0, free = 0;
int used_percent = 0, free_percent = 0;
@ -149,11 +152,15 @@ namespace Mem {
vector<string> disks_order;
};
//?* Get total system memory
uint64_t get_totalMem();
//* Collect mem & disks stats
auto collect(const bool no_update=false) -> mem_info&;
//* 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);
}
namespace Net {
@ -166,7 +173,7 @@ namespace Net {
extern unordered_flat_map<string, uint64_t> graph_max;
struct net_stat {
uint64_t speed = 0, top = 0, total = 0, last = 0, offset = 0;
uint64_t speed = 0, top = 0, total = 0, last = 0, offset = 0, rollover = 0;
};
struct net_info {
@ -193,7 +200,7 @@ namespace Proc {
extern bool shown, redraw;
extern int select_max;
extern atomic<int> detailed_pid;
extern int selected_pid, start, selected, collapse, expand;
extern int selected_pid, start, selected, collapse, expand, filter_found, selected_depth;
extern string selected_name;
//? Contains the valid sorting options for processes
@ -262,4 +269,18 @@ namespace Proc {
//* 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);
struct tree_proc {
std::reference_wrapper<proc_info> entry;
vector<tree_proc> children;
};
//* Sort vector of proc_info's
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, const bool reverse, const bool tree = false);
//* 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);
//* 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);
}

View file

@ -23,15 +23,17 @@ tab-size = 4
#include <sstream>
#include <iomanip>
#include <utility>
#include <ranges>
#include <robin_hood.h>
#include <widechar_width.hpp>
#include <unistd.h>
#include <limits.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
#include <btop_config.hpp>
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;
@ -46,7 +48,6 @@ namespace Term {
atomic<int> width = 0;
atomic<int> height = 0;
string current_tty;
char* custombuf;
namespace {
struct termios initial_settings;
@ -73,12 +74,14 @@ namespace Term {
}
}
bool refresh() {
bool refresh(bool only_check) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) return false;
if (width != w.ws_col or height != w.ws_row) {
width = w.ws_col;
height = w.ws_row;
if (not only_check) {
width = w.ws_col;
height = w.ws_row;
}
return true;
}
return false;
@ -120,9 +123,6 @@ namespace Term {
linebuffered(false);
refresh();
//? Set 1MB buffer for cout
std::cout.rdbuf()->pubsetbuf(custombuf, 1048576);
cout << alt_screen << hide_cursor << mouse_on << flush;
Global::resized = false;
}
@ -141,21 +141,85 @@ namespace Term {
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
// ! Dsiabled due to issue when compiling with musl, reverted back to using regex
// namespace Fx {
// string uncolor(const string& s) {
// string out = s;
// for (size_t offset = 0, start_pos = 0, end_pos = 0;;) {
// start_pos = (offset == 0) ? out.find('\x1b') : offset;
// if (start_pos == string::npos)
// break;
// offset = start_pos + 1;
// end_pos = out.find('m', offset);
// if (end_pos == string::npos)
// break;
// else if (auto next_pos = out.find('\x1b', offset); not isdigit(out[end_pos - 1]) or end_pos > next_pos) {
// offset = next_pos;
// continue;
// }
// out.erase(start_pos, (end_pos - start_pos)+1);
// offset = 0;
// }
// out.shrink_to_fit();
// return out;
// }
// }
namespace Tools {
atomic<int> active_locks (0);
size_t wide_ulen(const string& str) {
unsigned int chars = 0;
try {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
auto w_str = conv.from_bytes((str.size() > 10000 ? str.substr(0, 10000).c_str() : str.c_str()));
for (auto c : w_str) {
chars += utf8::wcwidth(c);
}
}
catch (...) {
return ulen(str);
}
return chars;
}
size_t wide_ulen(const std::wstring& w_str) {
unsigned int chars = 0;
for (auto c : w_str) {
chars += utf8::wcwidth(c);
}
return chars;
}
string uresize(string str, const size_t len, const bool wide) {
if (len < 1 or str.empty()) return "";
for (size_t x = 0, i = 0; i < str.size(); i++) {
if (wide and static_cast<unsigned char>(str.at(i)) > 0xef) x += 2;
else if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) x++;
if (x >= len + 1) {
str.resize(i);
str.shrink_to_fit();
break;
if (wide) {
try {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
auto w_str = conv.from_bytes((str.size() > 10000 ? str.substr(0, 10000).c_str() : str.c_str()));
while (wide_ulen(w_str) > len)
w_str.pop_back();
string n_str = conv.to_bytes(w_str);
return n_str;
}
catch (...) {
return uresize(str, len, false);
}
}
else {
for (size_t x = 0, i = 0; i < str.size(); i++) {
if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) x++;
if (x >= len + 1) {
str.resize(i);
break;
}
}
}
str.shrink_to_fit();
return str;
}
@ -271,28 +335,45 @@ namespace Tools {
string floating_humanizer(uint64_t value, const bool shorten, size_t start, const bool bit, const bool per_second) {
string out;
const size_t mult = (bit) ? 8 : 1;
static const array<string, 11> Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
static const array<string, 11> Units_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
const auto& units = (bit) ? Units_bit : Units_byte;
const 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"};
static const array<string, 11> megaUnits_bit = {"bit", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb", "Bb", "Gb"};
static const array<string, 11> megaUnits_byte = {"Byte", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "GB"};
const auto& units = (bit) ? ( mega ? megaUnits_bit : mebiUnits_bit) : ( mega ? megaUnits_byte : mebiUnits_byte);
value *= 100 * mult;
while (value >= 102400) {
value >>= 10;
if (value < 100) {
out = to_string(value);
break;
if (mega) {
while (value >= 100000) {
value /= 1000;
if (value < 100) {
out = to_string(value);
break;
}
start++;
}
}
else {
while (value >= 102400) {
value >>= 10;
if (value < 100) {
out = to_string(value);
break;
}
start++;
}
start++;
}
if (out.empty()) {
out = to_string(value);
if (out.size() == 4 and start > 0) { out.pop_back(); out.insert(2, ".");}
if (not mega and out.size() == 4 and start > 0) { out.pop_back(); 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 (out.find('.') != string::npos) out = to_string((int)round(stof(out)));
auto f_pos = out.find('.');
if (f_pos == 1 and out.size() > 3) out = to_string(round(stof(out) * 10) / 10).substr(0,3);
else if (f_pos != string::npos) out = to_string((int)round(stof(out)));
if (out.size() > 3) { out = to_string((int)(out[0] - '0') + 1); start++;}
out.push_back(units[start][0]);
}
@ -319,15 +400,22 @@ namespace Tools {
return ss.str();
}
atomic_lock::atomic_lock(atomic<bool>& atom) : atom(atom) {
active_locks++;
while (not this->atom.compare_exchange_strong(this->not_true, true));
void atomic_wait(const atomic<bool>& atom, const bool old) noexcept {
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 {
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);
}
atomic_lock::atomic_lock(atomic<bool>& atom, bool wait) : atom(atom) {
if (wait) while (not this->atom.compare_exchange_strong(this->not_true, true));
else this->atom.store(true);
}
atomic_lock::~atomic_lock() {
active_locks--;
this->atom.store(false);
atomic_notify(this->atom);
}
string readfile(const std::filesystem::path& path, const string& fallback) {
@ -338,7 +426,8 @@ namespace Tools {
for (string readstr; getline(file, readstr); out += readstr);
}
catch (const std::exception& e) {
throw std::runtime_error("readfile() : Exception when reading " + (string)path + " : " + e.what());
Logger::error("readfile() : Exception when reading " + (string)path + " : " + e.what());
return fallback;
}
return (out.empty() ? fallback : out);
}
@ -371,22 +460,33 @@ namespace Tools {
namespace Logger {
using namespace Tools;
namespace {
std::atomic<bool> busy (false);
bool first = true;
const string tdf = "%Y/%m/%d (%T) | ";
}
std::atomic<bool> busy (false);
bool first = true;
const string tdf = "%Y/%m/%d (%T) | ";
size_t loglevel;
fs::path logfile;
//* Wrapper for lowering priviliges if using SUID bit and currently isn't using real userid
class lose_priv {
int status = -1;
public:
lose_priv() {
if (geteuid() != Global::real_uid) this->status = seteuid(Global::real_uid);
}
~lose_priv() {
if (status == 0) status = seteuid(Global::set_uid);
}
};
void set(const string& level) {
loglevel = v_index(log_levels, level);
}
void log_write(const size_t level, const string& msg) {
if (loglevel < level or logfile.empty()) return;
atomic_lock lck(busy);
atomic_lock lck(busy, true);
lose_priv neutered{};
std::error_code ec;
try {
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {

View file

@ -21,16 +21,24 @@ tab-size = 4
#include <string>
#include <vector>
#include <array>
#include <regex>
#include <atomic>
#include <regex>
#include <filesystem>
#include <ranges>
#include <chrono>
#include <thread>
#include <tuple>
#include <pthread.h>
#include <limits.h>
#ifndef HOST_NAME_MAX
#ifdef __APPLE__
#define HOST_NAME_MAX 255
#else
#define HOST_NAME_MAX 64
#endif
#endif
using std::string, std::vector, std::atomic, std::to_string, std::regex, std::tuple, std::array;
using std::string, std::vector, std::atomic, std::to_string, std::tuple, std::array;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
@ -58,13 +66,14 @@ namespace Fx {
extern string reset;
//* Regex for matching color, style and cursor move escape sequences
const regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}");
const std::regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}");
//* Regex for matching only color and style escape sequences
const regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}");
const std::regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}");
//* Return a string with all colors and text styling removed
inline string uncolor(const string& s) { return regex_replace(s, color_regex, ""); }
inline string uncolor(const string& s) { return std::regex_replace(s, color_regex, ""); }
// string uncolor(const string& s);
}
@ -107,14 +116,14 @@ namespace Term {
const string clear_end = Fx::e + "0J";
const string clear_begin = Fx::e + "1J";
const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h"; //? Enable reporting of mouse position on click and release
const string mouse_off = Fx::e + "?1002l";
const string mouse_off = Fx::e + "?1002l" + Fx::e + "?1015l" + Fx::e + "?1006l";
const string mouse_direct_on = Fx::e + "?1003h"; //? Enable reporting of mouse position at any movement
const string mouse_direct_off = Fx::e + "?1003l";
const string sync_start = Fx::e + "?2026h"; //? Start of terminal synchronized output
const string sync_end = Fx::e + "?2026l"; //? End of terminal synchronized output
//* Returns true if terminal has been resized and updates width and height
bool refresh();
bool refresh(bool only_check=false);
//* Returns an array with the lowest possible width, height with current box config
auto get_min_size(const string& boxes) -> array<int, 2>;
@ -130,12 +139,13 @@ namespace Term {
namespace Tools {
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
extern atomic<int> active_locks;
//* Return number of UTF8 characters in a string (wide=true counts UTF-8 characters with a width > 1 as 2 characters)
size_t wide_ulen(const string& 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)
inline size_t ulen(const string& str, const bool wide=false) {
return std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; })
+ (wide ? std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) > 0xef); }) : 0);
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)
@ -177,6 +187,16 @@ namespace Tools {
return str.find(find_val) != string::npos;
}
//* Check if string <str> contains string <find_val>, while ignoring case
inline bool s_contains_ic(const string& str, const string& find_val) {
auto it = std::search(
str.begin(), str.end(),
find_val.begin(), find_val.end(),
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return it != str.end();
}
//* Return index of <find_val> from vector <vec>, returns size of <vec> if <find_val> is not present
template <typename T>
inline size_t v_index(const vector<T>& vec, const T& find_val) {
@ -269,20 +289,28 @@ namespace Tools {
string hostname();
string username();
#if __GNUC__ < 11
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { while (atom.load() == old) sleep_ms(1); }
inline void atomic_notify(const atomic<bool>& atom) noexcept { (void)atom; }
#else
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { atom.wait(old); }
inline void atomic_notify(const atomic<bool>& atom) noexcept { atom.notify_all(); }
#endif
static inline void busy_wait (void) {
#if defined __i386__ || defined __x86_64__
__builtin_ia32_pause();
#elif defined __ia64__
__asm volatile("hint @pause" : : : "memory");
#elif defined __sparc__ && (defined __arch64__ || defined __sparc_v9__)
__asm volatile("membar #LoadLoad" : : : "memory");
#else
__asm volatile("" : : : "memory");
#endif
}
//* Waits for atomic<bool> to be false and sets it to true on construct, sets to false and notifies on destruct
void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept;
void atomic_wait_for(const atomic<bool>& atom, const bool old=true, const uint64_t wait_ms=0) noexcept;
//* Sets atomic<bool> to true on construct, sets to false on destruct
class atomic_lock {
atomic<bool>& atom;
bool not_true = false;
public:
atomic_lock(atomic<bool>& atom);
atomic_lock(atomic<bool>& atom, bool wait=false);
~atomic_lock();
};

1319
src/freebsd/btop_collect.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1367
src/osx/btop_collect.cpp Normal file

File diff suppressed because it is too large Load diff

111
src/osx/sensors.cpp Normal file
View file

@ -0,0 +1,111 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include "sensors.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
#include <string>
#include <numeric>
#include <vector>
extern "C" {
typedef struct __IOHIDEvent *IOHIDEventRef;
typedef struct __IOHIDServiceClient *IOHIDServiceClientRef;
#ifdef __LP64__
typedef double IOHIDFloat;
#else
typedef float IOHIDFloat;
#endif
#define IOHIDEventFieldBase(type) (type << 16)
#define kIOHIDEventTypeTemperature 15
IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator);
int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, CFDictionaryRef match);
int IOHIDEventSystemClientSetMatchingMultiple(IOHIDEventSystemClientRef client, CFArrayRef match);
IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t);
CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef property);
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field);
// create a dict ref, like for temperature sensor {"PrimaryUsagePage":0xff00, "PrimaryUsage":0x5}
CFDictionaryRef matching(int page, int usage) {
CFNumberRef nums[2];
CFStringRef keys[2];
keys[0] = CFStringCreateWithCString(0, "PrimaryUsagePage", 0);
keys[1] = CFStringCreateWithCString(0, "PrimaryUsage", 0);
nums[0] = CFNumberCreate(0, kCFNumberSInt32Type, &page);
nums[1] = CFNumberCreate(0, kCFNumberSInt32Type, &usage);
CFDictionaryRef dict = CFDictionaryCreate(0, (const void **)keys, (const void **)nums, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(keys[0]);
CFRelease(keys[1]);
return dict;
}
double getValue(IOHIDServiceClientRef sc) {
IOHIDEventRef event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0); // here we use ...CopyEvent
IOHIDFloat temp = 0.0;
if (event != 0) {
temp = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature));
CFRelease(event);
}
return temp;
}
} // extern C
long long Cpu::ThermalSensors::getSensors() {
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
// can be checked by ioreg -lfx
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
IOHIDEventSystemClientSetMatching(system, thermalSensors);
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
std::vector<double> temps;
if (matchingsrvs) {
long count = CFArrayGetCount(matchingsrvs);
for (int i = 0; i < count; i++) {
IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
if (sc) {
CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); // here we use ...CopyProperty
if (name) {
char buf[200];
CFStringGetCString(name, buf, 200, kCFStringEncodingASCII);
std::string n(buf);
// this is just a guess, nobody knows which sensors mean what
// on my system PMU tdie 3 and 9 are missing...
// there is also PMU tdev1-8 but it has negative values??
// there is also eACC for efficiency package but it only has 2 entries
// and pACC for performance but it has 7 entries (2 - 9) WTF
if (n.starts_with("eACC") or n.starts_with("pACC")) {
temps.push_back(getValue(sc));
}
CFRelease(name);
}
}
}
CFRelease(matchingsrvs);
}
CFRelease(system);
CFRelease(thermalSensors);
if (temps.empty()) return 0ll;
return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size());
}

24
src/osx/sensors.hpp Normal file
View file

@ -0,0 +1,24 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
namespace Cpu {
class ThermalSensors {
public:
long long getSensors();
};
} // namespace Cpu

150
src/osx/smc.cpp Normal file
View file

@ -0,0 +1,150 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include "smc.hpp"
static UInt32 _strtoul(char *str, int size, int base) {
UInt32 total = 0;
int i;
for (i = 0; i < size; i++) {
if (base == 16) {
total += str[i] << (size - 1 - i) * 8;
} else {
total += (unsigned char)(str[i] << (size - 1 - i) * 8);
}
}
return total;
}
static void _ultostr(char *str, UInt32 val) {
str[0] = '\0';
sprintf(str, "%c%c%c%c",
(unsigned int)val >> 24,
(unsigned int)val >> 16,
(unsigned int)val >> 8,
(unsigned int)val);
}
namespace Cpu {
SMCConnection::SMCConnection() {
IOMasterPort(kIOMasterPortDefault, &masterPort);
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
if (result != kIOReturnSuccess) {
throw std::runtime_error("failed to get AppleSMC");
}
device = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (device == 0) {
throw std::runtime_error("failed to get SMC device");
}
result = IOServiceOpen(device, mach_task_self(), 0, &conn);
IOObjectRelease(device);
if (result != kIOReturnSuccess) {
throw std::runtime_error("failed to get SMC connection");
}
}
SMCConnection::~SMCConnection() {
IOServiceClose(conn);
}
long long SMCConnection::getSMCTemp(char *key) {
SMCVal_t val;
kern_return_t result;
result = SMCReadKey(key, &val);
if (result == kIOReturnSuccess) {
if (val.dataSize > 0) {
if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
// convert sp78 value to temperature
int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1];
return static_cast<long long>(intValue / 256.0);
}
}
}
return -1;
}
// core means physical core in SMC, while in core map it's cpu threads :-/ Only an issue on hackintosh?
// this means we can only get the T per physical core
// another issue with the SMC API is that the key is always 4 chars -> what with systems with more than 9 physical cores?
// no Mac models with more than 18 threads are released, so no problem so far
// according to VirtualSMC docs (hackintosh fake SMC) the enumeration follows with alphabetic chars - not implemented yet here (nor in VirtualSMC)
long long SMCConnection::getTemp(int core) {
char key[] = SMC_KEY_CPU_TEMP;
if (core >= 0) {
snprintf(key, 5, "TC%1dc", core);
}
long long result = getSMCTemp(key);
if (result == -1) {
// try again with C
snprintf(key, 5, "TC%1dC", core);
result = getSMCTemp(key);
}
return result;
}
kern_return_t SMCConnection::SMCReadKey(UInt32Char_t key, SMCVal_t *val) {
kern_return_t result;
SMCKeyData_t inputStructure;
SMCKeyData_t outputStructure;
memset(&inputStructure, 0, sizeof(SMCKeyData_t));
memset(&outputStructure, 0, sizeof(SMCKeyData_t));
memset(val, 0, sizeof(SMCVal_t));
inputStructure.key = _strtoul(key, 4, 16);
inputStructure.data8 = SMC_CMD_READ_KEYINFO;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
val->dataSize = outputStructure.keyInfo.dataSize;
_ultostr(val->dataType, outputStructure.keyInfo.dataType);
inputStructure.keyInfo.dataSize = val->dataSize;
inputStructure.data8 = SMC_CMD_READ_BYTES;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
return kIOReturnSuccess;
}
kern_return_t SMCConnection::SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
size_t structureInputSize;
size_t structureOutputSize;
structureInputSize = sizeof(SMCKeyData_t);
structureOutputSize = sizeof(SMCKeyData_t);
return IOConnectCallStructMethod(conn, index,
// inputStructure
inputStructure, structureInputSize,
// ouputStructure
outputStructure, &structureOutputSize);
}
} // namespace Cpu

117
src/osx/smc.hpp Normal file
View file

@ -0,0 +1,117 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#include <stdexcept>
#define VERSION "0.01"
#define KERNEL_INDEX_SMC 2
#define SMC_CMD_READ_BYTES 5
#define SMC_CMD_WRITE_BYTES 6
#define SMC_CMD_READ_INDEX 8
#define SMC_CMD_READ_KEYINFO 9
#define SMC_CMD_READ_PLIMIT 11
#define SMC_CMD_READ_VERS 12
#define DATATYPE_FPE2 "fpe2"
#define DATATYPE_UINT8 "ui8 "
#define DATATYPE_UINT16 "ui16"
#define DATATYPE_UINT32 "ui32"
#define DATATYPE_SP78 "sp78"
// key values
#define SMC_KEY_CPU_TEMP "TC0P" // proximity temp?
#define SMC_KEY_CPU_DIODE_TEMP "TC0D" // diode temp?
#define SMC_KEY_CPU_DIE_TEMP "TC0F" // die temp?
#define SMC_KEY_CPU1_TEMP "TC1C"
#define SMC_KEY_CPU2_TEMP "TC2C" // etc
#define SMC_KEY_FAN0_RPM_CUR "F0Ac"
typedef struct {
char major;
char minor;
char build;
char reserved[1];
UInt16 release;
} SMCKeyData_vers_t;
typedef struct {
UInt16 version;
UInt16 length;
UInt32 cpuPLimit;
UInt32 gpuPLimit;
UInt32 memPLimit;
} SMCKeyData_pLimitData_t;
typedef struct {
UInt32 dataSize;
UInt32 dataType;
char dataAttributes;
} SMCKeyData_keyInfo_t;
typedef char SMCBytes_t[32];
typedef struct {
UInt32 key;
SMCKeyData_vers_t vers;
SMCKeyData_pLimitData_t pLimitData;
SMCKeyData_keyInfo_t keyInfo;
char result;
char status;
char data8;
UInt32 data32;
SMCBytes_t bytes;
} SMCKeyData_t;
typedef char UInt32Char_t[5];
typedef struct {
UInt32Char_t key;
UInt32 dataSize;
UInt32Char_t dataType;
SMCBytes_t bytes;
} SMCVal_t;
namespace Cpu {
class SMCConnection {
public:
SMCConnection();
virtual ~SMCConnection();
long long getTemp(int core);
private:
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
long long getSMCTemp(char *key);
kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure);
io_connect_t conn;
kern_return_t result;
mach_port_t masterPort;
io_iterator_t iterator;
io_object_t device;
};
} // namespace Cpu

View file

@ -0,0 +1,94 @@
#HotPurpleTrafficLight
#by Pete Allebone - mess with the best... you know the rest. <catch me at: peter at allebone dot org>
#Designed to flash up bright red with danger when loads are high and attention is needed.
# Main background, empty for terminal default, need to be empty if you want transparent background
theme[main_bg]="#000000"
# Main text color
theme[main_fg]="#d1d1e0"
# Title color for boxes
theme[title]="#d1d1e0"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#9933ff"
# Background color of selected item in processes box
theme[selected_bg]="#6666ff"
# Foreground color of selected item in processes box
theme[selected_fg]="#d1d1e0"
# Color of inactive/disabled text
theme[inactive_fg]="#9999ff"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#9933ff"
# Background color of the percentage meters
theme[meter_bg]="#4d4dff"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#9933ff"
# Cpu box outline color
theme[cpu_box]="#a64dff"
# Memory/disks box outline color
theme[mem_box]="#a64dff"
# Net up/down box outline color
theme[net_box]="#a64dff"
# Processes box outline color
theme[proc_box]="#a64dff"
# Box divider line and small boxes line color
theme[div_line]="#4d4dff"
# Temperature graph colors
theme[temp_start]="#00ff00"
theme[temp_mid]="#ff9933"
theme[temp_end]="#ff0000"
# CPU graph colors
theme[cpu_start]="#00ff00"
theme[cpu_mid]="#ccff66"
theme[cpu_end]="#ff0000"
# Mem/Disk free meter
theme[free_end]="#00ff00"
theme[free_mid]="#ccff66"
theme[free_start]="#ff0000"
# Mem/Disk cached meter
theme[cached_start]="#00ff00"
theme[cached_mid]="#ccff66"
theme[cached_end]="#ff0000"
# Mem/Disk available meter
theme[available_start]="#ff0000"
theme[available_mid]="#ccff66"
theme[available_end]="#00ff00"
# Mem/Disk used meter
theme[used_start]="#00ff00"
theme[used_mid]="#ccff66"
theme[used_end]="#ff0000"
# Download graph colors
theme[download_start]="#00ff00"
theme[download_mid]="#ff9933"
theme[download_end]="#ff0000"
# Upload graph colors
theme[upload_start]="#00ff00"
theme[upload_mid]="#ff9933"
theme[upload_end]="#ff0000"
# Process box color gradient for threads, mem and cpu usage
theme[process_start]="#9999ff"
theme[process_mid]="#4d4dff"
theme[process_end]="#a64dff"

89
themes/ayu.theme Normal file
View file

@ -0,0 +1,89 @@
# Main background, empty for terminal default, need to be empty if you want transparent background
theme[main_bg]="#0B0E14"
# Main text color
theme[main_fg]="#BFBDB6"
# Title color for boxes
theme[title]="#BFBDB6"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#E6B450"
# Background color of selected item in processes box
theme[selected_bg]="#E6B450"
# Foreground color of selected item in processes box
theme[selected_fg]="#f8f8f2"
# Color of inactive/disabled text
theme[inactive_fg]="#565B66"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#BFBDB6"
# Background color of the percentage meters
theme[meter_bg]="#565B66"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#DFBFFF"
# Cpu box outline color
theme[cpu_box]="#DFBFFF"
# Memory/disks box outline color
theme[mem_box]="#95E6CB"
# Net up/down box outline color
theme[net_box]="#F28779"
# Processes box outline color
theme[proc_box]="#E6B673"
# Box divider line and small boxes line color
theme[div_line]="#565B66"
# Temperature graph colors
theme[temp_start]="#DFBFFF"
theme[temp_mid]="#D2A6FF"
theme[temp_end]="#A37ACC"
# CPU graph colors
theme[cpu_start]="#DFBFFF"
theme[cpu_mid]="#D2A6FF"
theme[cpu_end]="#A37ACC"
# Mem/Disk free meter
theme[free_start]="#95E6CB"
theme[free_mid]="#95E6CB"
theme[free_end]="#4CBF99"
# Mem/Disk cached meter
theme[cached_start]="#95E6CB"
theme[cached_mid]="#95E6CB"
theme[cached_end]="#4CBF99"
# Mem/Disk available meter
theme[available_start]="#95E6CB"
theme[available_mid]="#95E6CB"
theme[available_end]="#4CBF99"
# Mem/Disk used meter
theme[used_start]="#95E6CB"
theme[used_mid]="#95E6CB"
theme[used_end]="#4CBF99"
# Download graph colors
theme[download_start]="#F28779"
theme[download_mid]="#F07178"
theme[download_end]="#F07171"
# Upload graph colors
theme[upload_start]="#73D0FF"
theme[upload_mid]="#59C2FF"
theme[upload_end]="#399EE6"
# Process box color gradient for threads, mem and cpu usage
theme[process_start]="#FFCC66"
theme[process_mid]="#E6B450"
theme[process_end]="#FFAA33"

View file

@ -0,0 +1,93 @@
# Btop everforest dark hard theme by u/archontop.
# 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]="#2b3339"
# Main text color
theme[main_fg]="#d3c6aa"
# Title color for boxes
theme[title]="#d3c6aa"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#e67e80"
# Background color of selected items
theme[selected_bg]="#4b565c"
# Foreground color of selected items
theme[selected_fg]="#dbbc7f"
# Color of inactive/disabled text
theme[inactive_fg]="#2b3339"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#d3c6aa"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#a7c080"
# Cpu box outline color
theme[cpu_box]="#4b565c"
# Memory/disks box outline color
theme[mem_box]="#4b565c"
# Net up/down box outline color
theme[net_box]="#4b565c"
# Processes box outline color
theme[proc_box]="#4b565c"
# Box divider line and small boxes line color
theme[div_line]="#4b565c"
# Temperature graph colors
theme[temp_start]="#a7c080"
theme[temp_mid]="#dbbc7f"
theme[temp_end]="#f85552"
# CPU graph colors
theme[cpu_start]="#a7c080"
theme[cpu_mid]="#dbbc7f"
theme[cpu_end]="#f85552"
# Mem/Disk free meter
theme[free_start]="#f85552"
theme[free_mid]="#dbbc7f"
theme[free_end]="#a7c080"
# Mem/Disk cached meter
theme[cached_start]="#7fbbb3"
theme[cached_mid]="#83c092"
theme[cached_end]="#a7c080"
# Mem/Disk available meter
theme[available_start]="#f85552"
theme[available_mid]="#dbbc7f"
theme[available_end]="#a7c080"
# Mem/Disk used meter
theme[used_start]="#a7c080"
theme[used_mid]="#dbbc7f"
theme[used_end]="#f85552"
# Download graph colors
theme[download_start]="#8da101"
theme[download_mid]="#83c092"
theme[download_end]="#a7c080"
# Upload graph colors
theme[upload_start]="#f85552"
theme[upload_mid]="#dbbc7f"
theme[upload_end]="#a7c080"
# Process box color gradient for threads, mem and cpu usage
theme[process_start]="#a7c080"
theme[process_mid]="#f85552"
theme[process_end]="#CC241D"

View file

@ -0,0 +1,98 @@
# Bashtop gruvbox (https://github.com/morhetz/gruvbox) theme
# First version created By BachoSeven
# Adjustments to proper colors by Pietryszak (https://github.com/pietryszak/)
# 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]="#282828"
# Main text color
theme[main_fg]="#EBDBB2"
# Title color for boxes
theme[title]="#EBDBB2"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#CC241D"
# Background color of selected items
theme[selected_bg]="#32302F"
# Foreground color of selected items
theme[selected_fg]="#D3869B"
# Color of inactive/disabled text
theme[inactive_fg]="#3C3836"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#A89984"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#98971A"
# Cpu box outline color
theme[cpu_box]="#A89984"
# Memory/disks box outline color
theme[mem_box]="#A89984"
# Net up/down box outline color
theme[net_box]="#A89984"
# Processes box outline color
theme[proc_box]="#A89984"
# Box divider line and small boxes line color
theme[div_line]="#A89984"
# Temperature graph colors
theme[temp_start]="#98971A"
theme[temp_mid]=""
theme[temp_end]="#CC241D"
# CPU graph colors
theme[cpu_start]="#8EC07C"
theme[cpu_mid]="#D79921"
theme[cpu_end]="#CC241D"
# Mem/Disk free meter
theme[free_start]="#CC241D"
theme[free_mid]="#D79921"
theme[free_end]="#8EC07C"
# Mem/Disk cached meter
theme[cached_start]="#458588"
theme[cached_mid]="#83A598"
theme[cached_end]="#8EC07C"
# Mem/Disk available meter
theme[available_start]="#CC241D"
theme[available_mid]="#D65D0E"
theme[available_end]="#FABD2F"
# Mem/Disk used meter
theme[used_start]="#8EC07C"
theme[used_mid]="#D65D0E"
theme[used_end]="#CC241D"
# Download graph colors
theme[download_start]="#98971A"
theme[download_mid]="#689d6A"
theme[download_end]="#B8BB26"
# Upload graph colors
theme[upload_start]="#CC241D"
theme[upload_mid]="#D65d0E"
theme[upload_end]="#FABF2F"
# Process box color gradient for threads, mem and cpu usage
theme[process_start]="#8EC07C"
theme[process_mid]="#FE8019"
theme[process_end]="#CC241D"

View file

@ -0,0 +1,92 @@
# Btop gruvbox material dark (https://github.com/sainnhe/gruvbox-material) theme
# by Marco Radocchia
# 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]="#282828"
# Main text color
theme[main_fg]="#d4be98"
# Title color for boxes
theme[title]="#d4be98"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#ea6962"
# Background color of selected items
theme[selected_bg]="#d8a657"
# Foreground color of selected items
theme[selected_fg]="#282828"
# Color of inactive/disabled text
theme[inactive_fg]="#282828"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#665c54"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#a9b665"
# Cpu box outline color
theme[cpu_box]="#7c6f64"
# Memory/disks box outline color
theme[mem_box]="#7c6f64"
# Net up/down box outline color
theme[net_box]="#7c6f64"
# Processes box outline color
theme[proc_box]="#7c6f64"
# Box divider line and small boxes line color
theme[div_line]="#7c6f64"
# Temperature graph colors
theme[temp_start]="#7daea3"
theme[temp_mid]="#e78a4e"
theme[temp_end]="#ea6962"
# CPU graph colors
theme[cpu_start]="#a9b665"
theme[cpu_mid]="#d8a657"
theme[cpu_end]="#ea6962"
# Mem/Disk free meter
theme[free_start]="#89b482"
theme[free_mid]=""
theme[free_end]=""
# Mem/Disk cached meter
theme[cached_start]="#7daea3"
theme[cached_mid]=""
theme[cached_end]=""
# Mem/Disk available meter
theme[available_start]="#d8a657"
theme[available_mid]=""
theme[available_end]=""
# Mem/Disk used meter
theme[used_start]="#ea6962"
theme[used_mid]=""
theme[used_end]=""
# Download graph colors
theme[download_start]="#e78a4e"
theme[download_mid]=""
theme[download_end]=""
# Upload graph colors
theme[upload_start]="#d3869b"
theme[upload_mid]=""
theme[upload_end]=""

92
themes/night-owl.theme Normal file
View file

@ -0,0 +1,92 @@
#Bashtop theme with night-owl colors
#by zkourouma
# 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]="#011627"
# Main text color
theme[main_fg]="#d6deeb"
# Title color for boxes
theme[title]="#ffffff"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#addb67"
# Background color of selected items
theme[selected_bg]="#000000"
# Foreground color of selected items
theme[selected_fg]="#ffeb95"
# Color of inactive/disabled text
theme[inactive_fg]="#575656"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#585858"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#22da6e"
# Cpu box outline color
theme[cpu_box]="#ffffff"
# Memory/disks box outline color
theme[mem_box]="#ffffff"
# Net up/down box outline color
theme[net_box]="#ffffff"
# Processes box outline color
theme[proc_box]="#ffffff"
# Box divider line and small boxes line color
theme[div_line]="#ffffff"
# Temperature graph colors
theme[temp_start]="#82aaff"
theme[temp_mid]="#c792ea"
theme[temp_end]="#fb4394"
# CPU graph colors
theme[cpu_start]="#22da6e"
theme[cpu_mid]="#addb67"
theme[cpu_end]="#ef5350"
# Mem/Disk free meter
theme[free_start]="#4e5900"
theme[free_mid]=""
theme[free_end]="#22da6e"
# Mem/Disk cached meter
theme[cached_start]="#82aaff"
theme[cached_mid]=""
theme[cached_end]="#82aaff"
# Mem/Disk available meter
theme[available_start]="#addb67"
theme[available_mid]=""
theme[available_end]="#ffeb95"
# Mem/Disk used meter
theme[used_start]="#ef5350"
theme[used_mid]=""
theme[used_end]="#ef5350"
# 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]="#c792ea"
theme[upload_end]="#c792ea"

81
themes/onedark.theme Normal file
View file

@ -0,0 +1,81 @@
# Theme: OneDark
# By: Vitor Melo
# Main bg
theme[main_bg]="#282c34"
# Main text color
theme[main_fg]="#abb2bf"
# Title color for boxes
theme[title]="#abb2bf"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#61afef"
# Background color of selected item in processes box
theme[selected_bg]="#2c313c"
# Foreground color of selected item in processes box
theme[selected_fg]="#abb2bf"
# Color of inactive/disabled text
theme[inactive_fg]="#5c6370"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#61afef"
# Cpu box outline color
theme[cpu_box]="#5c6370"
# Memory/disks box outline color
theme[mem_box]="#5c6370"
# Net up/down box outline color
theme[net_box]="#5c6370"
# Processes box outline color
theme[proc_box]="#5c6370"
# Box divider line and small boxes line color
theme[div_line]="#5c6370"
# Temperature graph colors
theme[temp_start]="#98c379"
theme[temp_mid]="#e5c07b"
theme[temp_end]="#e06c75"
# CPU graph colors
theme[cpu_start]="#98c379"
theme[cpu_mid]="#e5c07b"
theme[cpu_end]="#e06c75"
# Mem/Disk free meter
theme[free_start]="#98c379"
theme[free_mid]="#e5c07b"
theme[free_end]="#e06c75"
# Mem/Disk cached meter
theme[cached_start]="#98c379"
theme[cached_mid]="#e5c07b"
theme[cached_end]="#e06c75"
# Mem/Disk available meter
theme[available_start]="#98c379"
theme[available_mid]="#e5c07b"
theme[available_end]="#e06c75"
# Mem/Disk used meter
theme[used_start]="#98c379"
theme[used_mid]="#e5c07b"
theme[used_end]="#e06c75"
# Download graph colors
theme[download_start]="#98c379"
theme[download_mid]="#e5c07b"
theme[download_end]="#e06c75"
# Upload graph colors
theme[upload_start]="#98c379"
theme[upload_mid]="#e5c07b"
theme[upload_end]="#e06c75"

81
themes/tokyo-night.theme Normal file
View file

@ -0,0 +1,81 @@
# Theme: tokyo-night
# By: Pascal Jaeger
# Main bg
theme[main_bg]="#1a1b26"
# Main text color
theme[main_fg]="#cfc9c2"
# Title color for boxes
theme[title]="#cfc9c2"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#7dcfff"
# Background color of selected item in processes box
theme[selected_bg]="#414868"
# Foreground color of selected item in processes box
theme[selected_fg]="#cfc9c2"
# Color of inactive/disabled text
theme[inactive_fg]="#565f89"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#7dcfff"
# Cpu box outline color
theme[cpu_box]="#565f89"
# Memory/disks box outline color
theme[mem_box]="#565f89"
# Net up/down box outline color
theme[net_box]="#565f89"
# Processes box outline color
theme[proc_box]="#565f89"
# Box divider line and small boxes line color
theme[div_line]="#565f89"
# Temperature graph colors
theme[temp_start]="#9ece6a"
theme[temp_mid]="#e0af68"
theme[temp_end]="#f7768e"
# CPU graph colors
theme[cpu_start]="#9ece6a"
theme[cpu_mid]="#e0af68"
theme[cpu_end]="#f7768e"
# Mem/Disk free meter
theme[free_start]="#9ece6a"
theme[free_mid]="#e0af68"
theme[free_end]="#f7768e"
# Mem/Disk cached meter
theme[cached_start]="#9ece6a"
theme[cached_mid]="#e0af68"
theme[cached_end]="#f7768e"
# Mem/Disk available meter
theme[available_start]="#9ece6a"
theme[available_mid]="#e0af68"
theme[available_end]="#f7768e"
# Mem/Disk used meter
theme[used_start]="#9ece6a"
theme[used_mid]="#e0af68"
theme[used_end]="#f7768e"
# Download graph colors
theme[download_start]="#9ece6a"
theme[download_mid]="#e0af68"
theme[download_end]="#f7768e"
# Upload graph colors
theme[upload_start]="#9ece6a"
theme[upload_mid]="#e0af68"
theme[upload_end]="#f7768e"

81
themes/tokyo-storm.theme Normal file
View file

@ -0,0 +1,81 @@
# Theme: tokyo-storm
# By: Pascal Jaeger
# Main bg
theme[main_bg]="#24283b"
# Main text color
theme[main_fg]="#cfc9c2"
# Title color for boxes
theme[title]="#cfc9c2"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#7dcfff"
# Background color of selected item in processes box
theme[selected_bg]="#414868"
# Foreground color of selected item in processes box
theme[selected_fg]="#cfc9c2"
# Color of inactive/disabled text
theme[inactive_fg]="#565f89"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#7dcfff"
# Cpu box outline color
theme[cpu_box]="#565f89"
# Memory/disks box outline color
theme[mem_box]="#565f89"
# Net up/down box outline color
theme[net_box]="#565f89"
# Processes box outline color
theme[proc_box]="#565f89"
# Box divider line and small boxes line color
theme[div_line]="#565f89"
# Temperature graph colors
theme[temp_start]="#9ece6a"
theme[temp_mid]="#e0af68"
theme[temp_end]="#f7768e"
# CPU graph colors
theme[cpu_start]="#9ece6a"
theme[cpu_mid]="#e0af68"
theme[cpu_end]="#f7768e"
# Mem/Disk free meter
theme[free_start]="#9ece6a"
theme[free_mid]="#e0af68"
theme[free_end]="#f7768e"
# Mem/Disk cached meter
theme[cached_start]="#9ece6a"
theme[cached_mid]="#e0af68"
theme[cached_end]="#f7768e"
# Mem/Disk available meter
theme[available_start]="#9ece6a"
theme[available_mid]="#e0af68"
theme[available_end]="#f7768e"
# Mem/Disk used meter
theme[used_start]="#9ece6a"
theme[used_mid]="#e0af68"
theme[used_end]="#f7768e"
# Download graph colors
theme[download_start]="#9ece6a"
theme[download_mid]="#e0af68"
theme[download_end]="#f7768e"
# Upload graph colors
theme[upload_start]="#9ece6a"
theme[upload_mid]="#e0af68"
theme[upload_end]="#f7768e"

View file

@ -0,0 +1,89 @@
#Nord theme but using the Tomorrow Night palette
#by Appuchia <contact@appu.ltd>
# 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]="#1d1f21"
# Main text color
theme[main_fg]="#c5c8c6"
# Title color for boxes
theme[title]="#c5c8c6"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#81beb7"
# Background color of selected item in processes box
theme[selected_bg]="#282a2e"
# Foreground color of selected item in processes box
theme[selected_fg]="#c5c8c6"
# Color of inactive/disabled text
theme[inactive_fg]="#373b41"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#969896"
# Cpu box outline color
theme[cpu_box]="#81a2be"
# Memory/disks box outline color
theme[mem_box]="#81a2be"
# Net up/down box outline color
theme[net_box]="#81a2be"
# Processes box outline color
theme[proc_box]="#81a2be"
# Box divider line and small boxes line color
theme[div_line]="#81a2be"
# Temperature graph colors
theme[temp_start]="#b5bd68"
theme[temp_mid]="#f0c674"
theme[temp_end]="#cc6666"
# CPU graph colors
theme[cpu_start]="#b5bd68"
theme[cpu_mid]="#f0c674"
theme[cpu_end]="#cc6666"
# Mem/Disk free meter
theme[free_start]="#b5bd68"
theme[free_mid]="#f0c674"
theme[free_end]="#cc6666"
# Mem/Disk cached meter
theme[cached_start]="#b5bd68"
theme[cached_mid]="#f0c674"
theme[cached_end]="#cc6666"
# Mem/Disk available meter
theme[available_start]="#b5bd68"
theme[available_mid]="#f0c674"
theme[available_end]="#cc6666"
# Mem/Disk used meter
theme[used_start]="#b5bd68"
theme[used_mid]="#f0c674"
theme[used_end]="#cc6666"
# Download graph colors
theme[download_start]="#b5bd68"
theme[download_mid]="#f0c674"
theme[download_end]="#cc6666"
# Upload graph colors
theme[upload_start]="#b5bd68"
theme[upload_mid]="#f0c674"
theme[upload_end]="#cc6666"