diff --git a/.editorconfig b/.editorconfig
index 61b7846..63202f0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,3 +1,7 @@
[*.{cpp,h,sh,md,cfg,sample}]
indent_style = tab
indent_size = 4
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index fd2bf38..3c937d1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -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:
diff --git a/.github/workflows/continuous-build-linux.yml b/.github/workflows/continuous-build-linux.yml
new file mode 100644
index 0000000..af32d65
--- /dev/null
+++ b/.github/workflows/continuous-build-linux.yml
@@ -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/**'
diff --git a/.github/workflows/continuous-build-macos.yml b/.github/workflows/continuous-build-macos.yml
new file mode 100644
index 0000000..2cb5e4a
--- /dev/null
+++ b/.github/workflows/continuous-build-macos.yml
@@ -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/*'
diff --git a/.gitignore b/.gitignore
index 3817f1b..fc1994b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,4 +49,8 @@ stage/
build
bin
btop
-.*/
\ No newline at end of file
+.*/
+
+
+#do not ignore .github directory
+!.github
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7354364..dacbb53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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()
diff --git a/Img/icon.png b/Img/icon.png
new file mode 100644
index 0000000..cba4890
Binary files /dev/null and b/Img/icon.png differ
diff --git a/Img/icon.svg b/Img/icon.svg
new file mode 100644
index 0000000..402b842
--- /dev/null
+++ b/Img/icon.svg
@@ -0,0 +1,111 @@
+
+
diff --git a/Img/logo.png b/Img/logo.png
index 9704fb5..7e9cb7b 100644
Binary files a/Img/logo.png and b/Img/logo.png differ
diff --git a/Img/logo.svg b/Img/logo.svg
new file mode 100644
index 0000000..951858f
--- /dev/null
+++ b/Img/logo.svg
@@ -0,0 +1,44 @@
+
+
diff --git a/Makefile b/Makefile
index e6b4d44..d1bc95d 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 596dc42..3520962 100644
--- a/README.md
+++ b/README.md
@@ -5,17 +5,20 @@
![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!
+
+
+More...
+
+##### 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.
+
+
## 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=` 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=` 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=` 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"
diff --git a/btop.desktop b/btop.desktop
new file mode 100644
index 0000000..3771b21
--- /dev/null
+++ b/btop.desktop
@@ -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
diff --git a/include/robin_hood.h b/include/robin_hood.h
index 511a308..0af031f 100644
--- a/include/robin_hood.h
+++ b/include/robin_hood.h
@@ -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
#include
@@ -1820,6 +1820,12 @@ public:
InsertionState::key_found != idxAndState.second);
}
+ template
+ iterator emplace_hint(const_iterator position, Args&&... args) {
+ (void)position;
+ return emplace(std::forward(args)...).first;
+ }
+
template
std::pair try_emplace(const key_type& key, Args&&... args) {
return try_emplace_impl(key, std::forward(args)...);
@@ -1831,16 +1837,15 @@ public:
}
template
- std::pair 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)...);
+ return try_emplace_impl(key, std::forward(args)...).first;
}
template
- std::pair 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)...);
+ return try_emplace_impl(std::move(key), std::forward(args)...).first;
}
template
@@ -1854,16 +1859,15 @@ public:
}
template
- std::pair 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(obj));
+ return insertOrAssignImpl(key, std::forward(obj)).first;
}
template
- std::pair 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(obj));
+ return insertOrAssignImpl(std::move(key), std::forward(obj)).first;
}
std::pair 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 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(
- detail::assertNotNull(std::calloc(1, numBytesTotal)));
+ detail::assertNotNull(std::malloc(numBytesTotal)));
mInfo = reinterpret_cast(mKeyVals + numElementsWithBuffer);
+ std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node));
// set sentinel
mInfo[numElementsWithBuffer] = 1;
diff --git a/include/widechar_width.hpp b/include/widechar_width.hpp
new file mode 100644
index 0000000..a5b4c85
--- /dev/null
+++ b/include/widechar_width.hpp
@@ -0,0 +1,1491 @@
+/**
+ * widechar_width.h, generated on 2022-02-11.
+ * See https://github.com/ridiculousfish/widecharwidth/
+ *
+ * SHA1 file hashes:
+ * UnicodeData.txt: 8a5c26bfb27df8cfab23cf2c34c62d8d3075ae4d
+ * EastAsianWidth.txt: 8ec36ccac3852bf0c2f02e37c6151551cd14db72
+ * emoji-data.txt: 3f0ec08c001c4bc6df0b07d01068fc73808bfb4c
+ */
+
+#ifndef WIDECHAR_WIDTH_H
+#define WIDECHAR_WIDTH_H
+
+#include
+#include
+#include
+#include
+
+namespace utf8 {
+
+/* Special width values */
+enum {
+ widechar_nonprint = 0, // The character is not printable.
+ widechar_combining = 0, // The character is a zero-width combiner.
+ widechar_ambiguous = 1, // The character is East-Asian ambiguous width.
+ widechar_private_use = 1, // The character is for private use.
+ widechar_unassigned = 0, // The character is unassigned.
+ widechar_widened_in_9 = 2, // Width is 1 in Unicode 8, 2 in Unicode 9+.
+ widechar_non_character = 0 // The character is a noncharacter.
+};
+
+/* An inclusive range of characters. */
+struct widechar_range {
+ uint32_t lo;
+ uint32_t hi;
+};
+
+/* Simple ASCII characters - used a lot, so we check them first. */
+static const struct widechar_range widechar_ascii_table[] = {
+ {0x00020, 0x0007E}
+};
+
+/* Private usage range. */
+static const struct widechar_range widechar_private_table[] = {
+ {0x0E000, 0x0F8FF},
+ {0xF0000, 0xFFFFD},
+ {0x100000, 0x10FFFD}
+};
+
+/* Nonprinting characters. */
+static const struct widechar_range widechar_nonprint_table[] = {
+ {0x00000, 0x0001F},
+ {0x0007F, 0x0009F},
+ {0x000AD, 0x000AD},
+ {0x00600, 0x00605},
+ {0x0061C, 0x0061C},
+ {0x006DD, 0x006DD},
+ {0x0070F, 0x0070F},
+ {0x00890, 0x00891},
+ {0x008E2, 0x008E2},
+ {0x0180E, 0x0180E},
+ {0x0200B, 0x0200F},
+ {0x02028, 0x0202E},
+ {0x02060, 0x02064},
+ {0x02066, 0x0206F},
+ {0x0D800, 0x0DFFF},
+ {0x0FEFF, 0x0FEFF},
+ {0x0FFF9, 0x0FFFB},
+ {0x110BD, 0x110BD},
+ {0x110CD, 0x110CD},
+ {0x13430, 0x13438},
+ {0x1BCA0, 0x1BCA3},
+ {0x1D173, 0x1D17A},
+ {0xE0001, 0xE0001},
+ {0xE0020, 0xE007F}
+};
+
+/* Width 0 combining marks. */
+static const struct widechar_range widechar_combining_table[] = {
+ {0x00300, 0x0036F},
+ {0x00483, 0x00489},
+ {0x00591, 0x005BD},
+ {0x005BF, 0x005BF},
+ {0x005C1, 0x005C2},
+ {0x005C4, 0x005C5},
+ {0x005C7, 0x005C7},
+ {0x00610, 0x0061A},
+ {0x0064B, 0x0065F},
+ {0x00670, 0x00670},
+ {0x006D6, 0x006DC},
+ {0x006DF, 0x006E4},
+ {0x006E7, 0x006E8},
+ {0x006EA, 0x006ED},
+ {0x00711, 0x00711},
+ {0x00730, 0x0074A},
+ {0x007A6, 0x007B0},
+ {0x007EB, 0x007F3},
+ {0x007FD, 0x007FD},
+ {0x00816, 0x00819},
+ {0x0081B, 0x00823},
+ {0x00825, 0x00827},
+ {0x00829, 0x0082D},
+ {0x00859, 0x0085B},
+ {0x00898, 0x0089F},
+ {0x008CA, 0x008E1},
+ {0x008E3, 0x00903},
+ {0x0093A, 0x0093C},
+ {0x0093E, 0x0094F},
+ {0x00951, 0x00957},
+ {0x00962, 0x00963},
+ {0x00981, 0x00983},
+ {0x009BC, 0x009BC},
+ {0x009BE, 0x009C4},
+ {0x009C7, 0x009C8},
+ {0x009CB, 0x009CD},
+ {0x009D7, 0x009D7},
+ {0x009E2, 0x009E3},
+ {0x009FE, 0x009FE},
+ {0x00A01, 0x00A03},
+ {0x00A3C, 0x00A3C},
+ {0x00A3E, 0x00A42},
+ {0x00A47, 0x00A48},
+ {0x00A4B, 0x00A4D},
+ {0x00A51, 0x00A51},
+ {0x00A70, 0x00A71},
+ {0x00A75, 0x00A75},
+ {0x00A81, 0x00A83},
+ {0x00ABC, 0x00ABC},
+ {0x00ABE, 0x00AC5},
+ {0x00AC7, 0x00AC9},
+ {0x00ACB, 0x00ACD},
+ {0x00AE2, 0x00AE3},
+ {0x00AFA, 0x00AFF},
+ {0x00B01, 0x00B03},
+ {0x00B3C, 0x00B3C},
+ {0x00B3E, 0x00B44},
+ {0x00B47, 0x00B48},
+ {0x00B4B, 0x00B4D},
+ {0x00B55, 0x00B57},
+ {0x00B62, 0x00B63},
+ {0x00B82, 0x00B82},
+ {0x00BBE, 0x00BC2},
+ {0x00BC6, 0x00BC8},
+ {0x00BCA, 0x00BCD},
+ {0x00BD7, 0x00BD7},
+ {0x00C00, 0x00C04},
+ {0x00C3C, 0x00C3C},
+ {0x00C3E, 0x00C44},
+ {0x00C46, 0x00C48},
+ {0x00C4A, 0x00C4D},
+ {0x00C55, 0x00C56},
+ {0x00C62, 0x00C63},
+ {0x00C81, 0x00C83},
+ {0x00CBC, 0x00CBC},
+ {0x00CBE, 0x00CC4},
+ {0x00CC6, 0x00CC8},
+ {0x00CCA, 0x00CCD},
+ {0x00CD5, 0x00CD6},
+ {0x00CE2, 0x00CE3},
+ {0x00D00, 0x00D03},
+ {0x00D3B, 0x00D3C},
+ {0x00D3E, 0x00D44},
+ {0x00D46, 0x00D48},
+ {0x00D4A, 0x00D4D},
+ {0x00D57, 0x00D57},
+ {0x00D62, 0x00D63},
+ {0x00D81, 0x00D83},
+ {0x00DCA, 0x00DCA},
+ {0x00DCF, 0x00DD4},
+ {0x00DD6, 0x00DD6},
+ {0x00DD8, 0x00DDF},
+ {0x00DF2, 0x00DF3},
+ {0x00E31, 0x00E31},
+ {0x00E34, 0x00E3A},
+ {0x00E47, 0x00E4E},
+ {0x00EB1, 0x00EB1},
+ {0x00EB4, 0x00EBC},
+ {0x00EC8, 0x00ECD},
+ {0x00F18, 0x00F19},
+ {0x00F35, 0x00F35},
+ {0x00F37, 0x00F37},
+ {0x00F39, 0x00F39},
+ {0x00F3E, 0x00F3F},
+ {0x00F71, 0x00F84},
+ {0x00F86, 0x00F87},
+ {0x00F8D, 0x00F97},
+ {0x00F99, 0x00FBC},
+ {0x00FC6, 0x00FC6},
+ {0x0102B, 0x0103E},
+ {0x01056, 0x01059},
+ {0x0105E, 0x01060},
+ {0x01062, 0x01064},
+ {0x01067, 0x0106D},
+ {0x01071, 0x01074},
+ {0x01082, 0x0108D},
+ {0x0108F, 0x0108F},
+ {0x0109A, 0x0109D},
+ {0x0135D, 0x0135F},
+ {0x01712, 0x01715},
+ {0x01732, 0x01734},
+ {0x01752, 0x01753},
+ {0x01772, 0x01773},
+ {0x017B4, 0x017D3},
+ {0x017DD, 0x017DD},
+ {0x0180B, 0x0180D},
+ {0x0180F, 0x0180F},
+ {0x01885, 0x01886},
+ {0x018A9, 0x018A9},
+ {0x01920, 0x0192B},
+ {0x01930, 0x0193B},
+ {0x01A17, 0x01A1B},
+ {0x01A55, 0x01A5E},
+ {0x01A60, 0x01A7C},
+ {0x01A7F, 0x01A7F},
+ {0x01AB0, 0x01ACE},
+ {0x01B00, 0x01B04},
+ {0x01B34, 0x01B44},
+ {0x01B6B, 0x01B73},
+ {0x01B80, 0x01B82},
+ {0x01BA1, 0x01BAD},
+ {0x01BE6, 0x01BF3},
+ {0x01C24, 0x01C37},
+ {0x01CD0, 0x01CD2},
+ {0x01CD4, 0x01CE8},
+ {0x01CED, 0x01CED},
+ {0x01CF4, 0x01CF4},
+ {0x01CF7, 0x01CF9},
+ {0x01DC0, 0x01DFF},
+ {0x020D0, 0x020F0},
+ {0x02CEF, 0x02CF1},
+ {0x02D7F, 0x02D7F},
+ {0x02DE0, 0x02DFF},
+ {0x0302A, 0x0302F},
+ {0x03099, 0x0309A},
+ {0x0A66F, 0x0A672},
+ {0x0A674, 0x0A67D},
+ {0x0A69E, 0x0A69F},
+ {0x0A6F0, 0x0A6F1},
+ {0x0A802, 0x0A802},
+ {0x0A806, 0x0A806},
+ {0x0A80B, 0x0A80B},
+ {0x0A823, 0x0A827},
+ {0x0A82C, 0x0A82C},
+ {0x0A880, 0x0A881},
+ {0x0A8B4, 0x0A8C5},
+ {0x0A8E0, 0x0A8F1},
+ {0x0A8FF, 0x0A8FF},
+ {0x0A926, 0x0A92D},
+ {0x0A947, 0x0A953},
+ {0x0A980, 0x0A983},
+ {0x0A9B3, 0x0A9C0},
+ {0x0A9E5, 0x0A9E5},
+ {0x0AA29, 0x0AA36},
+ {0x0AA43, 0x0AA43},
+ {0x0AA4C, 0x0AA4D},
+ {0x0AA7B, 0x0AA7D},
+ {0x0AAB0, 0x0AAB0},
+ {0x0AAB2, 0x0AAB4},
+ {0x0AAB7, 0x0AAB8},
+ {0x0AABE, 0x0AABF},
+ {0x0AAC1, 0x0AAC1},
+ {0x0AAEB, 0x0AAEF},
+ {0x0AAF5, 0x0AAF6},
+ {0x0ABE3, 0x0ABEA},
+ {0x0ABEC, 0x0ABED},
+ {0x0FB1E, 0x0FB1E},
+ {0x0FE00, 0x0FE0F},
+ {0x0FE20, 0x0FE2F},
+ {0x101FD, 0x101FD},
+ {0x102E0, 0x102E0},
+ {0x10376, 0x1037A},
+ {0x10A01, 0x10A03},
+ {0x10A05, 0x10A06},
+ {0x10A0C, 0x10A0F},
+ {0x10A38, 0x10A3A},
+ {0x10A3F, 0x10A3F},
+ {0x10AE5, 0x10AE6},
+ {0x10D24, 0x10D27},
+ {0x10EAB, 0x10EAC},
+ {0x10F46, 0x10F50},
+ {0x10F82, 0x10F85},
+ {0x11000, 0x11002},
+ {0x11038, 0x11046},
+ {0x11070, 0x11070},
+ {0x11073, 0x11074},
+ {0x1107F, 0x11082},
+ {0x110B0, 0x110BA},
+ {0x110C2, 0x110C2},
+ {0x11100, 0x11102},
+ {0x11127, 0x11134},
+ {0x11145, 0x11146},
+ {0x11173, 0x11173},
+ {0x11180, 0x11182},
+ {0x111B3, 0x111C0},
+ {0x111C9, 0x111CC},
+ {0x111CE, 0x111CF},
+ {0x1122C, 0x11237},
+ {0x1123E, 0x1123E},
+ {0x112DF, 0x112EA},
+ {0x11300, 0x11303},
+ {0x1133B, 0x1133C},
+ {0x1133E, 0x11344},
+ {0x11347, 0x11348},
+ {0x1134B, 0x1134D},
+ {0x11357, 0x11357},
+ {0x11362, 0x11363},
+ {0x11366, 0x1136C},
+ {0x11370, 0x11374},
+ {0x11435, 0x11446},
+ {0x1145E, 0x1145E},
+ {0x114B0, 0x114C3},
+ {0x115AF, 0x115B5},
+ {0x115B8, 0x115C0},
+ {0x115DC, 0x115DD},
+ {0x11630, 0x11640},
+ {0x116AB, 0x116B7},
+ {0x1171D, 0x1172B},
+ {0x1182C, 0x1183A},
+ {0x11930, 0x11935},
+ {0x11937, 0x11938},
+ {0x1193B, 0x1193E},
+ {0x11940, 0x11940},
+ {0x11942, 0x11943},
+ {0x119D1, 0x119D7},
+ {0x119DA, 0x119E0},
+ {0x119E4, 0x119E4},
+ {0x11A01, 0x11A0A},
+ {0x11A33, 0x11A39},
+ {0x11A3B, 0x11A3E},
+ {0x11A47, 0x11A47},
+ {0x11A51, 0x11A5B},
+ {0x11A8A, 0x11A99},
+ {0x11C2F, 0x11C36},
+ {0x11C38, 0x11C3F},
+ {0x11C92, 0x11CA7},
+ {0x11CA9, 0x11CB6},
+ {0x11D31, 0x11D36},
+ {0x11D3A, 0x11D3A},
+ {0x11D3C, 0x11D3D},
+ {0x11D3F, 0x11D45},
+ {0x11D47, 0x11D47},
+ {0x11D8A, 0x11D8E},
+ {0x11D90, 0x11D91},
+ {0x11D93, 0x11D97},
+ {0x11EF3, 0x11EF6},
+ {0x16AF0, 0x16AF4},
+ {0x16B30, 0x16B36},
+ {0x16F4F, 0x16F4F},
+ {0x16F51, 0x16F87},
+ {0x16F8F, 0x16F92},
+ {0x16FE4, 0x16FE4},
+ {0x16FF0, 0x16FF1},
+ {0x1BC9D, 0x1BC9E},
+ {0x1CF00, 0x1CF2D},
+ {0x1CF30, 0x1CF46},
+ {0x1D165, 0x1D169},
+ {0x1D16D, 0x1D172},
+ {0x1D17B, 0x1D182},
+ {0x1D185, 0x1D18B},
+ {0x1D1AA, 0x1D1AD},
+ {0x1D242, 0x1D244},
+ {0x1DA00, 0x1DA36},
+ {0x1DA3B, 0x1DA6C},
+ {0x1DA75, 0x1DA75},
+ {0x1DA84, 0x1DA84},
+ {0x1DA9B, 0x1DA9F},
+ {0x1DAA1, 0x1DAAF},
+ {0x1E000, 0x1E006},
+ {0x1E008, 0x1E018},
+ {0x1E01B, 0x1E021},
+ {0x1E023, 0x1E024},
+ {0x1E026, 0x1E02A},
+ {0x1E130, 0x1E136},
+ {0x1E2AE, 0x1E2AE},
+ {0x1E2EC, 0x1E2EF},
+ {0x1E8D0, 0x1E8D6},
+ {0x1E944, 0x1E94A},
+ {0xE0100, 0xE01EF}
+};
+
+/* Width 0 combining letters. */
+static const struct widechar_range widechar_combiningletters_table[] = {
+ {0x01160, 0x011FF},
+ {0x0D7B0, 0x0D7FF}
+};
+
+/* Width 2 characters. */
+static const struct widechar_range widechar_doublewide_table[] = {
+ {0x01100, 0x0115F},
+ {0x02329, 0x0232A},
+ {0x02E80, 0x02E99},
+ {0x02E9B, 0x02EF3},
+ {0x02F00, 0x02FD5},
+ {0x02FF0, 0x02FFB},
+ {0x03000, 0x0303E},
+ {0x03041, 0x03096},
+ {0x03099, 0x030FF},
+ {0x03105, 0x0312F},
+ {0x03131, 0x0318E},
+ {0x03190, 0x031E3},
+ {0x031F0, 0x0321E},
+ {0x03220, 0x03247},
+ {0x03250, 0x04DBF},
+ {0x04E00, 0x0A48C},
+ {0x0A490, 0x0A4C6},
+ {0x0A960, 0x0A97C},
+ {0x0AC00, 0x0D7A3},
+ {0x0F900, 0x0FAFF},
+ {0x0FE10, 0x0FE19},
+ {0x0FE30, 0x0FE52},
+ {0x0FE54, 0x0FE66},
+ {0x0FE68, 0x0FE6B},
+ {0x0FF01, 0x0FF60},
+ {0x0FFE0, 0x0FFE6},
+ {0x16FE0, 0x16FE4},
+ {0x16FF0, 0x16FF1},
+ {0x17000, 0x187F7},
+ {0x18800, 0x18CD5},
+ {0x18D00, 0x18D08},
+ {0x1AFF0, 0x1AFF3},
+ {0x1AFF5, 0x1AFFB},
+ {0x1AFFD, 0x1AFFE},
+ {0x1B000, 0x1B122},
+ {0x1B150, 0x1B152},
+ {0x1B164, 0x1B167},
+ {0x1B170, 0x1B2FB},
+ {0x1F200, 0x1F200},
+ {0x1F202, 0x1F202},
+ {0x1F210, 0x1F219},
+ {0x1F21B, 0x1F22E},
+ {0x1F230, 0x1F231},
+ {0x1F237, 0x1F237},
+ {0x1F23B, 0x1F23B},
+ {0x1F240, 0x1F248},
+ {0x1F260, 0x1F265},
+ {0x1F57A, 0x1F57A},
+ {0x1F5A4, 0x1F5A4},
+ {0x1F6D1, 0x1F6D2},
+ {0x1F6D5, 0x1F6D7},
+ {0x1F6DD, 0x1F6DF},
+ {0x1F6F4, 0x1F6FC},
+ {0x1F7E0, 0x1F7EB},
+ {0x1F7F0, 0x1F7F0},
+ {0x1F90C, 0x1F90F},
+ {0x1F919, 0x1F93A},
+ {0x1F93C, 0x1F945},
+ {0x1F947, 0x1F97F},
+ {0x1F985, 0x1F9BF},
+ {0x1F9C1, 0x1F9FF},
+ {0x1FA70, 0x1FA74},
+ {0x1FA78, 0x1FA7C},
+ {0x1FA80, 0x1FA86},
+ {0x1FA90, 0x1FAAC},
+ {0x1FAB0, 0x1FABA},
+ {0x1FAC0, 0x1FAC5},
+ {0x1FAD0, 0x1FAD9},
+ {0x1FAE0, 0x1FAE7},
+ {0x1FAF0, 0x1FAF6},
+ {0x20000, 0x2FFFD},
+ {0x30000, 0x3FFFD}
+};
+
+/* Ambiguous-width characters. */
+static const struct widechar_range widechar_ambiguous_table[] = {
+ {0x000A1, 0x000A1},
+ {0x000A4, 0x000A4},
+ {0x000A7, 0x000A8},
+ {0x000AA, 0x000AA},
+ {0x000AD, 0x000AE},
+ {0x000B0, 0x000B4},
+ {0x000B6, 0x000BA},
+ {0x000BC, 0x000BF},
+ {0x000C6, 0x000C6},
+ {0x000D0, 0x000D0},
+ {0x000D7, 0x000D8},
+ {0x000DE, 0x000E1},
+ {0x000E6, 0x000E6},
+ {0x000E8, 0x000EA},
+ {0x000EC, 0x000ED},
+ {0x000F0, 0x000F0},
+ {0x000F2, 0x000F3},
+ {0x000F7, 0x000FA},
+ {0x000FC, 0x000FC},
+ {0x000FE, 0x000FE},
+ {0x00101, 0x00101},
+ {0x00111, 0x00111},
+ {0x00113, 0x00113},
+ {0x0011B, 0x0011B},
+ {0x00126, 0x00127},
+ {0x0012B, 0x0012B},
+ {0x00131, 0x00133},
+ {0x00138, 0x00138},
+ {0x0013F, 0x00142},
+ {0x00144, 0x00144},
+ {0x00148, 0x0014B},
+ {0x0014D, 0x0014D},
+ {0x00152, 0x00153},
+ {0x00166, 0x00167},
+ {0x0016B, 0x0016B},
+ {0x001CE, 0x001CE},
+ {0x001D0, 0x001D0},
+ {0x001D2, 0x001D2},
+ {0x001D4, 0x001D4},
+ {0x001D6, 0x001D6},
+ {0x001D8, 0x001D8},
+ {0x001DA, 0x001DA},
+ {0x001DC, 0x001DC},
+ {0x00251, 0x00251},
+ {0x00261, 0x00261},
+ {0x002C4, 0x002C4},
+ {0x002C7, 0x002C7},
+ {0x002C9, 0x002CB},
+ {0x002CD, 0x002CD},
+ {0x002D0, 0x002D0},
+ {0x002D8, 0x002DB},
+ {0x002DD, 0x002DD},
+ {0x002DF, 0x002DF},
+ {0x00300, 0x0036F},
+ {0x00391, 0x003A1},
+ {0x003A3, 0x003A9},
+ {0x003B1, 0x003C1},
+ {0x003C3, 0x003C9},
+ {0x00401, 0x00401},
+ {0x00410, 0x0044F},
+ {0x00451, 0x00451},
+ {0x02010, 0x02010},
+ {0x02013, 0x02016},
+ {0x02018, 0x02019},
+ {0x0201C, 0x0201D},
+ {0x02020, 0x02022},
+ {0x02024, 0x02027},
+ {0x02030, 0x02030},
+ {0x02032, 0x02033},
+ {0x02035, 0x02035},
+ {0x0203B, 0x0203B},
+ {0x0203E, 0x0203E},
+ {0x02074, 0x02074},
+ {0x0207F, 0x0207F},
+ {0x02081, 0x02084},
+ {0x020AC, 0x020AC},
+ {0x02103, 0x02103},
+ {0x02105, 0x02105},
+ {0x02109, 0x02109},
+ {0x02113, 0x02113},
+ {0x02116, 0x02116},
+ {0x02121, 0x02122},
+ {0x02126, 0x02126},
+ {0x0212B, 0x0212B},
+ {0x02153, 0x02154},
+ {0x0215B, 0x0215E},
+ {0x02160, 0x0216B},
+ {0x02170, 0x02179},
+ {0x02189, 0x02189},
+ {0x02190, 0x02199},
+ {0x021B8, 0x021B9},
+ {0x021D2, 0x021D2},
+ {0x021D4, 0x021D4},
+ {0x021E7, 0x021E7},
+ {0x02200, 0x02200},
+ {0x02202, 0x02203},
+ {0x02207, 0x02208},
+ {0x0220B, 0x0220B},
+ {0x0220F, 0x0220F},
+ {0x02211, 0x02211},
+ {0x02215, 0x02215},
+ {0x0221A, 0x0221A},
+ {0x0221D, 0x02220},
+ {0x02223, 0x02223},
+ {0x02225, 0x02225},
+ {0x02227, 0x0222C},
+ {0x0222E, 0x0222E},
+ {0x02234, 0x02237},
+ {0x0223C, 0x0223D},
+ {0x02248, 0x02248},
+ {0x0224C, 0x0224C},
+ {0x02252, 0x02252},
+ {0x02260, 0x02261},
+ {0x02264, 0x02267},
+ {0x0226A, 0x0226B},
+ {0x0226E, 0x0226F},
+ {0x02282, 0x02283},
+ {0x02286, 0x02287},
+ {0x02295, 0x02295},
+ {0x02299, 0x02299},
+ {0x022A5, 0x022A5},
+ {0x022BF, 0x022BF},
+ {0x02312, 0x02312},
+ {0x02460, 0x024E9},
+ {0x024EB, 0x0254B},
+ {0x02550, 0x02573},
+ {0x02580, 0x0258F},
+ {0x02592, 0x02595},
+ {0x025A0, 0x025A1},
+ {0x025A3, 0x025A9},
+ {0x025B2, 0x025B3},
+ {0x025B6, 0x025B7},
+ {0x025BC, 0x025BD},
+ {0x025C0, 0x025C1},
+ {0x025C6, 0x025C8},
+ {0x025CB, 0x025CB},
+ {0x025CE, 0x025D1},
+ {0x025E2, 0x025E5},
+ {0x025EF, 0x025EF},
+ {0x02605, 0x02606},
+ {0x02609, 0x02609},
+ {0x0260E, 0x0260F},
+ {0x0261C, 0x0261C},
+ {0x0261E, 0x0261E},
+ {0x02640, 0x02640},
+ {0x02642, 0x02642},
+ {0x02660, 0x02661},
+ {0x02663, 0x02665},
+ {0x02667, 0x0266A},
+ {0x0266C, 0x0266D},
+ {0x0266F, 0x0266F},
+ {0x0269E, 0x0269F},
+ {0x026BF, 0x026BF},
+ {0x026C6, 0x026CD},
+ {0x026CF, 0x026D3},
+ {0x026D5, 0x026E1},
+ {0x026E3, 0x026E3},
+ {0x026E8, 0x026E9},
+ {0x026EB, 0x026F1},
+ {0x026F4, 0x026F4},
+ {0x026F6, 0x026F9},
+ {0x026FB, 0x026FC},
+ {0x026FE, 0x026FF},
+ {0x0273D, 0x0273D},
+ {0x02776, 0x0277F},
+ {0x02B56, 0x02B59},
+ {0x03248, 0x0324F},
+ {0x0E000, 0x0F8FF},
+ {0x0FE00, 0x0FE0F},
+ {0x0FFFD, 0x0FFFD},
+ {0x1F100, 0x1F10A},
+ {0x1F110, 0x1F12D},
+ {0x1F130, 0x1F169},
+ {0x1F170, 0x1F18D},
+ {0x1F18F, 0x1F190},
+ {0x1F19B, 0x1F1AC},
+ {0xE0100, 0xE01EF},
+ {0xF0000, 0xFFFFD},
+ {0x100000, 0x10FFFD}
+};
+
+/* Unassigned characters. */
+static const struct widechar_range widechar_unassigned_table[] = {
+ {0x00378, 0x00379},
+ {0x00380, 0x00383},
+ {0x0038B, 0x0038B},
+ {0x0038D, 0x0038D},
+ {0x003A2, 0x003A2},
+ {0x00530, 0x00530},
+ {0x00557, 0x00558},
+ {0x0058B, 0x0058C},
+ {0x00590, 0x00590},
+ {0x005C8, 0x005CF},
+ {0x005EB, 0x005EE},
+ {0x005F5, 0x005FF},
+ {0x0070E, 0x0070E},
+ {0x0074B, 0x0074C},
+ {0x007B2, 0x007BF},
+ {0x007FB, 0x007FC},
+ {0x0082E, 0x0082F},
+ {0x0083F, 0x0083F},
+ {0x0085C, 0x0085D},
+ {0x0085F, 0x0085F},
+ {0x0086B, 0x0086F},
+ {0x0088F, 0x0088F},
+ {0x00892, 0x00897},
+ {0x00984, 0x00984},
+ {0x0098D, 0x0098E},
+ {0x00991, 0x00992},
+ {0x009A9, 0x009A9},
+ {0x009B1, 0x009B1},
+ {0x009B3, 0x009B5},
+ {0x009BA, 0x009BB},
+ {0x009C5, 0x009C6},
+ {0x009C9, 0x009CA},
+ {0x009CF, 0x009D6},
+ {0x009D8, 0x009DB},
+ {0x009DE, 0x009DE},
+ {0x009E4, 0x009E5},
+ {0x009FF, 0x00A00},
+ {0x00A04, 0x00A04},
+ {0x00A0B, 0x00A0E},
+ {0x00A11, 0x00A12},
+ {0x00A29, 0x00A29},
+ {0x00A31, 0x00A31},
+ {0x00A34, 0x00A34},
+ {0x00A37, 0x00A37},
+ {0x00A3A, 0x00A3B},
+ {0x00A3D, 0x00A3D},
+ {0x00A43, 0x00A46},
+ {0x00A49, 0x00A4A},
+ {0x00A4E, 0x00A50},
+ {0x00A52, 0x00A58},
+ {0x00A5D, 0x00A5D},
+ {0x00A5F, 0x00A65},
+ {0x00A77, 0x00A80},
+ {0x00A84, 0x00A84},
+ {0x00A8E, 0x00A8E},
+ {0x00A92, 0x00A92},
+ {0x00AA9, 0x00AA9},
+ {0x00AB1, 0x00AB1},
+ {0x00AB4, 0x00AB4},
+ {0x00ABA, 0x00ABB},
+ {0x00AC6, 0x00AC6},
+ {0x00ACA, 0x00ACA},
+ {0x00ACE, 0x00ACF},
+ {0x00AD1, 0x00ADF},
+ {0x00AE4, 0x00AE5},
+ {0x00AF2, 0x00AF8},
+ {0x00B00, 0x00B00},
+ {0x00B04, 0x00B04},
+ {0x00B0D, 0x00B0E},
+ {0x00B11, 0x00B12},
+ {0x00B29, 0x00B29},
+ {0x00B31, 0x00B31},
+ {0x00B34, 0x00B34},
+ {0x00B3A, 0x00B3B},
+ {0x00B45, 0x00B46},
+ {0x00B49, 0x00B4A},
+ {0x00B4E, 0x00B54},
+ {0x00B58, 0x00B5B},
+ {0x00B5E, 0x00B5E},
+ {0x00B64, 0x00B65},
+ {0x00B78, 0x00B81},
+ {0x00B84, 0x00B84},
+ {0x00B8B, 0x00B8D},
+ {0x00B91, 0x00B91},
+ {0x00B96, 0x00B98},
+ {0x00B9B, 0x00B9B},
+ {0x00B9D, 0x00B9D},
+ {0x00BA0, 0x00BA2},
+ {0x00BA5, 0x00BA7},
+ {0x00BAB, 0x00BAD},
+ {0x00BBA, 0x00BBD},
+ {0x00BC3, 0x00BC5},
+ {0x00BC9, 0x00BC9},
+ {0x00BCE, 0x00BCF},
+ {0x00BD1, 0x00BD6},
+ {0x00BD8, 0x00BE5},
+ {0x00BFB, 0x00BFF},
+ {0x00C0D, 0x00C0D},
+ {0x00C11, 0x00C11},
+ {0x00C29, 0x00C29},
+ {0x00C3A, 0x00C3B},
+ {0x00C45, 0x00C45},
+ {0x00C49, 0x00C49},
+ {0x00C4E, 0x00C54},
+ {0x00C57, 0x00C57},
+ {0x00C5B, 0x00C5C},
+ {0x00C5E, 0x00C5F},
+ {0x00C64, 0x00C65},
+ {0x00C70, 0x00C76},
+ {0x00C8D, 0x00C8D},
+ {0x00C91, 0x00C91},
+ {0x00CA9, 0x00CA9},
+ {0x00CB4, 0x00CB4},
+ {0x00CBA, 0x00CBB},
+ {0x00CC5, 0x00CC5},
+ {0x00CC9, 0x00CC9},
+ {0x00CCE, 0x00CD4},
+ {0x00CD7, 0x00CDC},
+ {0x00CDF, 0x00CDF},
+ {0x00CE4, 0x00CE5},
+ {0x00CF0, 0x00CF0},
+ {0x00CF3, 0x00CFF},
+ {0x00D0D, 0x00D0D},
+ {0x00D11, 0x00D11},
+ {0x00D45, 0x00D45},
+ {0x00D49, 0x00D49},
+ {0x00D50, 0x00D53},
+ {0x00D64, 0x00D65},
+ {0x00D80, 0x00D80},
+ {0x00D84, 0x00D84},
+ {0x00D97, 0x00D99},
+ {0x00DB2, 0x00DB2},
+ {0x00DBC, 0x00DBC},
+ {0x00DBE, 0x00DBF},
+ {0x00DC7, 0x00DC9},
+ {0x00DCB, 0x00DCE},
+ {0x00DD5, 0x00DD5},
+ {0x00DD7, 0x00DD7},
+ {0x00DE0, 0x00DE5},
+ {0x00DF0, 0x00DF1},
+ {0x00DF5, 0x00E00},
+ {0x00E3B, 0x00E3E},
+ {0x00E5C, 0x00E80},
+ {0x00E83, 0x00E83},
+ {0x00E85, 0x00E85},
+ {0x00E8B, 0x00E8B},
+ {0x00EA4, 0x00EA4},
+ {0x00EA6, 0x00EA6},
+ {0x00EBE, 0x00EBF},
+ {0x00EC5, 0x00EC5},
+ {0x00EC7, 0x00EC7},
+ {0x00ECE, 0x00ECF},
+ {0x00EDA, 0x00EDB},
+ {0x00EE0, 0x00EFF},
+ {0x00F48, 0x00F48},
+ {0x00F6D, 0x00F70},
+ {0x00F98, 0x00F98},
+ {0x00FBD, 0x00FBD},
+ {0x00FCD, 0x00FCD},
+ {0x00FDB, 0x00FFF},
+ {0x010C6, 0x010C6},
+ {0x010C8, 0x010CC},
+ {0x010CE, 0x010CF},
+ {0x01249, 0x01249},
+ {0x0124E, 0x0124F},
+ {0x01257, 0x01257},
+ {0x01259, 0x01259},
+ {0x0125E, 0x0125F},
+ {0x01289, 0x01289},
+ {0x0128E, 0x0128F},
+ {0x012B1, 0x012B1},
+ {0x012B6, 0x012B7},
+ {0x012BF, 0x012BF},
+ {0x012C1, 0x012C1},
+ {0x012C6, 0x012C7},
+ {0x012D7, 0x012D7},
+ {0x01311, 0x01311},
+ {0x01316, 0x01317},
+ {0x0135B, 0x0135C},
+ {0x0137D, 0x0137F},
+ {0x0139A, 0x0139F},
+ {0x013F6, 0x013F7},
+ {0x013FE, 0x013FF},
+ {0x0169D, 0x0169F},
+ {0x016F9, 0x016FF},
+ {0x01716, 0x0171E},
+ {0x01737, 0x0173F},
+ {0x01754, 0x0175F},
+ {0x0176D, 0x0176D},
+ {0x01771, 0x01771},
+ {0x01774, 0x0177F},
+ {0x017DE, 0x017DF},
+ {0x017EA, 0x017EF},
+ {0x017FA, 0x017FF},
+ {0x0181A, 0x0181F},
+ {0x01879, 0x0187F},
+ {0x018AB, 0x018AF},
+ {0x018F6, 0x018FF},
+ {0x0191F, 0x0191F},
+ {0x0192C, 0x0192F},
+ {0x0193C, 0x0193F},
+ {0x01941, 0x01943},
+ {0x0196E, 0x0196F},
+ {0x01975, 0x0197F},
+ {0x019AC, 0x019AF},
+ {0x019CA, 0x019CF},
+ {0x019DB, 0x019DD},
+ {0x01A1C, 0x01A1D},
+ {0x01A5F, 0x01A5F},
+ {0x01A7D, 0x01A7E},
+ {0x01A8A, 0x01A8F},
+ {0x01A9A, 0x01A9F},
+ {0x01AAE, 0x01AAF},
+ {0x01ACF, 0x01AFF},
+ {0x01B4D, 0x01B4F},
+ {0x01B7F, 0x01B7F},
+ {0x01BF4, 0x01BFB},
+ {0x01C38, 0x01C3A},
+ {0x01C4A, 0x01C4C},
+ {0x01C89, 0x01C8F},
+ {0x01CBB, 0x01CBC},
+ {0x01CC8, 0x01CCF},
+ {0x01CFB, 0x01CFF},
+ {0x01F16, 0x01F17},
+ {0x01F1E, 0x01F1F},
+ {0x01F46, 0x01F47},
+ {0x01F4E, 0x01F4F},
+ {0x01F58, 0x01F58},
+ {0x01F5A, 0x01F5A},
+ {0x01F5C, 0x01F5C},
+ {0x01F5E, 0x01F5E},
+ {0x01F7E, 0x01F7F},
+ {0x01FB5, 0x01FB5},
+ {0x01FC5, 0x01FC5},
+ {0x01FD4, 0x01FD5},
+ {0x01FDC, 0x01FDC},
+ {0x01FF0, 0x01FF1},
+ {0x01FF5, 0x01FF5},
+ {0x01FFF, 0x01FFF},
+ {0x02065, 0x02065},
+ {0x02072, 0x02073},
+ {0x0208F, 0x0208F},
+ {0x0209D, 0x0209F},
+ {0x020C1, 0x020CF},
+ {0x020F1, 0x020FF},
+ {0x0218C, 0x0218F},
+ {0x02427, 0x0243F},
+ {0x0244B, 0x0245F},
+ {0x02B74, 0x02B75},
+ {0x02B96, 0x02B96},
+ {0x02CF4, 0x02CF8},
+ {0x02D26, 0x02D26},
+ {0x02D28, 0x02D2C},
+ {0x02D2E, 0x02D2F},
+ {0x02D68, 0x02D6E},
+ {0x02D71, 0x02D7E},
+ {0x02D97, 0x02D9F},
+ {0x02DA7, 0x02DA7},
+ {0x02DAF, 0x02DAF},
+ {0x02DB7, 0x02DB7},
+ {0x02DBF, 0x02DBF},
+ {0x02DC7, 0x02DC7},
+ {0x02DCF, 0x02DCF},
+ {0x02DD7, 0x02DD7},
+ {0x02DDF, 0x02DDF},
+ {0x02E5E, 0x02E7F},
+ {0x02E9A, 0x02E9A},
+ {0x02EF4, 0x02EFF},
+ {0x02FD6, 0x02FEF},
+ {0x02FFC, 0x02FFF},
+ {0x03040, 0x03040},
+ {0x03097, 0x03098},
+ {0x03100, 0x03104},
+ {0x03130, 0x03130},
+ {0x0318F, 0x0318F},
+ {0x031E4, 0x031EF},
+ {0x0321F, 0x0321F},
+ {0x03401, 0x04DBE},
+ {0x04E01, 0x09FFE},
+ {0x0A48D, 0x0A48F},
+ {0x0A4C7, 0x0A4CF},
+ {0x0A62C, 0x0A63F},
+ {0x0A6F8, 0x0A6FF},
+ {0x0A7CB, 0x0A7CF},
+ {0x0A7D2, 0x0A7D2},
+ {0x0A7D4, 0x0A7D4},
+ {0x0A7DA, 0x0A7F1},
+ {0x0A82D, 0x0A82F},
+ {0x0A83A, 0x0A83F},
+ {0x0A878, 0x0A87F},
+ {0x0A8C6, 0x0A8CD},
+ {0x0A8DA, 0x0A8DF},
+ {0x0A954, 0x0A95E},
+ {0x0A97D, 0x0A97F},
+ {0x0A9CE, 0x0A9CE},
+ {0x0A9DA, 0x0A9DD},
+ {0x0A9FF, 0x0A9FF},
+ {0x0AA37, 0x0AA3F},
+ {0x0AA4E, 0x0AA4F},
+ {0x0AA5A, 0x0AA5B},
+ {0x0AAC3, 0x0AADA},
+ {0x0AAF7, 0x0AB00},
+ {0x0AB07, 0x0AB08},
+ {0x0AB0F, 0x0AB10},
+ {0x0AB17, 0x0AB1F},
+ {0x0AB27, 0x0AB27},
+ {0x0AB2F, 0x0AB2F},
+ {0x0AB6C, 0x0AB6F},
+ {0x0ABEE, 0x0ABEF},
+ {0x0ABFA, 0x0ABFF},
+ {0x0AC01, 0x0D7A2},
+ {0x0D7A4, 0x0D7AF},
+ {0x0D7C7, 0x0D7CA},
+ {0x0D7FC, 0x0D7FF},
+ {0x0FA6E, 0x0FA6F},
+ {0x0FADA, 0x0FAFF},
+ {0x0FB07, 0x0FB12},
+ {0x0FB18, 0x0FB1C},
+ {0x0FB37, 0x0FB37},
+ {0x0FB3D, 0x0FB3D},
+ {0x0FB3F, 0x0FB3F},
+ {0x0FB42, 0x0FB42},
+ {0x0FB45, 0x0FB45},
+ {0x0FBC3, 0x0FBD2},
+ {0x0FD90, 0x0FD91},
+ {0x0FDC8, 0x0FDCE},
+ {0x0FE1A, 0x0FE1F},
+ {0x0FE53, 0x0FE53},
+ {0x0FE67, 0x0FE67},
+ {0x0FE6C, 0x0FE6F},
+ {0x0FE75, 0x0FE75},
+ {0x0FEFD, 0x0FEFE},
+ {0x0FF00, 0x0FF00},
+ {0x0FFBF, 0x0FFC1},
+ {0x0FFC8, 0x0FFC9},
+ {0x0FFD0, 0x0FFD1},
+ {0x0FFD8, 0x0FFD9},
+ {0x0FFDD, 0x0FFDF},
+ {0x0FFE7, 0x0FFE7},
+ {0x0FFEF, 0x0FFF8},
+ {0x1000C, 0x1000C},
+ {0x10027, 0x10027},
+ {0x1003B, 0x1003B},
+ {0x1003E, 0x1003E},
+ {0x1004E, 0x1004F},
+ {0x1005E, 0x1007F},
+ {0x100FB, 0x100FF},
+ {0x10103, 0x10106},
+ {0x10134, 0x10136},
+ {0x1018F, 0x1018F},
+ {0x1019D, 0x1019F},
+ {0x101A1, 0x101CF},
+ {0x101FE, 0x1027F},
+ {0x1029D, 0x1029F},
+ {0x102D1, 0x102DF},
+ {0x102FC, 0x102FF},
+ {0x10324, 0x1032C},
+ {0x1034B, 0x1034F},
+ {0x1037B, 0x1037F},
+ {0x1039E, 0x1039E},
+ {0x103C4, 0x103C7},
+ {0x103D6, 0x103FF},
+ {0x1049E, 0x1049F},
+ {0x104AA, 0x104AF},
+ {0x104D4, 0x104D7},
+ {0x104FC, 0x104FF},
+ {0x10528, 0x1052F},
+ {0x10564, 0x1056E},
+ {0x1057B, 0x1057B},
+ {0x1058B, 0x1058B},
+ {0x10593, 0x10593},
+ {0x10596, 0x10596},
+ {0x105A2, 0x105A2},
+ {0x105B2, 0x105B2},
+ {0x105BA, 0x105BA},
+ {0x105BD, 0x105FF},
+ {0x10737, 0x1073F},
+ {0x10756, 0x1075F},
+ {0x10768, 0x1077F},
+ {0x10786, 0x10786},
+ {0x107B1, 0x107B1},
+ {0x107BB, 0x107FF},
+ {0x10806, 0x10807},
+ {0x10809, 0x10809},
+ {0x10836, 0x10836},
+ {0x10839, 0x1083B},
+ {0x1083D, 0x1083E},
+ {0x10856, 0x10856},
+ {0x1089F, 0x108A6},
+ {0x108B0, 0x108DF},
+ {0x108F3, 0x108F3},
+ {0x108F6, 0x108FA},
+ {0x1091C, 0x1091E},
+ {0x1093A, 0x1093E},
+ {0x10940, 0x1097F},
+ {0x109B8, 0x109BB},
+ {0x109D0, 0x109D1},
+ {0x10A04, 0x10A04},
+ {0x10A07, 0x10A0B},
+ {0x10A14, 0x10A14},
+ {0x10A18, 0x10A18},
+ {0x10A36, 0x10A37},
+ {0x10A3B, 0x10A3E},
+ {0x10A49, 0x10A4F},
+ {0x10A59, 0x10A5F},
+ {0x10AA0, 0x10ABF},
+ {0x10AE7, 0x10AEA},
+ {0x10AF7, 0x10AFF},
+ {0x10B36, 0x10B38},
+ {0x10B56, 0x10B57},
+ {0x10B73, 0x10B77},
+ {0x10B92, 0x10B98},
+ {0x10B9D, 0x10BA8},
+ {0x10BB0, 0x10BFF},
+ {0x10C49, 0x10C7F},
+ {0x10CB3, 0x10CBF},
+ {0x10CF3, 0x10CF9},
+ {0x10D28, 0x10D2F},
+ {0x10D3A, 0x10E5F},
+ {0x10E7F, 0x10E7F},
+ {0x10EAA, 0x10EAA},
+ {0x10EAE, 0x10EAF},
+ {0x10EB2, 0x10EFF},
+ {0x10F28, 0x10F2F},
+ {0x10F5A, 0x10F6F},
+ {0x10F8A, 0x10FAF},
+ {0x10FCC, 0x10FDF},
+ {0x10FF7, 0x10FFF},
+ {0x1104E, 0x11051},
+ {0x11076, 0x1107E},
+ {0x110C3, 0x110CC},
+ {0x110CE, 0x110CF},
+ {0x110E9, 0x110EF},
+ {0x110FA, 0x110FF},
+ {0x11135, 0x11135},
+ {0x11148, 0x1114F},
+ {0x11177, 0x1117F},
+ {0x111E0, 0x111E0},
+ {0x111F5, 0x111FF},
+ {0x11212, 0x11212},
+ {0x1123F, 0x1127F},
+ {0x11287, 0x11287},
+ {0x11289, 0x11289},
+ {0x1128E, 0x1128E},
+ {0x1129E, 0x1129E},
+ {0x112AA, 0x112AF},
+ {0x112EB, 0x112EF},
+ {0x112FA, 0x112FF},
+ {0x11304, 0x11304},
+ {0x1130D, 0x1130E},
+ {0x11311, 0x11312},
+ {0x11329, 0x11329},
+ {0x11331, 0x11331},
+ {0x11334, 0x11334},
+ {0x1133A, 0x1133A},
+ {0x11345, 0x11346},
+ {0x11349, 0x1134A},
+ {0x1134E, 0x1134F},
+ {0x11351, 0x11356},
+ {0x11358, 0x1135C},
+ {0x11364, 0x11365},
+ {0x1136D, 0x1136F},
+ {0x11375, 0x113FF},
+ {0x1145C, 0x1145C},
+ {0x11462, 0x1147F},
+ {0x114C8, 0x114CF},
+ {0x114DA, 0x1157F},
+ {0x115B6, 0x115B7},
+ {0x115DE, 0x115FF},
+ {0x11645, 0x1164F},
+ {0x1165A, 0x1165F},
+ {0x1166D, 0x1167F},
+ {0x116BA, 0x116BF},
+ {0x116CA, 0x116FF},
+ {0x1171B, 0x1171C},
+ {0x1172C, 0x1172F},
+ {0x11747, 0x117FF},
+ {0x1183C, 0x1189F},
+ {0x118F3, 0x118FE},
+ {0x11907, 0x11908},
+ {0x1190A, 0x1190B},
+ {0x11914, 0x11914},
+ {0x11917, 0x11917},
+ {0x11936, 0x11936},
+ {0x11939, 0x1193A},
+ {0x11947, 0x1194F},
+ {0x1195A, 0x1199F},
+ {0x119A8, 0x119A9},
+ {0x119D8, 0x119D9},
+ {0x119E5, 0x119FF},
+ {0x11A48, 0x11A4F},
+ {0x11AA3, 0x11AAF},
+ {0x11AF9, 0x11BFF},
+ {0x11C09, 0x11C09},
+ {0x11C37, 0x11C37},
+ {0x11C46, 0x11C4F},
+ {0x11C6D, 0x11C6F},
+ {0x11C90, 0x11C91},
+ {0x11CA8, 0x11CA8},
+ {0x11CB7, 0x11CFF},
+ {0x11D07, 0x11D07},
+ {0x11D0A, 0x11D0A},
+ {0x11D37, 0x11D39},
+ {0x11D3B, 0x11D3B},
+ {0x11D3E, 0x11D3E},
+ {0x11D48, 0x11D4F},
+ {0x11D5A, 0x11D5F},
+ {0x11D66, 0x11D66},
+ {0x11D69, 0x11D69},
+ {0x11D8F, 0x11D8F},
+ {0x11D92, 0x11D92},
+ {0x11D99, 0x11D9F},
+ {0x11DAA, 0x11EDF},
+ {0x11EF9, 0x11FAF},
+ {0x11FB1, 0x11FBF},
+ {0x11FF2, 0x11FFE},
+ {0x1239A, 0x123FF},
+ {0x1246F, 0x1246F},
+ {0x12475, 0x1247F},
+ {0x12544, 0x12F8F},
+ {0x12FF3, 0x12FFF},
+ {0x1342F, 0x1342F},
+ {0x13439, 0x143FF},
+ {0x14647, 0x167FF},
+ {0x16A39, 0x16A3F},
+ {0x16A5F, 0x16A5F},
+ {0x16A6A, 0x16A6D},
+ {0x16ABF, 0x16ABF},
+ {0x16ACA, 0x16ACF},
+ {0x16AEE, 0x16AEF},
+ {0x16AF6, 0x16AFF},
+ {0x16B46, 0x16B4F},
+ {0x16B5A, 0x16B5A},
+ {0x16B62, 0x16B62},
+ {0x16B78, 0x16B7C},
+ {0x16B90, 0x16E3F},
+ {0x16E9B, 0x16EFF},
+ {0x16F4B, 0x16F4E},
+ {0x16F88, 0x16F8E},
+ {0x16FA0, 0x16FDF},
+ {0x16FE5, 0x16FEF},
+ {0x16FF2, 0x16FFF},
+ {0x17001, 0x187F6},
+ {0x187F8, 0x187FF},
+ {0x18CD6, 0x18CFF},
+ {0x18D01, 0x18D07},
+ {0x18D09, 0x1AFEF},
+ {0x1AFF4, 0x1AFF4},
+ {0x1AFFC, 0x1AFFC},
+ {0x1AFFF, 0x1AFFF},
+ {0x1B123, 0x1B14F},
+ {0x1B153, 0x1B163},
+ {0x1B168, 0x1B16F},
+ {0x1B2FC, 0x1BBFF},
+ {0x1BC6B, 0x1BC6F},
+ {0x1BC7D, 0x1BC7F},
+ {0x1BC89, 0x1BC8F},
+ {0x1BC9A, 0x1BC9B},
+ {0x1BCA4, 0x1CEFF},
+ {0x1CF2E, 0x1CF2F},
+ {0x1CF47, 0x1CF4F},
+ {0x1CFC4, 0x1CFFF},
+ {0x1D0F6, 0x1D0FF},
+ {0x1D127, 0x1D128},
+ {0x1D1EB, 0x1D1FF},
+ {0x1D246, 0x1D2DF},
+ {0x1D2F4, 0x1D2FF},
+ {0x1D357, 0x1D35F},
+ {0x1D379, 0x1D3FF},
+ {0x1D455, 0x1D455},
+ {0x1D49D, 0x1D49D},
+ {0x1D4A0, 0x1D4A1},
+ {0x1D4A3, 0x1D4A4},
+ {0x1D4A7, 0x1D4A8},
+ {0x1D4AD, 0x1D4AD},
+ {0x1D4BA, 0x1D4BA},
+ {0x1D4BC, 0x1D4BC},
+ {0x1D4C4, 0x1D4C4},
+ {0x1D506, 0x1D506},
+ {0x1D50B, 0x1D50C},
+ {0x1D515, 0x1D515},
+ {0x1D51D, 0x1D51D},
+ {0x1D53A, 0x1D53A},
+ {0x1D53F, 0x1D53F},
+ {0x1D545, 0x1D545},
+ {0x1D547, 0x1D549},
+ {0x1D551, 0x1D551},
+ {0x1D6A6, 0x1D6A7},
+ {0x1D7CC, 0x1D7CD},
+ {0x1DA8C, 0x1DA9A},
+ {0x1DAA0, 0x1DAA0},
+ {0x1DAB0, 0x1DEFF},
+ {0x1DF1F, 0x1DFFF},
+ {0x1E007, 0x1E007},
+ {0x1E019, 0x1E01A},
+ {0x1E022, 0x1E022},
+ {0x1E025, 0x1E025},
+ {0x1E02B, 0x1E0FF},
+ {0x1E12D, 0x1E12F},
+ {0x1E13E, 0x1E13F},
+ {0x1E14A, 0x1E14D},
+ {0x1E150, 0x1E28F},
+ {0x1E2AF, 0x1E2BF},
+ {0x1E2FA, 0x1E2FE},
+ {0x1E300, 0x1E7DF},
+ {0x1E7E7, 0x1E7E7},
+ {0x1E7EC, 0x1E7EC},
+ {0x1E7EF, 0x1E7EF},
+ {0x1E7FF, 0x1E7FF},
+ {0x1E8C5, 0x1E8C6},
+ {0x1E8D7, 0x1E8FF},
+ {0x1E94C, 0x1E94F},
+ {0x1E95A, 0x1E95D},
+ {0x1E960, 0x1EC70},
+ {0x1ECB5, 0x1ED00},
+ {0x1ED3E, 0x1EDFF},
+ {0x1EE04, 0x1EE04},
+ {0x1EE20, 0x1EE20},
+ {0x1EE23, 0x1EE23},
+ {0x1EE25, 0x1EE26},
+ {0x1EE28, 0x1EE28},
+ {0x1EE33, 0x1EE33},
+ {0x1EE38, 0x1EE38},
+ {0x1EE3A, 0x1EE3A},
+ {0x1EE3C, 0x1EE41},
+ {0x1EE43, 0x1EE46},
+ {0x1EE48, 0x1EE48},
+ {0x1EE4A, 0x1EE4A},
+ {0x1EE4C, 0x1EE4C},
+ {0x1EE50, 0x1EE50},
+ {0x1EE53, 0x1EE53},
+ {0x1EE55, 0x1EE56},
+ {0x1EE58, 0x1EE58},
+ {0x1EE5A, 0x1EE5A},
+ {0x1EE5C, 0x1EE5C},
+ {0x1EE5E, 0x1EE5E},
+ {0x1EE60, 0x1EE60},
+ {0x1EE63, 0x1EE63},
+ {0x1EE65, 0x1EE66},
+ {0x1EE6B, 0x1EE6B},
+ {0x1EE73, 0x1EE73},
+ {0x1EE78, 0x1EE78},
+ {0x1EE7D, 0x1EE7D},
+ {0x1EE7F, 0x1EE7F},
+ {0x1EE8A, 0x1EE8A},
+ {0x1EE9C, 0x1EEA0},
+ {0x1EEA4, 0x1EEA4},
+ {0x1EEAA, 0x1EEAA},
+ {0x1EEBC, 0x1EEEF},
+ {0x1EEF2, 0x1EFFF},
+ {0x1F02C, 0x1F02F},
+ {0x1F094, 0x1F09F},
+ {0x1F0AF, 0x1F0B0},
+ {0x1F0C0, 0x1F0C0},
+ {0x1F0D0, 0x1F0D0},
+ {0x1F0F6, 0x1F0FF},
+ {0x1F1AE, 0x1F1E5},
+ {0x1F203, 0x1F20F},
+ {0x1F23C, 0x1F23F},
+ {0x1F249, 0x1F24F},
+ {0x1F252, 0x1F25F},
+ {0x1F266, 0x1F2FF},
+ {0x1F6D8, 0x1F6DC},
+ {0x1F6ED, 0x1F6EF},
+ {0x1F6FD, 0x1F6FF},
+ {0x1F774, 0x1F77F},
+ {0x1F7D9, 0x1F7DF},
+ {0x1F7EC, 0x1F7EF},
+ {0x1F7F1, 0x1F7FF},
+ {0x1F80C, 0x1F80F},
+ {0x1F848, 0x1F84F},
+ {0x1F85A, 0x1F85F},
+ {0x1F888, 0x1F88F},
+ {0x1F8AE, 0x1F8AF},
+ {0x1F8B2, 0x1F8FF},
+ {0x1FA54, 0x1FA5F},
+ {0x1FA6E, 0x1FA6F},
+ {0x1FA75, 0x1FA77},
+ {0x1FA7D, 0x1FA7F},
+ {0x1FA87, 0x1FA8F},
+ {0x1FAAD, 0x1FAAF},
+ {0x1FABB, 0x1FABF},
+ {0x1FAC6, 0x1FACF},
+ {0x1FADA, 0x1FADF},
+ {0x1FAE8, 0x1FAEF},
+ {0x1FAF7, 0x1FAFF},
+ {0x1FB93, 0x1FB93},
+ {0x1FBCB, 0x1FBEF},
+ {0x1FBFA, 0x1FFFD},
+ {0x20001, 0x2A6DE},
+ {0x2A6E0, 0x2A6FF},
+ {0x2A701, 0x2B737},
+ {0x2B739, 0x2B73F},
+ {0x2B741, 0x2B81C},
+ {0x2B81E, 0x2B81F},
+ {0x2B821, 0x2CEA0},
+ {0x2CEA2, 0x2CEAF},
+ {0x2CEB1, 0x2EBDF},
+ {0x2EBE1, 0x2F7FF},
+ {0x2FA1E, 0x2FFFD},
+ {0x30001, 0x31349},
+ {0x3134B, 0x3FFFD},
+ {0x40000, 0x4FFFD},
+ {0x50000, 0x5FFFD},
+ {0x60000, 0x6FFFD},
+ {0x70000, 0x7FFFD},
+ {0x80000, 0x8FFFD},
+ {0x90000, 0x9FFFD},
+ {0xA0000, 0xAFFFD},
+ {0xB0000, 0xBFFFD},
+ {0xC0000, 0xCFFFD},
+ {0xD0000, 0xDFFFD},
+ {0xE0000, 0xE0000},
+ {0xE0002, 0xE001F},
+ {0xE0080, 0xE00FF},
+ {0xE01F0, 0xEFFFD}
+};
+
+/* Non-characters. */
+static const struct widechar_range widechar_nonchar_table[] = {
+ {0x0FDD0, 0x0FDEF},
+ {0x0FFFE, 0x0FFFF},
+ {0x1FFFE, 0x1FFFF},
+ {0x2FFFE, 0x2FFFF},
+ {0x3FFFE, 0x3FFFF},
+ {0x4FFFE, 0x4FFFF},
+ {0x5FFFE, 0x5FFFF},
+ {0x6FFFE, 0x6FFFF},
+ {0x7FFFE, 0x7FFFF},
+ {0x8FFFE, 0x8FFFF},
+ {0x9FFFE, 0x9FFFF},
+ {0xAFFFE, 0xAFFFF},
+ {0xBFFFE, 0xBFFFF},
+ {0xCFFFE, 0xCFFFF},
+ {0xDFFFE, 0xDFFFF},
+ {0xEFFFE, 0xEFFFF},
+ {0xFFFFE, 0xFFFFF},
+ {0x10FFFE, 0x10FFFF}
+};
+
+/* Characters that were widened from width 1 to 2 in Unicode 9. */
+static const struct widechar_range widechar_widened_table[] = {
+ {0x0231A, 0x0231B},
+ {0x023E9, 0x023EC},
+ {0x023F0, 0x023F0},
+ {0x023F3, 0x023F3},
+ {0x025FD, 0x025FE},
+ {0x02614, 0x02615},
+ {0x02648, 0x02653},
+ {0x0267F, 0x0267F},
+ {0x02693, 0x02693},
+ {0x026A1, 0x026A1},
+ {0x026AA, 0x026AB},
+ {0x026BD, 0x026BE},
+ {0x026C4, 0x026C5},
+ {0x026CE, 0x026CE},
+ {0x026D4, 0x026D4},
+ {0x026EA, 0x026EA},
+ {0x026F2, 0x026F3},
+ {0x026F5, 0x026F5},
+ {0x026FA, 0x026FA},
+ {0x026FD, 0x026FD},
+ {0x02705, 0x02705},
+ {0x0270A, 0x0270B},
+ {0x02728, 0x02728},
+ {0x0274C, 0x0274C},
+ {0x0274E, 0x0274E},
+ {0x02753, 0x02755},
+ {0x02757, 0x02757},
+ {0x02795, 0x02797},
+ {0x027B0, 0x027B0},
+ {0x027BF, 0x027BF},
+ {0x02B1B, 0x02B1C},
+ {0x02B50, 0x02B50},
+ {0x02B55, 0x02B55},
+ {0x1F004, 0x1F004},
+ {0x1F0CF, 0x1F0CF},
+ {0x1F18E, 0x1F18E},
+ {0x1F191, 0x1F19A},
+ {0x1F201, 0x1F201},
+ {0x1F21A, 0x1F21A},
+ {0x1F22F, 0x1F22F},
+ {0x1F232, 0x1F236},
+ {0x1F238, 0x1F23A},
+ {0x1F250, 0x1F251},
+ {0x1F300, 0x1F320},
+ {0x1F32D, 0x1F335},
+ {0x1F337, 0x1F37C},
+ {0x1F37E, 0x1F393},
+ {0x1F3A0, 0x1F3CA},
+ {0x1F3CF, 0x1F3D3},
+ {0x1F3E0, 0x1F3F0},
+ {0x1F3F4, 0x1F3F4},
+ {0x1F3F8, 0x1F43E},
+ {0x1F440, 0x1F440},
+ {0x1F442, 0x1F4FC},
+ {0x1F4FF, 0x1F53D},
+ {0x1F54B, 0x1F54E},
+ {0x1F550, 0x1F567},
+ {0x1F595, 0x1F596},
+ {0x1F5FB, 0x1F64F},
+ {0x1F680, 0x1F6C5},
+ {0x1F6CC, 0x1F6CC},
+ {0x1F6D0, 0x1F6D0},
+ {0x1F6EB, 0x1F6EC},
+ {0x1F910, 0x1F918},
+ {0x1F980, 0x1F984},
+ {0x1F9C0, 0x1F9C0}
+};
+
+template
+bool widechar_in_table(const Collection &arr, uint32_t c) {
+ auto where = std::lower_bound(std::begin(arr), std::end(arr), c,
+ [](widechar_range p, uint32_t c) { return p.hi < c; });
+ return where != std::end(arr) && where->lo <= c;
+}
+
+/* Return the width of character c, or a special negative value. */
+int wcwidth(uint32_t c) {
+ if (widechar_in_table(widechar_ascii_table, c))
+ return 1;
+ if (widechar_in_table(widechar_private_table, c))
+ return widechar_private_use;
+ if (widechar_in_table(widechar_nonprint_table, c))
+ return widechar_nonprint;
+ if (widechar_in_table(widechar_nonchar_table, c))
+ return widechar_non_character;
+ if (widechar_in_table(widechar_combining_table, c))
+ return widechar_combining;
+ if (widechar_in_table(widechar_combiningletters_table, c))
+ return widechar_combining;
+ if (widechar_in_table(widechar_doublewide_table, c))
+ return 2;
+ if (widechar_in_table(widechar_ambiguous_table, c))
+ return widechar_ambiguous;
+ if (widechar_in_table(widechar_unassigned_table, c))
+ return widechar_unassigned;
+ if (widechar_in_table(widechar_widened_table, c))
+ return widechar_widened_in_9;
+ return 1;
+}
+
+} // namespace
+#endif // WIDECHAR_WIDTH_H
diff --git a/snap/gui/btop.desktop b/snap/gui/btop.desktop
new file mode 100644
index 0000000..0135a93
--- /dev/null
+++ b/snap/gui/btop.desktop
@@ -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
diff --git a/snap/gui/icon.svg b/snap/gui/icon.svg
new file mode 100644
index 0000000..402b842
--- /dev/null
+++ b/snap/gui/icon.svg
@@ -0,0 +1,111 @@
+
+
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 860b103..e000e9a 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -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:
diff --git a/src/btop.cpp b/src/btop.cpp
index af2ffcf..484a0df 100644
--- a/src/btop.cpp
+++ b/src/btop.cpp
@@ -17,10 +17,12 @@ tab-size = 4
*/
#include
+#include
#include
+#ifdef __FreeBSD__
+ #include
+#endif
#include
-#include
-#include
#include
#include
#include
@@ -30,6 +32,11 @@ tab-size = 4
#include
#include
#include
+#ifdef __APPLE__
+ #include
+ #include
+ #include
+#endif
#include
#include
@@ -40,7 +47,7 @@ tab-size = 4
#include
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 resized (false);
- atomic resizing (false);
atomic quitting (false);
+ atomic should_quit (false);
+ atomic should_sleep (false);
atomic _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 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 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 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> debug_times;
struct runner_conf {
- bitset<8> box_mask;
+ vector 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;
- future mem;
- future net;
- future&> 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);
}
}
diff --git a/src/btop_config.cpp b/src/btop_config.cpp
index 2a1bbc0..479101a 100644
--- a/src/btop_config.cpp
+++ b/src/btop_config.cpp
@@ -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 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 available_batteries = {"Auto"};
+
vector current_boxes;
vector 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 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 {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/btop_config.hpp b/src/btop_config.hpp
index fd08d92..9d2f63c 100644
--- a/src/btop_config.hpp
+++ b/src/btop_config.hpp
@@ -45,9 +45,10 @@ namespace Config {
extern vector current_boxes;
extern vector preset_list;
+ extern vector 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
diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp
index 91a239a..3603406 100644
--- a/src/btop_draw.cpp
+++ b/src/btop_draw.cpp
@@ -43,42 +43,42 @@ namespace Symbols {
const unordered_flat_map> 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 core_graphs;
vector 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 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") + "" + 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 p_graphs;
+ unordered_flat_map p_wide_cmd;
unordered_flat_map 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 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);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/btop_draw.hpp b/src/btop_draw.hpp
index 203eed1..e425845 100644
--- a/src/btop_draw.hpp
+++ b/src/btop_draw.hpp
@@ -52,7 +52,7 @@ namespace Symbols {
const string down = "↓";
const string left = "←";
const string right = "→";
- const string enter = "↲";
+ const string enter = "↵";
}
namespace Draw {
diff --git a/src/btop_input.cpp b/src/btop_input.cpp
index 2056bea..586dea9 100644
--- a/src/btop_input.cpp
+++ b/src/btop_input.cpp
@@ -19,6 +19,8 @@ tab-size = 4
#include
#include
#include
+#include
+#include
#include
#include
@@ -72,20 +74,74 @@ namespace Input {
};
std::atomic interrupt (false);
+ std::atomic polling (false);
array mouse_pos;
unordered_flat_map mouse_mappings;
deque 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 g(lock);
+ current.push_back(ch);
+ if (current.size() > 100) {
+ current.clear();
+ }
+ }
+ }
+
+ size_t avail() {
+ std::lock_guard g(lock);
+
+ return current.size();
+ }
+
+ std::string get() {
+ std::string res;
+
+ {
+ std::lock_guard 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;
}
diff --git a/src/btop_input.hpp b/src/btop_input.hpp
index fdcf6e2..7342709 100644
--- a/src/btop_input.hpp
+++ b/src/btop_input.hpp
@@ -42,6 +42,7 @@ namespace Input {
extern unordered_flat_map mouse_mappings;
extern atomic interrupt;
+ extern atomic polling;
//* Mouse column and line position
extern array mouse_pos;
diff --git a/src/btop_menu.cpp b/src/btop_menu.cpp
index 3128544..8037e0f 100644
--- a/src/btop_menu.cpp
+++ b/src/btop_menu.cpp
@@ -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")
diff --git a/src/btop_shared.cpp b/src/btop_shared.cpp
new file mode 100644
index 0000000..606c082
--- /dev/null
+++ b/src/btop_shared.cpp
@@ -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
+
+#include
+#include
+
+using std::string_literals::operator""s;
+namespace rng = std::ranges;
+using namespace Tools;
+
+
+namespace Proc {
+ void proc_sorter(vector& 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& 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& in_procs, vector& 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 ? "[+]─" : "[-]─") : " ├─ ");
+ }
+
+}
\ No newline at end of file
diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp
index 4d0f323..1eb81b6 100644
--- a/src/btop_shared.hpp
+++ b/src/btop_shared.hpp
@@ -27,12 +27,15 @@ tab-size = 4
#include
#include
#include
+#include
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> Banner_src;
extern const string Version;
@@ -43,6 +46,7 @@ namespace Global {
extern atomic 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 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 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 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 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 as data source
string draw(const vector& plist, const bool force_redraw=false, const bool data_same=false);
+
+ struct tree_proc {
+ std::reference_wrapper entry;
+ vector children;
+ };
+
+ //* Sort vector of proc_info's
+ void proc_sorter(vector& proc_vec, const string& sorting, const bool reverse, const bool tree = false);
+
+ //* Recursive sort of process tree
+ void tree_sort(vector& 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& in_procs, vector& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false, const bool no_update=false, const bool should_filter=false);
}
diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp
index d801fe1..301e554 100644
--- a/src/btop_tools.cpp
+++ b/src/btop_tools.cpp
@@ -23,15 +23,17 @@ tab-size = 4
#include
#include
#include
+#include
#include
+#include
#include
-#include
#include
#include
#include
#include
+#include
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 width = 0;
atomic 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 active_locks (0);
+ size_t wide_ulen(const string& str) {
+ unsigned int chars = 0;
+ try {
+ std::wstring_convert> 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(str.at(i)) > 0xef) x += 2;
- else if ((static_cast(str.at(i)) & 0xC0) != 0x80) x++;
- if (x >= len + 1) {
- str.resize(i);
- str.shrink_to_fit();
- break;
+ if (wide) {
+ try {
+ std::wstring_convert> 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(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 Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
- static const array 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 mebiUnits_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
+ static const array mebiUnits_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
+ static const array megaUnits_bit = {"bit", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb", "Bb", "Gb"};
+ static const array 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& atom) : atom(atom) {
- active_locks++;
- while (not this->atom.compare_exchange_strong(this->not_true, true));
+ void atomic_wait(const atomic& atom, const bool old) noexcept {
+ while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
+ }
+
+ void atomic_wait_for(const atomic& 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& 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 busy (false);
- bool first = true;
- const string tdf = "%Y/%m/%d (%T) | ";
- }
+ std::atomic 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) {
diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp
index da4a784..862981a 100644
--- a/src/btop_tools.hpp
+++ b/src/btop_tools.hpp
@@ -21,16 +21,24 @@ tab-size = 4
#include
#include
#include
-#include
#include
+#include
#include
#include
#include
#include
#include
#include
+#include
+#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;
@@ -130,12 +139,13 @@ namespace Term {
namespace Tools {
constexpr auto SSmax = std::numeric_limits::max();
- extern atomic 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(c) & 0xC0) != 0x80; })
- + (wide ? std::ranges::count_if(str, [](char c) { return (static_cast(c) > 0xef); }) : 0);
+ return (wide ? wide_ulen(str) : std::ranges::count_if(str, [](char c) { return (static_cast(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 contains string , 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 from vector , returns size of if is not present
template
inline size_t v_index(const vector& vec, const T& find_val) {
@@ -269,20 +289,28 @@ namespace Tools {
string hostname();
string username();
-#if __GNUC__ < 11
- inline void atomic_wait(const atomic& atom, const bool old=true) noexcept { while (atom.load() == old) sleep_ms(1); }
- inline void atomic_notify(const atomic& atom) noexcept { (void)atom; }
-#else
- inline void atomic_wait(const atomic& atom, const bool old=true) noexcept { atom.wait(old); }
- inline void atomic_notify(const atomic& 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 to be false and sets it to true on construct, sets to false and notifies on destruct
+ void atomic_wait(const atomic& atom, const bool old=true) noexcept;
+
+ void atomic_wait_for(const atomic& atom, const bool old=true, const uint64_t wait_ms=0) noexcept;
+
+ //* Sets atomic to true on construct, sets to false on destruct
class atomic_lock {
atomic& atom;
bool not_true = false;
public:
- atomic_lock(atomic& atom);
+ atomic_lock(atomic& atom, bool wait=false);
~atomic_lock();
};
diff --git a/src/freebsd/btop_collect.cpp b/src/freebsd/btop_collect.cpp
new file mode 100644
index 0000000..9e5be73
--- /dev/null
+++ b/src/freebsd/btop_collect.cpp
@@ -0,0 +1,1319 @@
+/* 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
+using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
+namespace fs = std::filesystem;
+namespace rng = std::ranges;
+using namespace Tools;
+
+//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
+
+namespace Cpu {
+ vector core_old_totals;
+ vector core_old_idles;
+ vector available_fields = {"total"};
+ vector available_sensors = {"Auto"};
+ cpu_info current_cpu;
+ bool got_sensors = false, cpu_temp_only = false;
+
+ //* Populate found_sensors map
+ bool get_sensors();
+
+ //* Get current cpu clock speed
+ string get_cpuHz();
+
+ //* Search /proc/cpuinfo for a cpu name
+ string get_cpuName();
+
+ struct Sensor {
+ fs::path path;
+ string label;
+ int64_t temp = 0;
+ int64_t high = 0;
+ int64_t crit = 0;
+ };
+
+ string cpu_sensor;
+ vector core_sensors;
+ unordered_flat_map core_mapping;
+} // namespace Cpu
+
+namespace Mem {
+ double old_uptime;
+ std::vector zpools;
+
+ void get_zpools();
+}
+
+namespace Shared {
+
+ fs::path passwd_path;
+ uint64_t totalMem;
+ long pageSize, clkTck, coreCount, physicalCoreCount, arg_max;
+ int totalMem_len, kfscale;
+ long bootTime;
+
+ void init() {
+ //? Shared global variables init
+ int mib[2];
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ int ncpu;
+ size_t len = sizeof(ncpu);
+ if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+ Logger::warning("Could not determine number of cores, defaulting to 1.");
+ } else {
+ coreCount = ncpu;
+ }
+
+ pageSize = sysconf(_SC_PAGE_SIZE);
+ if (pageSize <= 0) {
+ pageSize = 4096;
+ Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
+ }
+
+ clkTck = sysconf(_SC_CLK_TCK);
+ if (clkTck <= 0) {
+ clkTck = 100;
+ Logger::warning("Could not get system clock ticks per second. Defaulting to 100, processes cpu usage might be incorrect.");
+ }
+
+ int64_t memsize = 0;
+ size_t size = sizeof(memsize);
+ if (sysctlbyname("hw.physmem", &memsize, &size, NULL, 0) < 0) {
+ Logger::warning("Could not get memory size");
+ }
+ totalMem = memsize;
+
+ struct timeval result;
+ size = sizeof(result);
+ if (sysctlbyname("kern.boottime", &result, &size, NULL, 0) < 0) {
+ Logger::warning("Could not get boot time");
+ } else {
+ bootTime = result.tv_sec;
+ }
+
+ size = sizeof(kfscale);
+ if (sysctlbyname("kern.fscale", &kfscale, &size, NULL, 0) == -1) {
+ kfscale = 2048;
+ }
+
+ //* Get maximum length of process arguments
+ arg_max = sysconf(_SC_ARG_MAX);
+
+ //? Init for namespace Cpu
+ Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
+ Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
+ Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
+ Cpu::core_old_idles.insert(Cpu::core_old_idles.begin(), Shared::coreCount, 0);
+ Cpu::collect();
+ for (auto &[field, vec] : Cpu::current_cpu.cpu_percent) {
+ if (not vec.empty() and not v_contains(Cpu::available_fields, field)) Cpu::available_fields.push_back(field);
+ }
+ Cpu::cpuName = Cpu::get_cpuName();
+ Cpu::got_sensors = Cpu::get_sensors();
+ Cpu::core_mapping = Cpu::get_core_mapping();
+
+ //? Init for namespace Mem
+ Mem::old_uptime = system_uptime();
+ Mem::collect();
+ Mem::get_zpools();
+ }
+
+} // namespace Shared
+
+namespace Cpu {
+ string cpuName;
+ string cpuHz;
+ bool has_battery = true;
+ tuple