diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 93639b7..fd2bf38 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -25,6 +25,8 @@ assignees: aristocratos
**Info (please complete the following information):**
- btop++ version: `bpytop -v`
+ - Binary: [self compiled or static binary from release]
+ - (If compiled) Compiler and version:
- Architecture: [x86_64, aarch64, etc.] `uname -m`
- Platform: [Linux, FreeBSD, OsX]
- (Linux) Kernel: `uname -r`
@@ -34,6 +36,18 @@ assignees: aristocratos
**Additional context**
-contents of `~/.config/btop/error.log`
+contents of `~/.config/btop/btop.log`
(try running btop with `--debug` flag if error.log is empty)
+
+**GDB Backtrace**
+
+If btop++ is crashing at start the following steps could be helpful:
+
+1. run `gdb btop`
+
+2. `r` to run, wait for crash and press enter
+
+3. `bt` to get backtrace
+
+4. Copy and paste the backtrace here:
diff --git a/.gitignore b/.gitignore
index 6772cda..3817f1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@ stage/
build
bin
btop
+.*/
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e38a6d..7354364 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,61 @@
+## v1.0.9
+
+* Added: ifstream check and try-catch for stod() in Tools::system_uptime()
+
+* Fixed: Freeze on cin.ignore()
+
+## v1.0.8
+
+* Fixed: Additional NULL checks in UTF-8 detection
+
+* Changed: Makefile: Only look for g++-11 if CXX=g++
+
+* Fixed: Missing NULL check for ttyname
+
+* Changed: Only log tty name if known
+
+## v1.0.7
+
+* Fixed: Crash when opening menu at too small size
+
+* Fixed: Cores not constrained to cpu box and core numbers above 100 cut off
+
+* Fixed: Scrollbar position incorrect in small lists and selection not working when filtering
+
+## v1.0.6
+
+* Fixed: Check that getenv("LANG") is not NULL in UTF-8 check
+
+* Fixed: Processes not completely hidden when collapsed in tree mode
+
+* Fixed: Changed wrong filename error.log to btop.log
+
+## v1.0.5
+
+* Fixed: Load AVG sizing when hiding temperatures
+
+* Fixed: Sizing constraints bug on start and boxes can be toggled from size error screen
+
+* Fixed: UTF-8 check crashing if LANG was set to non existant locale
+
+## v1.0.4
+
+* Fixed: Use /proc/pid/statm if RSS memory from /proc/pid/stat is faulty
+
+## v1.0.3
+
+* Fixed: stoi 0 literal pointer to nullptr and added more clamping for gradient array access
+
+## v1.0.2
+
+* Fixed: ARCH detection in Makefile
+
+* Fixed: Color gradient array out of bounds, added clamp 0-100 for cpu percent values
+
+* Fixed: Menu size and preset size issues and added warnings for small terminal size
+
+* Fixed: Options menu page selection alignment
+
## v1.0.1
* Fixed: UTF-8 check to include UTF8
diff --git a/Makefile b/Makefile
index 9bce83d..e6b4d44 100644
--- a/Makefile
+++ b/Makefile
@@ -5,16 +5,20 @@ BANNER = \n \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m█
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")
+ifneq ($(QUIET),true)
+ override PRE := info
+ override QUIET := false
+else
+ override PRE := info-quiet
+endif
+
PREFIX ?= /usr/local
#? NOTICE! Manually set PLATFORM and ARCH if not compiling for host system
PLATFORM ?= $(shell uname -s || echo unknown)
-ARCH ?= $(shell uname -p || echo unknown)
+ARCH ?= $(shell uname -m || echo unknown)
#? Only enable fcf-protection if on x86_64
-ifeq ($(ARCH),unknown)
- ARCH := $(shell uname -m || echo unknown)
-endif
ifeq ($(ARCH),x86_64)
override ADDFLAGS += -fcf-protection
endif
@@ -35,7 +39,7 @@ CXX ?= g++
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
-ifneq ($(CXX),g++-10)
+ifeq ($(CXX),g++)
V_MAJOR := $(shell echo $(CXX_VERSION) | cut -f1 -d".")
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
@@ -92,9 +96,9 @@ SOURCES += $(shell find $(SRCDIR)/$(PLATFORM_DIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
#? Default Make
-all: pre directories btop
+all: $(PRE) directories btop
-pre:
+info:
@printf " $(BANNER)\n"
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
@@ -109,6 +113,10 @@ pre:
@printf "\n\033[1;92mBuilding btop++ \033[93m(\033[97mv$(BTOP_VERSION)\033[93m)\033[0m\n"
+info-quiet:
+
+ @printf "\n\033[1;92mBuilding btop++ \033[91m(\033[97mv$(BTOP_VERSION)\033[91m) \033[93m$(PLATFORM) \033[96m$(ARCH)\033[0m\n"
+
help:
@printf " $(BANNER)\n"
@printf "\033[1;97mbtop++ makefile\033[0m\n"
@@ -144,7 +152,7 @@ install:
@printf "\033[1;92mInstalling doc to: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop\n"
@mkdir -p $(DESTDIR)$(PREFIX)/share/btop
@cp -p README.md $(DESTDIR)$(PREFIX)/share/btop
- @printf "\033[1;92mInstalling themes to: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop/themes\n"
+ @printf "\033[1;92mInstalling themes to: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop/themes\033[0m\n"
@cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
#? Set SUID bit for btop as $SU_USER in $SU_GROUP
@@ -169,17 +177,17 @@ uninstall:
btop: $(OBJECTS)
@sleep 0.1 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
- @printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
+ @$(QUIET) || printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
@$(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 "\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"
#? Compile
.ONESHELL:
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
@sleep 0.1 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
- @printf "\033[1;97mCompiling $<\033[0m\n"
+ @$(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
diff --git a/README.md b/README.md
index d432377..6278b0a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# ![btop++](Img/logo.png)
+
+
+
+
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow)
![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green)
@@ -163,9 +167,9 @@ Also needs a UTF8 locale and a font that covers:
## Installation
-**Binary release (statically compiled)**
+**Binary release (statically compiled, for kernel 3.2.0 and newer)**
-1. **Download btop-(VERSION)-(PLATFORM)-(ARCH).tbz from latest release and unpack to a new folder**
+1. **Download btop-(VERSION)-(PLATFORM)-(ARCH).tbz from [latest release](https://github.com/aristocratos/btop/releases/latest) and unpack to a new folder**
2. **Install (from created folder)**
@@ -227,9 +231,13 @@ Also needs a UTF8 locale and a font that covers:
Append `STATIC=true` to `make` command for static compilation.
+ Append `QUIET=true` for less verbose output.
+
Notice! Manually set `$ARCH` variable if cross-compiling
- Use `$ADDFLAGS` variable for appending flags to both compiler and linker.
+ 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.
``` bash
make
@@ -276,22 +284,27 @@ Also needs a UTF8 locale and a font that covers:
```bash
make help
```
-
+
## Install the snap
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
- `sudo snap install btop`
+ * **Install the snap**
+
+ ```bash
+ sudo snap install btop
+ ```
* **Connect the interfaces**
- ```
- sudo snap connect btop:system-observe
+
+ ```bash
+ sudo snap connect btop:system-observe
sudo snap connect btop:physical-memory-observe
- sudo snap connect btop:mount-observe
+ sudo snap connect btop:mount-observe
sudo snap connect btop:hardware-observe
sudo snap connect btop:network-observe
sudo snap connect btop:process-control
```
-
+
## Configurability
@@ -485,7 +498,7 @@ net_iface = "br0"
#* Show battery stats in top right if battery is present.
show_battery = True
-#* Set loglevel for "~/.config/btop/error.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
+#* 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"
```
@@ -501,7 +514,7 @@ optional arguments:
-lc, --low-color disable truecolor, converts 24-bit colors to 256-color
-t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols
+t, --tty_off force (OFF) tty mode
- -p --preset start with preset, integer value between 0-9
+ -p, --preset start with preset, integer value between 0-9
--utf-force force start even if no UTF-8 locale was detected
--debug start in DEBUG mode: shows microsecond timer for information collect
and screen draw functions and sets loglevel to DEBUG
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 3b887ec..d41dbf4 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -25,16 +25,16 @@ architectures:
package-repositories:
- type: apt
ppa: ubuntu-toolchain-r/test
-
+
apps:
btop:
command: usr/local/bin/btop
- command-chain:
- - bin/homeishome-launch
+ command-chain:
+ - bin/homeishome-launch
environment:
LC_ALL: C.UTF-8
- LANG: C.UTF-8
- plugs:
+ LANG: C.UTF-8
+ plugs:
- mount-observe
- process-control
- system-observe
@@ -43,30 +43,28 @@ apps:
- network-observe
- physical-memory-observe
- home
-
+
parts:
- btop:
+ btop:
source: https://github.com/aristocratos/btop
source-type: git
plugin: make
make-parameters:
- PREFIX=/usr/local
- STATIC=true
-
build-packages:
- - coreutils
- - sed
- - git
- - build-essential
- - gcc-11
- - g++-11
-
+ - coreutils
+ - sed
+ - git
+ - build-essential
+ - gcc-11
+ - g++-11
override-pull: |
snapcraftctl pull
snapcraftctl set-version "$(git describe --tags | sed 's/^v//' | cut -d "-" -f1)"
-
+
homeishome-launch:
plugin: nil
stage-snaps:
- - homeishome-launch
-
+ - homeishome-launch
+
diff --git a/src/btop.cpp b/src/btop.cpp
index ec12c12..af2ffcf 100644
--- a/src/btop.cpp
+++ b/src/btop.cpp
@@ -55,7 +55,7 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
- const string Version = "1.0.0";
+ const string Version = "1.0.9";
int coreCount;
string overlay;
@@ -79,6 +79,7 @@ namespace Global {
uint64_t start_time;
atomic resized (false);
+ atomic resizing (false);
atomic quitting (false);
atomic _runner_started (false);
@@ -100,7 +101,7 @@ void argumentParser(const int& argc, char **argv) {
<< " -lc, --low-color disable truecolor, converts 24-bit colors to 256-color\n"
<< " -t, --tty_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n"
<< " +t, --tty_off force (OFF) tty mode\n"
- << " -p --preset start with preset, integer value between 0-9\n"
+ << " -p, --preset start with preset, integer value between 0-9\n"
<< " --utf-force force start even if no UTF-8 locale was detected\n"
<< " --debug start in DEBUG mode: shows microsecond timer for information collect\n"
<< " and screen draw functions and sets loglevel to DEBUG\n"
@@ -149,13 +150,17 @@ 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) {
if (force and refreshed) force = false;
}
else return;
+ static const array all_boxes = {"cpu", "mem", "net", "proc"};
Global::resized = true;
- Runner::stop();
+ if (Runner::active) Runner::stop();
+ Config::unlock();
auto boxes = Config::getS("shown_boxes");
auto min_size = Term::get_min_size(boxes);
@@ -171,7 +176,16 @@ void term_resize(bool force) {
<< "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() and Input::get() == "q") exit(0);
+ if (Input::poll()) {
+ auto key = Input::get();
+ if (key == "q")
+ exit(0);
+ else if (is_in(key, "1", "2", "3", "4")) {
+ Config::current_preset = -1;
+ Config::toggle_box(all_boxes.at(std::stoi(key) - 1));
+ boxes = Config::getS("shown_boxes");
+ }
+ }
min_size = Term::get_min_size(boxes);
}
else if (not Term::refresh()) break;
@@ -644,8 +658,8 @@ int main(int argc, char **argv) {
//? Setup paths for config, log and user themes
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
- if (getenv(env) != NULL and access(getenv(env), W_OK) != -1) {
- Config::conf_dir = fs::path(getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
+ if (std::getenv(env) != NULL and access(std::getenv(env), W_OK) != -1) {
+ Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
break;
}
}
@@ -704,22 +718,28 @@ int main(int argc, char **argv) {
}
//? Try to find and set a UTF-8 locale
- if (bool found = false; not str_to_upper(s_replace(string(std::setlocale(LC_ALL, NULL)), "-", "")).ends_with("UTF8")) {
- if (const string lang = (string)getenv("LANG"); str_to_upper(s_replace(lang, "-", "")).ends_with("UTF8")) {
- found = true;
- std::setlocale(LC_ALL, lang.c_str());
+ 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;
+ }
}
- else if (const string loc = std::locale("").name(); not loc.empty()) {
- try {
- for (auto& l : ssplit(loc, ';')) {
- if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
- found = true;
- std::setlocale(LC_ALL, l.substr(l.find('=') + 1).c_str());
- break;
+ else {
+ if (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;
+ }
+ break;
+ }
+ }
}
}
+ catch (...) { found = false; }
}
- catch (const std::out_of_range&) { found = false; }
}
if (not found and Global::utf_force)
@@ -738,7 +758,7 @@ int main(int argc, char **argv) {
exit(1);
}
- Logger::info("Running on " + Term::current_tty);
+ if (Term::current_tty != "unknown") Logger::info("Running on " + Term::current_tty);
if (not Global::arg_tty and Config::getB("force_tty")) {
Config::set("tty_mode", true);
Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols");
@@ -777,20 +797,19 @@ int main(int argc, char **argv) {
Config::current_preset = min(Global::arg_preset, (int)Config::preset_list.size() - 1);
Config::apply_preset(Config::preset_list.at(Config::current_preset));
}
- Draw::calcSizes();
{
const auto [x, y] = Term::get_min_size(Config::getS("shown_boxes"));
if (Term::height < y or Term::width < x) {
- Global::exit_error_msg = "Terminal size to small for current config.\n(WxH) Current: " + to_string(Term::width)
- + 'x' + to_string(Term::height) + " | Needed: " + to_string(x) + 'x' + to_string(y)
- + "\nResize terminal or disable some boxes in config file to fix this.";
- exit(1);
+ term_resize(true);
+ Global::resized = false;
+ Input::interrupt = false;
}
}
//? Print out box outlines
+ Draw::calcSizes();
cout << Term::sync_start << Cpu::box << Mem::box << Net::box << Proc::box << Term::sync_end << flush;
diff --git a/src/btop_config.cpp b/src/btop_config.cpp
index 077ef93..2a1bbc0 100644
--- a/src/btop_config.cpp
+++ b/src/btop_config.cpp
@@ -167,7 +167,7 @@ namespace Config {
{"show_battery", "#* Show battery stats in top right if battery is present."},
- {"log_level", "#* Set loglevel for \"~/.config/btop/error.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
+ {"log_level", "#* Set loglevel for \"~/.config/btop/btop.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."}
};
@@ -224,7 +224,7 @@ namespace Config {
{"swap_disk", true},
{"show_disks", true},
{"only_physical", true},
- {"use_fstab", false},
+ {"use_fstab", true},
{"show_io_stat", true},
{"io_mode", false},
{"io_graph_combined", false},
@@ -306,15 +306,26 @@ namespace Config {
//* Apply selected preset
void apply_preset(const string& preset) {
string boxes;
+
for (const auto& box : ssplit(preset, ',')) {
const auto& vals = ssplit(box, ':');
boxes += vals.at(0) + ' ';
+ }
+ if (not boxes.empty()) boxes.pop_back();
+
+ auto min_size = Term::get_min_size(boxes);
+ if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
+ return;
+ }
+
+ for (const auto& box : ssplit(preset, ',')) {
+ const auto& vals = ssplit(box, ':');
if (vals.at(0) == "cpu") set("cpu_bottom", (vals.at(1) == "0" ? false : true));
else if (vals.at(0) == "mem") set("mem_below_net", (vals.at(1) == "0" ? false : true));
else if (vals.at(0) == "proc") set("proc_left", (vals.at(1) == "0" ? false : true));
set("graph_symbol_" + vals.at(0), vals.at(2));
}
- if (not boxes.empty()) boxes.pop_back();
+
if (check_boxes(boxes)) set("shown_boxes", boxes);
}
@@ -475,6 +486,7 @@ namespace Config {
}
void toggle_box(const string& box) {
+ auto old_boxes = current_boxes;
auto box_pos = rng::find(current_boxes, box);
if (box_pos == current_boxes.end())
current_boxes.push_back(box);
@@ -486,6 +498,14 @@ namespace Config {
for (const auto& b : current_boxes) new_boxes += b + ' ';
new_boxes.pop_back();
}
+
+ auto min_size = Term::get_min_size(new_boxes);
+
+ if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
+ current_boxes = old_boxes;
+ return;
+ }
+
Config::set("shown_boxes", new_boxes);
}
diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp
index 2e844a3..91a239a 100644
--- a/src/btop_draw.cpp
+++ b/src/btop_draw.cpp
@@ -396,7 +396,7 @@ namespace Draw {
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(last));
+ out += (last < 1 and not color_gradient.empty() ? Theme::c("inactive_fg") : Theme::g(color_gradient).at(clamp(last, 0ll, 100ll)));
out += graphs.at(current).at(0);
}
else {
@@ -618,7 +618,7 @@ namespace Cpu {
+ Symbols::title_left + Fx::b + Theme::c("title") + cpuHz + Fx::ub + Theme::c("div_line") + Symbols::title_right;
out += Mv::to(b_y + 1, b_x + 1) + Theme::c("main_fg") + Fx::b + "CPU " + cpu_meter(cpu.cpu_percent.at("total").back())
- + Theme::g("cpu").at(cpu.cpu_percent.at("total").back()) + rjust(to_string(cpu.cpu_percent.at("total").back()), 4) + Theme::c("main_fg") + '%';
+ + Theme::g("cpu").at(clamp(cpu.cpu_percent.at("total").back(), 0ll, 100ll)) + rjust(to_string(cpu.cpu_percent.at("total").back()), 4) + Theme::c("main_fg") + '%';
if (show_temps) {
const auto [temp, unit] = celsius_to(cpu.temp.at(0).back(), temp_scale);
const auto& temp_color = Theme::g("temp").at(clamp(cpu.temp.at(0).back() * 100 / cpu.temp_max, 0ll, 100ll));
@@ -632,15 +632,16 @@ namespace Cpu {
} catch (const std::exception& e) { throw std::runtime_error("graphs, clock, meter : " + (string)e.what()); }
//? Core text and graphs
- int cx = 0, cy = 1, cc = 0;
+ int cx = 0, cy = 1, cc = 0, core_width = (b_column_size == 0 ? 2 : 3);
+ if (Shared::coreCount >= 100) core_width++;
for (const auto& n : iota(0, Shared::coreCount)) {
out += Mv::to(b_y + cy + 1, b_x + cx + 1) + Theme::c("main_fg") + (Shared::coreCount < 100 ? Fx::b + 'C' + Fx::ub : "")
- + ljust(to_string(n), (b_column_size == 0 ? 2 : 3));
+ + 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(cpu.core_percent.at(n).back()) + core_graphs.at(n)(cpu.core_percent.at(n), data_same or redraw);
+ + 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(cpu.core_percent.at(n).back());
+ 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) {
@@ -654,7 +655,7 @@ namespace Cpu {
out += Theme::c("div_line") + Symbols::v_line;
- if (++cy > ceil((double)Shared::coreCount / b_columns) and n != Shared::coreCount - 1) {
+ if ((++cy > ceil((double)Shared::coreCount / b_columns) or cy == b_height - 2) and n != Shared::coreCount - 1) {
if (++cc >= b_columns) break;
cy = 1; cx = (b_width / b_columns) * cc;
}
@@ -664,9 +665,9 @@ namespace Cpu {
if (cy < b_height - 1 and cc <= b_columns) {
string lavg_pre;
int sep = 1;
- if (b_column_size == 2 and got_sensors) { lavg_pre = "Load AVG:"; sep = 3; }
- else if (b_column_size == 2 or (b_column_size == 1 and got_sensors)) { lavg_pre = "LAV:"; }
- else if (b_column_size == 1 or (b_column_size == 0 and got_sensors)) { lavg_pre = "L"; }
+ if (b_column_size == 2 and show_temps) { lavg_pre = "Load AVG:"; sep = 3; }
+ else if (b_column_size == 2 or (b_column_size == 1 and show_temps)) { lavg_pre = "LAV:"; }
+ else if (b_column_size == 1 or (b_column_size == 0 and show_temps)) { lavg_pre = "L"; }
string lavg;
for (const auto& val : cpu.load_avg) {
lavg += string(sep, ' ') + (lavg_pre.size() < 3 ? to_string((int)round(val)) : to_string(val).substr(0, 4));
@@ -860,7 +861,7 @@ namespace Mem {
const string used_percent = to_string(disk.used_percent);
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2)) + Theme::c("main_fg") + used_percent + '%';
}
- 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(max(50ll, disk.io_activity.back()))
+ 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))
+ 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) {
@@ -901,7 +902,7 @@ namespace Mem {
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;
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(max(50ll, disk.io_activity.back()))
+ 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 (++cy > height - 3) break;
@@ -1331,11 +1332,8 @@ namespace Proc {
+ Theme::c("inactive_fg") + Fx::ub + graph_bg * (d_width / 3) + Mv::l(d_width / 3)
+ Theme::c("proc_misc") + detailed_mem_graph(detailed.mem_bytes, (redraw or data_same or not alive)) + ' '
+ Theme::c("title") + Fx::b + detailed.memory;
-
-
}
-
//? Check bounds of current selection and view
if (start > 0 and numpids <= select_max)
start = 0;
@@ -1349,7 +1347,7 @@ namespace Proc {
//* Iteration over processes
int lc = 0;
for (int n=0; auto& p : plist) {
- if (n++ < start or p.filtered) continue;
+ if (p.filtered or (proc_tree and p.tree_index == plist.size()) or n++ < start) continue;
bool is_selected = (lc + 1 == selected);
if (is_selected) {
selected_pid = (int)p.pid;
@@ -1388,11 +1386,11 @@ namespace Proc {
for (int i = 0; int v : {(int)round(p.cpu_p), (int)round(p.mem * 100 / Shared::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(val);
- else colors[i++] = Theme::g("process").at(val - 100);
+ if (val < 100) colors[i++] = Theme::g("proc_color").at(max(0, val));
+ else colors[i++] = Theme::g("process").at(clamp(val - 100, 0, 100));
}
else
- colors[i++] = Theme::g("process").at(min(v, 100));
+ colors[i++] = Theme::g("process").at(clamp(v, 0, 100));
}
c_color = colors.at(0); m_color = colors.at(1); t_color = colors.at(2);
}
@@ -1401,7 +1399,7 @@ namespace Proc {
end = Fx::ub;
}
if (proc_gradient) {
- g_color = Theme::g("proc").at(calc * 100 / select_max);
+ g_color = Theme::g("proc").at(clamp(calc * 100 / select_max, 0, 100));
}
}
@@ -1433,9 +1431,10 @@ namespace Proc {
if (p.cpu_p < 10 or p.cpu_p >= 100) cpu_str.resize(3);
string mem_str = (mem_bytes ? floating_humanizer(p.mem, true) : "");
if (not mem_bytes) {
- double mem_p = (double)p.mem * 100 / Shared::totalMem;
+ double mem_p = clamp((double)p.mem * 100 / Shared::totalMem, 0.0, 100.0);
mem_str = to_string(mem_p);
- mem_str.resize((mem_p < 10 or mem_p >= 100 ? 3 : 4));
+ if (mem_str.size() < 4) mem_str = "0";
+ else mem_str.resize((mem_p < 10 or mem_p >= 100 ? 3 : 4));
mem_str += '%';
}
out += (thread_size > 0 ? t_color + rjust(to_string(min(p.threads, (size_t)9999)), thread_size) + ' ' + end : "" )
@@ -1452,7 +1451,7 @@ namespace Proc {
//? Draw scrollbar if needed
if (numpids > select_max) {
- const int scroll_pos = clamp((int)round((double)start * (select_max - 2) / (numpids - (select_max - 2))), 0, height - 5);
+ const int scroll_pos = clamp((int)round((double)start * select_max / (numpids - select_max)), 0, height - 5);
out += Mv::to(y + 1, x + width - 2) + Fx::b + Theme::c("main_fg") + Symbols::up
+ Mv::to(y + height - 2, x + width - 2) + Symbols::down
+ Mv::to(y + 2 + scroll_pos, x + width - 2) + "█";
diff --git a/src/btop_input.cpp b/src/btop_input.cpp
index f4ffd3d..2056bea 100644
--- a/src/btop_input.cpp
+++ b/src/btop_input.cpp
@@ -95,7 +95,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(SSmax);
+ if (cin.rdbuf()->in_avail() > 0) cin.ignore(cin.rdbuf()->in_avail());
if (not key.empty()) {
//? Remove escape code prefix if present
if (key.substr(0, 2) == Fx::e) {
@@ -351,7 +351,6 @@ namespace Input {
return;
}
else if (key == "s" and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
- if (Term::width < 80 or Term::height < 20) return;
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::SignalChoose);
diff --git a/src/btop_menu.cpp b/src/btop_menu.cpp
index 5793cb8..3128544 100644
--- a/src/btop_menu.cpp
+++ b/src/btop_menu.cpp
@@ -757,6 +757,28 @@ namespace Menu {
return (redraw ? Changed : retval);
}
+ int sizeError(const string& key) {
+ if (redraw) {
+ vector cont_vec;
+ cont_vec.push_back(Fx::b + Theme::g("used")[100] + "Error:" + Theme::c("main_fg") + Fx::ub);
+ cont_vec.push_back("Terminal size to small to" + Fx::reset);
+ cont_vec.push_back("display menu or box!" + Fx::reset);
+
+ messageBox = Menu::msgBox{45, 0, cont_vec, "error"};
+ Global::overlay = messageBox();
+ }
+
+ auto ret = messageBox.input(key);
+ if (ret == msgBox::Ok_Yes or ret == msgBox::No_Esc) {
+ messageBox.clear();
+ return Closed;
+ }
+ else if (redraw) {
+ return Changed;
+ }
+ return NoChange;
+ }
+
int signalSend(const string& key) {
auto& s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
if (s_pid == 0) return Closed;
@@ -1168,7 +1190,7 @@ namespace Menu {
i++;
}
if (pages > 1) {
- out += Mv::to(y+6 + height, x+2) + Theme::c("hi_fg") + Symbols::title_left_down + Fx::b + Symbols::up + Theme::c("title") + " page "
+ out += Mv::to(y+6 + height - 1, x+2) + Theme::c("hi_fg") + Symbols::title_left_down + Fx::b + Symbols::up + Theme::c("title") + " page "
+ to_string(page+1) + '/' + to_string(pages) + ' ' + Theme::c("hi_fg") + Symbols::down + Fx::ub + Symbols::title_right_down;
}
//? Option name and value
@@ -1286,6 +1308,7 @@ namespace Menu {
//* Add menus here and update enum Menus in header
const auto menuFunc = vector{
+ ref(sizeError),
ref(signalChoose),
ref(signalSend),
ref(signalReturn),
@@ -1312,9 +1335,17 @@ namespace Menu {
if (currentMenu < 0 or not menuMask.test(currentMenu)) {
Menu::active = true;
redraw = true;
+ if (((menuMask.test(Main) or menuMask.test(Options) or menuMask.test(Help) or menuMask.test(SignalChoose))
+ and (Term::width < 80 or Term::height < 24))
+ or (Term::width < 50 or Term::height < 20)) {
+ menuMask.reset();
+ menuMask.set(SizeError);
+ }
+
for (const auto& i : iota(0, (int)menuMask.size())) {
if (menuMask.test(i)) currentMenu = i;
}
+
}
auto retCode = menuFunc.at(currentMenu)(key);
diff --git a/src/btop_menu.hpp b/src/btop_menu.hpp
index c57dcea..b4fcab1 100644
--- a/src/btop_menu.hpp
+++ b/src/btop_menu.hpp
@@ -69,6 +69,7 @@ namespace Menu {
//* Enum for functions in vector menuFuncs
enum Menus {
+ SizeError,
SignalChoose,
SignalSend,
SignalReturn,
diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp
index e54465f..4d0f323 100644
--- a/src/btop_shared.hpp
+++ b/src/btop_shared.hpp
@@ -70,6 +70,7 @@ namespace Shared {
void init();
extern long coreCount, page_size, clk_tck;
+ extern int totalMem_len;
extern uint64_t totalMem;
}
diff --git a/src/btop_theme.cpp b/src/btop_theme.cpp
index a912eb2..116e78b 100644
--- a/src/btop_theme.cpp
+++ b/src/btop_theme.cpp
@@ -159,7 +159,7 @@ namespace Theme {
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
if (hexa.size() == 2) {
- int h_int = stoi(hexa, 0, 16);
+ int h_int = stoi(hexa, nullptr, 16);
if (t_to_256) {
return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m";
} else {
@@ -170,14 +170,14 @@ namespace Theme {
else if (hexa.size() == 6) {
if (t_to_256) {
return pre + to_string(truecolor_to_256(
- stoi(hexa.substr(0, 2), 0, 16),
- stoi(hexa.substr(2, 2), 0, 16),
- stoi(hexa.substr(4, 2), 0, 16))) + "m";
+ stoi(hexa.substr(0, 2), nullptr, 16),
+ stoi(hexa.substr(2, 2), nullptr, 16),
+ stoi(hexa.substr(4, 2), nullptr, 16))) + "m";
} else {
return pre +
- to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" +
- to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" +
- to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m";
+ to_string(stoi(hexa.substr(0, 2), nullptr, 16)) + ";" +
+ to_string(stoi(hexa.substr(2, 2), nullptr, 16)) + ";" +
+ to_string(stoi(hexa.substr(4, 2), nullptr, 16)) + "m";
}
}
else Logger::error("Invalid size of hex value: " + hexa);
@@ -203,14 +203,14 @@ namespace Theme {
for (auto& c : hexa) if (not isxdigit(c)) return array{-1, -1, -1};
if (hexa.size() == 2) {
- int h_int = stoi(hexa, 0, 16);
+ int h_int = stoi(hexa, nullptr, 16);
return array{h_int, h_int, h_int};
}
else if (hexa.size() == 6) {
return array{
- stoi(hexa.substr(0, 2), 0, 16),
- stoi(hexa.substr(2, 2), 0, 16),
- stoi(hexa.substr(4, 2), 0, 16)
+ stoi(hexa.substr(0, 2), nullptr, 16),
+ stoi(hexa.substr(2, 2), nullptr, 16),
+ stoi(hexa.substr(4, 2), nullptr, 16)
};
}
}
@@ -434,4 +434,4 @@ namespace Theme {
Fx::reset = Fx::reset_base + Term::fg + Term::bg;
}
-}
\ No newline at end of file
+}
diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp
index 74df467..d801fe1 100644
--- a/src/btop_tools.cpp
+++ b/src/btop_tools.cpp
@@ -107,7 +107,7 @@ namespace Term {
initialized = (bool)isatty(STDIN_FILENO);
if (initialized) {
tcgetattr(STDIN_FILENO, &initial_settings);
- current_tty = (string)ttyname(STDIN_FILENO);
+ current_tty = (ttyname(STDIN_FILENO) != NULL ? (string)ttyname(STDIN_FILENO) : "unknown");
//? Disable stream sync
cin.sync_with_stdio(false);
diff --git a/src/linux/btop_collect.cpp b/src/linux/btop_collect.cpp
index cc72370..3c8b9fe 100644
--- a/src/linux/btop_collect.cpp
+++ b/src/linux/btop_collect.cpp
@@ -80,6 +80,7 @@ namespace Shared {
fs::path procPath, passwd_path;
uint64_t totalMem;
long pageSize, clkTck, coreCount;
+ int totalMem_len;
void init() {
@@ -114,6 +115,7 @@ namespace Shared {
if (meminfo.good()) {
meminfo.ignore(SSmax, ':');
meminfo >> totalMem;
+ totalMem_len = to_string(totalMem).size();
totalMem <<= 10;
}
if (not meminfo.good() or totalMem == 0)
@@ -1168,6 +1170,7 @@ namespace Proc {
out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads;
+ filter_found++;
}
if (collapsed and not filtering) {
cur_proc.filtered = true;
@@ -1201,7 +1204,7 @@ namespace Proc {
//? Update cpu percent deque for process cpu graph
if (not Config::getB("proc_per_core")) detailed.entry.cpu_p *= Shared::coreCount;
- detailed.cpu_percent.push_back(round(detailed.entry.cpu_p));
+ detailed.cpu_percent.push_back(clamp((long long)round(detailed.entry.cpu_p), 0ll, 100ll));
while (cmp_greater(detailed.cpu_percent.size(), width)) detailed.cpu_percent.pop_front();
//? Process runtime
@@ -1451,7 +1454,10 @@ namespace Proc {
next_x = 24;
continue;
case 24: //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x)
- new_proc.mem = stoull(short_str) * Shared::pageSize;
+ if (cmp_greater(short_str.size(), Shared::totalMem_len))
+ new_proc.mem = Shared::totalMem;
+ else
+ new_proc.mem = stoull(short_str) * Shared::pageSize;
}
break;
}
@@ -1464,8 +1470,18 @@ namespace Proc {
if (x-offset < 24) continue;
+ //? Get RSS memory from /proc/[pid]/statm if value from /proc/[pid]/stat looks wrong
+ if (new_proc.mem >= Shared::totalMem) {
+ pread.open(d.path() / "statm");
+ if (not pread.good()) continue;
+ pread.ignore(SSmax, ' ');
+ pread >> new_proc.mem;
+ new_proc.mem *= Shared::pageSize;
+ pread.close();
+ }
+
//? Process cpu usage since last update
- new_proc.cpu_p = round(cmult * 1000 * (cpu_t - new_proc.cpu_t) / max((uint64_t)1, cputimes - old_cputimes)) / 10.0;
+ new_proc.cpu_p = clamp(round(cmult * 1000 * (cpu_t - new_proc.cpu_t) / max((uint64_t)1, cputimes - old_cputimes)) / 10.0, 0.0, 100.0 * Shared::coreCount);
//? Process cumulative cpu usage since process start
new_proc.cpu_c = (double)cpu_t / max(1.0, (uptime * Shared::clkTck) - new_proc.cpu_s);
@@ -1595,8 +1611,15 @@ namespace Tools {
double system_uptime() {
string upstr;
ifstream pread(Shared::procPath / "uptime");
- getline(pread, upstr, ' ');
- pread.close();
- return stod(upstr);
+ if (pread.good()) {
+ try {
+ getline(pread, upstr, ' ');
+ pread.close();
+ return stod(upstr);
+ }
+ catch (const std::invalid_argument&) {}
+ catch (const std::out_of_range&) {}
+ }
+ throw std::runtime_error("Failed get uptime from from " + (string)Shared::procPath + "/uptime");
}
}
\ No newline at end of file