summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile114
-rw-r--r--README.md16
-rw-r--r--src/btop.cpp258
-rw-r--r--src/btop_config.cpp13
-rw-r--r--src/btop_draw.cpp54
-rw-r--r--src/btop_draw.hpp29
-rw-r--r--src/btop_input.cpp9
-rw-r--r--src/btop_linux.cpp169
-rw-r--r--src/btop_menu.cpp2
-rw-r--r--src/btop_menu.hpp4
-rw-r--r--src/btop_shared.hpp2
-rw-r--r--src/btop_tools.cpp34
-rw-r--r--src/btop_tools.hpp17
13 files changed, 465 insertions, 256 deletions
diff --git a/Makefile b/Makefile
index 520162e..79eff29 100644
--- a/Makefile
+++ b/Makefile
@@ -1,29 +1,40 @@
+#* Btop++ makefile v1.0
+
+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╚═════╝ ╚═╝ ╚═════╝ ╚═╝
+
+BTOP_VERSION = $(shell head -n100 src/btop.cpp 2>/dev/null | grep "Version =" | cut -f2 -d"\"" || echo " unknown")
+TIMESTAMP = $(shell date +%s)
+
PREFIX ?= /usr/local
-DOCDIR ?= $(PREFIX)/share/btop/doc
-#Compiler and Linker
+#? Compiler and Linker
CXX ?= g++
+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
+#? Try to make sure we are using GCC/G++ version 11 or later if not instructed to use g++-10
ifneq ($(CXX),g++-10)
- CXX_VERSION = $(shell $(CXX) -dumpfullversion -dumpversion | cut -f1 -d"." || echo 0)
- ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
+ V_MAJOR = $(shell echo $(CXX_VERSION) | cut -f1 -d"." || echo 0)
+ ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
override CXX = g++-11
endif
endif
endif
-#Only enable fcf-protection if on x86
-ARCH = $(shell uname -p)
+#? Only enable fcf-protection if on x86_64
+ARCH = $(shell uname -p ||true)
ifeq ($(ARCH),x86_64)
ADDFLAGS = -fcf-protection
endif
+ifeq ($(ARCH),unknown)
+ ARCH = $(shell uname -m ||true)
+endif
+PLATFORM = $(shell uname -s ||true)
-#The Target Binary Program
-TARGET := btop
+#? Use all CPU cores (will only be set if using Make >=4.3)
+MAKEFLAGS := --jobs=$(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
-#The Directories, Source, Includes, Objects and Binary
+#? The Directories, Source, Includes, Objects and Binary
SRCDIR := src
INCDIR := include
BUILDDIR := obj
@@ -32,7 +43,7 @@ SRCEXT := cpp
DEPEXT := d
OBJEXT := o
-#Flags, Libraries and Includes
+#? Flags, Libraries and Includes
REQFLAGS := -std=c++20
WARNFLAGS := -Wall -Wextra -Wno-stringop-overread -pedantic -pedantic-errors -Wfatal-errors
OPTFLAGS := -O2 -ftree-loop-vectorize
@@ -40,58 +51,97 @@ override LDCXXFLAGS += -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -fexce
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) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
-#Default Make
-all: directories $(TARGET)
-
-#Make the Directories
+#? Default Make
+all: msg directories btop
+
+msg:
+ @printf " $(BANNER)\n"
+ @printf "\033[1;97mCompiler : \033[0m$(CXX) ($(CXX_VERSION))\n"
+ @printf "\033[1;97mREQFLAGS : \033[0m$(REQFLAGS)\n"
+ @printf "\033[1;97mWARNFLAGS : \033[0m$(WARNFLAGS)\n"
+ @printf "\033[1;97mOPTFLAGS : \033[0m$(OPTFLAGS)\n"
+ @printf "\033[1;97mLDCXXFLAGS : \033[0m$(LDCXXFLAGS)\n"
+
+ @printf "\n\033[1;92mBuilding btop++ v$(BTOP_VERSION) for $(PLATFORM) ($(ARCH))\033[0m\n"
+
+help:
+ @printf "\033[1;97mbtop++ makefile\033[0m\n"
+ @printf "usage: make [argument]\n\n"
+ @printf "arguments:\n"
+ @printf " all Compile btop (default argument)\n"
+ @printf " clean Remove built objects\n"
+ @printf " distclean Remove built objects and binaries\n"
+ @printf " install Install btop++ to \$$PREFIX\n"
+ @printf " setuid Set installed binary owner/group to \$$SU_USER/\$$SU_OWNER and set SUID bit\n"
+ @printf " uninstall Uninstall btop++ from \$$PREFIX\n"
+
+#? Make the Directories
directories:
@mkdir -p $(TARGETDIR)
@mkdir -p $(BUILDDIR)
-#Clean only Objects
+#? Clean only Objects
clean:
+ @printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
@rm -rf $(BUILDDIR)
-#Full Clean, Objects and Binaries
+#? Clean Objects and Binaries
distclean: clean
+ @printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
@rm -rf $(TARGETDIR)
install:
+ @printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
@mkdir -p $(DESTDIR)$(PREFIX)/bin
@cp -p $(TARGETDIR)/btop $(DESTDIR)$(PREFIX)/bin/btop
- @mkdir -p $(DESTDIR)$(DOCDIR)
- @cp -p README.md $(DESTDIR)$(DOCDIR)
- @cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
@chmod 755 $(DESTDIR)$(PREFIX)/bin/btop
+ @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"
+ @cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
-#Set suid bit for btop to root, will make btop run with admin privileges regardless of actual user
-su-setuid:
- @su --session-command "chown root:root $(DESTDIR)$(PREFIX)/bin/btop && chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop" root
+#? Set suid bit for btop for $SU_USER in SU_GROUP, will make btop run with (root by default) privileges regardless of actual user
+setuid:
+ @printf "\033[1;97mFile: $(DESTDIR)$(PREFIX)/bin/btop\n"
+ @printf "\033[1;92mSetting owner \033[1;97m$(SU_USER):$(SU_GROUP)\033[0m\n"
+ @chown $(SU_USER):$(SU_GROUP) $(DESTDIR)$(PREFIX)/bin/btop
+ @printf "\033[1;92mSetting SUID bit\033[0m\n"
+ @chmod u+s $(DESTDIR)$(PREFIX)/bin/btop
uninstall:
+ @printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
- @rm -rf $(DESTDIR)$(DOCDIR)
+ @printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/share/btop\033[0m\n"
@rm -rf $(DESTDIR)$(PREFIX)/share/btop
-#Pull in dependency info for *existing* .o files
+#? Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
-#Link
+#? Link
btop: $(OBJECTS)
- $(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS)
+ @sleep 0.1 2>/dev/null || true
+ @printf "\n\033[1;92mLinking and optimizing binary\033[0m\n"
+ @$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS)
+ @printf "\033[1;97m./$(TARGETDIR)/btop ($$(du -ah $(TARGETDIR)/btop | cut -f1)iB)\033[0m\n"
+ @printf "\n\033[1;92mBuild complete in (\033[1;97m$$(date -d @$$(expr $$(date +%s) - $(TIMESTAMP)) -u +%Mm:%Ss)\033[1;92m)\033[0m\n"
-#Compile
+#? Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
- $(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
- @$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
+ @sleep 0.1 2>/dev/null || true
+ @printf "\033[1;97mCompiling $< \n"
+ @$(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
+ @$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) >/dev/null
@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
-#Non-File Targets
-.PHONY: all
+#? Non-File Targets
+.PHONY: all msg help
diff --git a/README.md b/README.md
index 09762bb..7d73ae8 100644
--- a/README.md
+++ b/README.md
@@ -84,8 +84,8 @@ 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))
-* 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" argument.
- (16 color TTY mode now available as well.)
+* 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)
Also needs a UTF8 locale and a font that covers:
@@ -105,7 +105,8 @@ See comments by @sgleizes [link](https://github.com/aristocratos/bpytop/issues/1
If text are misaligned and you are using Konsole or Yakuake, turning off "Bi-Directional text rendering" is a possible fix.
-Characters clipping in to each other or text/border misalignments is not bugs caused by bpytop, but most likely a fontconfig or terminal problem where the braille characters making up the graphs aren't rendered correctly.
+Characters clipping in to each other or text/border misalignments is not bugs caused by btop, but most likely a fontconfig or terminal problem where the braille characters making up the graphs aren't rendered correctly.
+
Look to the creators of the terminal emulator you use to fix these issues if the previous mentioned fixes don't work for you.
## Screenshots
@@ -152,11 +153,12 @@ sudo make install
# only use "sudo" when installing to a NON user owned directory
```
->to make btop always run as root (no need for `sudo` to enable signal sending to any process and to prevent /proc read permissions problems on some systems)
+>to make btop always run as root (or other user), (no need for `sudo` to enable signal sending to any process and to prevent /proc read permissions problems on some systems)
``` bash
# run after make install and use same PREFIX if any was used at install
-make su-setuid
+sudo make setuid
+# set SU_USER and SU_GROUP to select user and group, default is root:root
```
@@ -166,13 +168,13 @@ make su-setuid
sudo make uninstall
```
->to remove any object files
+>to remove any object files from source dir
```bash
make clean
```
->to remove all object files, binaries and created directories
+>to remove all object files, binaries and created directories in source dir
```bash
make distclean
diff --git a/src/btop.cpp b/src/btop.cpp
index 49b4724..bb13365 100644
--- a/src/btop.cpp
+++ b/src/btop.cpp
@@ -27,6 +27,8 @@ tab-size = 4
#include <cmath>
#include <iostream>
#include <exception>
+#include <tuple>
+#include <regex>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
@@ -53,8 +55,8 @@ tab-size = 4
#error Platform not supported!
#endif
-using std::string, std::string_view, std::vector, std::array, std::atomic, std::endl, std::cout, std::min;
-using std::flush, std::endl, std::string_literals::operator""s, std::to_string, std::future, std::async, std::bitset, std::future_status;
+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;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
@@ -108,8 +110,9 @@ 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"
- << " --utf-foce force start even if no UTF-8 locale was detected"
- << " --debug start in debug mode with loglevel set to DEBUG\n"
+ << " --utf-foce 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"
<< endl;
exit(0);
}
@@ -270,12 +273,89 @@ void banner_gen() {
+ Fx::i + "v" + Global::Version + Fx::ui;
}
+bool update_clock() {
+ const auto& clock_format = Config::getS("clock_format");
+ if (not Cpu::shown or clock_format.empty()) return false;
+
+ static const unordered_flat_map<string, string> clock_custom_format = {
+ {"/user", Tools::username()},
+ {"/host", Tools::hostname()},
+ {"/uptime", ""}
+ };
+ static time_t c_time = 0;
+ static size_t clock_len = 0;
+
+ if (auto n_time = time(NULL); n_time == c_time)
+ return false;
+ else
+ c_time = n_time;
+
+ auto& out = Global::clock;
+ const auto& cpu_bottom = Config::getB("cpu_bottom");
+ const auto& x = Cpu::x;
+ const auto y = (cpu_bottom ? Cpu::y + Cpu::height - 1 : Cpu::y);
+ const auto& width = Cpu::width;
+ const auto& title_left = (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
+ const auto& title_right = (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
+ string new_clock = clock_format;
+
+ for (const auto& [c_format, replacement] : clock_custom_format) {
+ if (s_contains(new_clock, c_format)) {
+ if (c_format == "/uptime") {
+ string upstr = sec_to_dhms(system_uptime());
+ if (upstr.size() > 8) upstr.resize(upstr.size() - 3);
+ new_clock = s_replace(new_clock, c_format, upstr);
+ }
+ else {
+ new_clock = s_replace(new_clock, c_format, replacement);
+ }
+ }
+
+ }
+
+ new_clock = uresize(Tools::strf_time(new_clock), std::max(0, width - 56));
+ out.clear();
+
+ if (new_clock.size() != clock_len) {
+ out = Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + Symbols::h_line * clock_len;
+ clock_len = new_clock.size();
+ }
+
+ out += Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + title_left
+ + Theme::c("title") + Fx::b + new_clock + Theme::c("cpu_box") + Fx::ub + title_right;
+
+ return true;
+}
+
//* Manages secondary thread for collection and drawing of boxes
namespace Runner {
atomic<bool> active (false);
atomic<bool> stopping (false);
atomic<bool> waiting (false);
- atomic<bool> do_work (false);
+
+ //* Setup semaphore for triggering thread to do work
+#if __GNUC__ < 11
+ #include <semaphore.h>
+ sem_t do_work;
+ inline void thread_sem_init() { sem_init(&do_work, 0, 0); }
+ inline void thread_wait() { sem_wait(&do_work); }
+ inline void thread_trigger() { sem_post(&do_work); }
+#else
+ #include <semaphore>
+ std::binary_semaphore do_work(0);
+ inline void thread_sem_init() { ; }
+ inline void thread_wait() { do_work.acquire(); }
+ inline void thread_trigger() { do_work.release(); }
+#endif
+
+ //* RAII wrapper for pthread_mutex locking
+ class thread_lock {
+ 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() { if (status == 0) pthread_mutex_unlock(&pt_mutex); }
+ };
string output;
sigset_t mask;
@@ -296,6 +376,17 @@ namespace Runner {
cpu_present, cpu_running
};
+ enum debug_actions {
+ collect_begin,
+ draw_begin,
+ draw_done
+ };
+
+ enum debug_array {
+ collect,
+ 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;
@@ -314,13 +405,35 @@ namespace Runner {
struct runner_conf current_conf;
+ void debug_timer(const char* name, const int action) {
+ switch (action) {
+ case collect_begin:
+ debug_times[name].at(collect) = time_micros();
+ return;
+ case draw_begin:
+ debug_times[name].at(draw) = time_micros();
+ debug_times[name].at(collect) = debug_times[name].at(draw) - debug_times[name].at(collect);
+ debug_times["total"].at(collect) += debug_times[name].at(collect);
+ return;
+ case draw_done:
+ debug_times[name].at(draw) = time_micros() - debug_times[name].at(draw);
+ debug_times["total"].at(draw) += debug_times[name].at(draw);
+ return;
+ }
+ }
+
//? ------------------------------- 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
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTSTP);
+ sigaddset(&mask, SIGWINCH);
+ sigaddset(&mask, SIGTERM);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
- //? pthread_mutex_lock to make sure this thread is a single instance thread
+ //? pthread_mutex_lock to lock thread and monitor health from main thread
thread_lock pt_lck(mtx);
if (pt_lck.status != 0) {
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
@@ -329,18 +442,18 @@ namespace Runner {
stopping = true;
}
+ //* ----------------------------------------------- THREAD LOOP -----------------------------------------------
while (not Global::quitting) {
- atomic_wait(do_work, false);
- do_work = false;
+ thread_wait();
if (stopping or Global::resized) {
continue;
}
- auto& conf = current_conf;
-
- //? Secondary atomic lock used for signaling status to main thread
+ //? Atomic lock used for blocking non-thread safe actions in main thread
atomic_lock lck(active);
+ auto& conf = current_conf;
+
//! DEBUG stats
if (Global::debug) {
debug_times.clear();
@@ -356,12 +469,17 @@ namespace Runner {
future<Mem::mem_info&> mem;
future<Net::net_info&> net;
future<vector<Proc::proc_info>&> proc;
+
+ //? Loop until all box flags present in bitmask have been zeroed
while (conf.box_mask.count() > 0) {
if (stopping) break;
+
//? PROC
if (conf.box_mask.test(proc_present)) {
if (not conf.box_mask.test(proc_running)) {
- if (Global::debug) debug_times["proc"].at(0) = time_micros();
+ if (Global::debug) debug_timer("proc", collect_begin);
+
+ //? Start async collect
proc = async(Proc::collect, conf.no_update);
conf.box_mask.set(proc_running);
}
@@ -370,16 +488,12 @@ namespace Runner {
else if (proc.wait_for(std::chrono::microseconds(10)) == future_status::ready) {
try {
- if (Global::debug) {
- debug_times["proc"].at(1) = time_micros();
- debug_times["proc"].at(0) = debug_times["proc"].at(1) - debug_times["proc"].at(0);
- debug_times["total"].at(0) += debug_times["proc"].at(0);
- }
+ if (Global::debug) debug_timer("proc", draw_begin);
+
+ //? Draw box
output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
- if (Global::debug) {
- debug_times["proc"].at(1) = time_micros() - debug_times["proc"].at(1);
- debug_times["total"].at(1) += debug_times["proc"].at(1);
- }
+
+ if (Global::debug) debug_timer("proc", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Proc:: -> " + (string)e.what());
@@ -387,10 +501,13 @@ namespace Runner {
conf.box_mask ^= proc_done;
}
}
+
//? NET
if (conf.box_mask.test(net_present)) {
if (not conf.box_mask.test(net_running)) {
- if (Global::debug) debug_times["net"].at(0) = time_micros();
+ if (Global::debug) debug_timer("net", collect_begin);
+
+ //? Start async collect
net = async(Net::collect, conf.no_update);
conf.box_mask.set(net_running);
}
@@ -399,16 +516,12 @@ namespace Runner {
else if (net.wait_for(ZeroSec) == future_status::ready) {
try {
- if (Global::debug) {
- debug_times["net"].at(1) = time_micros();
- debug_times["net"].at(0) = debug_times["net"].at(1) - debug_times["net"].at(0);
- debug_times["total"].at(0) += debug_times["net"].at(0);
- }
+ if (Global::debug) debug_timer("net", draw_begin);
+
+ //? Draw box
output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
- if (Global::debug) {
- debug_times["net"].at(1) = time_micros() - debug_times["net"].at(1);
- debug_times["total"].at(1) += debug_times["net"].at(1);
- }
+
+ if (Global::debug) debug_timer("net", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Net:: -> " + (string)e.what());
@@ -416,10 +529,13 @@ namespace Runner {
conf.box_mask ^= net_done;
}
}
+
//? MEM
if (conf.box_mask.test(mem_present)) {
if (not conf.box_mask.test(mem_running)) {
- if (Global::debug) debug_times["mem"].at(0) = time_micros();
+ if (Global::debug) debug_timer("mem", collect_begin);
+
+ //? Start async collect
mem = async(Mem::collect, conf.no_update);
conf.box_mask.set(mem_running);
}
@@ -428,16 +544,12 @@ namespace Runner {
else if (mem.wait_for(ZeroSec) == future_status::ready) {
try {
- if (Global::debug) {
- debug_times["mem"].at(1) = time_micros();
- debug_times["mem"].at(0) = debug_times["mem"].at(1) - debug_times["mem"].at(0);
- debug_times["total"].at(0) += debug_times["mem"].at(0);
- }
+ if (Global::debug) debug_timer("mem", draw_begin);
+
+ //? Draw box
output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
- if (Global::debug) {
- debug_times["mem"].at(1) = time_micros() - debug_times["mem"].at(1);
- debug_times["total"].at(1) += debug_times["mem"].at(1);
- }
+
+ if (Global::debug) debug_timer("mem", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Mem:: -> " + (string)e.what());
@@ -445,10 +557,13 @@ namespace Runner {
conf.box_mask ^= mem_done;
}
}
+
//? CPU
if (conf.box_mask.test(cpu_present)) {
if (not conf.box_mask.test(cpu_running)) {
- if (Global::debug) debug_times["cpu"].at(0) = time_micros();
+ if (Global::debug) debug_timer("cpu", collect_begin);
+
+ //? Start async collect
cpu = async(Cpu::collect, conf.no_update);
conf.box_mask.set(cpu_running);
}
@@ -457,16 +572,12 @@ namespace Runner {
else if (cpu.wait_for(ZeroSec) == future_status::ready) {
try {
- if (Global::debug) {
- debug_times["cpu"].at(1) = time_micros();
- debug_times["cpu"].at(0) = debug_times["cpu"].at(1) - debug_times["cpu"].at(0);
- debug_times["total"].at(0) += debug_times["cpu"].at(0);
- }
+ if (Global::debug) debug_timer("cpu", draw_begin);
+
+ //? Draw box
output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
- if (Global::debug) {
- debug_times["cpu"].at(1) = time_micros() - debug_times["cpu"].at(1);
- debug_times["total"].at(1) += debug_times["cpu"].at(1);
- }
+
+ if (Global::debug) debug_timer("cpu", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Cpu:: -> " + (string)e.what());
@@ -501,9 +612,10 @@ namespace Runner {
//? If overlay isn't empty, print output without color and then print overlay on top
cout << Term::sync_start << (conf.overlay.empty()
? output + conf.clock
- : Theme::c("inactive_fg") + Fx::ub + Fx::uncolor(output + conf.clock) + conf.overlay)
+ : Fx::ub + Theme::c("inactive_fg") + Fx::uncolor(output + conf.clock) + conf.overlay)
<< Term::sync_end << flush;
}
+ //* ----------------------------------------------- THREAD LOOP -----------------------------------------------
pthread_exit(NULL);
}
@@ -516,20 +628,19 @@ namespace Runner {
if (stopping or Global::resized) return;
if (box == "overlay") {
- cout << Term::sync_start << Global::overlay << Term::sync_end;
+ cout << Term::sync_start << Global::overlay << Term::sync_end << flush;
}
else if (box == "clock") {
- if (not Global::clock.empty())
- cout << Term::sync_start << Global::clock << Term::sync_end;
+ cout << Term::sync_start << Global::clock << Term::sync_end << flush;
}
else if (box.empty() and Config::current_boxes.empty()) {
- cout << Term::sync_start << Term::clear + Mv::to(10, 10) << "No boxes shown!" << Term::sync_end;
+ cout << Term::sync_start << Term::clear + Mv::to(10, 10) << "No boxes shown!" << Term::sync_end << flush;
}
else {
Config::unlock();
Config::lock();
- //? Setup bitmask for selected boxes instead of parsing strings
+ //? Setup bitmask for selected boxes instead of parsing strings in _runner thread loop
bitset<8> box_mask;
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
box_mask |= box_bits.at(box);
@@ -537,9 +648,9 @@ namespace Runner {
current_conf = {box_mask, no_update, force_redraw, Global::overlay, Global::clock};
- do_work = true;
- atomic_notify(do_work);
+ thread_trigger();
+ //? Wait for _runner thread to be active before returning
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
}
}
@@ -554,14 +665,9 @@ namespace Runner {
exit(1);
}
else if (ret == EBUSY) {
- if (not active) {
- do_work = true;
- atomic_notify(do_work);
- sleep_ms(1);
- }
- else {
- atomic_wait(active);
- }
+ atomic_wait(active);
+ thread_trigger();
+ sleep_ms(1);
}
stopping = false;
}
@@ -585,11 +691,6 @@ int main(int argc, char **argv) {
std::signal(SIGTSTP, _signal_handler);
std::signal(SIGCONT, _signal_handler);
std::signal(SIGWINCH, _signal_handler);
- sigemptyset(&Runner::mask);
- sigaddset(&Runner::mask, SIGINT);
- sigaddset(&Runner::mask, SIGTSTP);
- sigaddset(&Runner::mask, SIGWINCH);
- sigaddset(&Runner::mask, SIGTERM);
//? Setup paths for config, log and user themes
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
@@ -707,6 +808,7 @@ int main(int argc, char **argv) {
Theme::setTheme();
//? 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);
@@ -743,10 +845,16 @@ int main(int argc, char **argv) {
if (Global::resized) {
Global::resized = false;
Draw::calcSizes();
+ update_clock();
Runner::run("all", true);
atomic_wait(Runner::active);
}
+ //? Update clock if needed
+ if (update_clock()) {
+ Runner::run("clock");
+ }
+
//? Start secondary collect & draw thread at the interval set by <update_ms> config value
if (time_ms() >= future_time) {
Runner::run("all");
@@ -767,14 +875,12 @@ int main(int argc, char **argv) {
//? Poll for input and process any input detected
else if (Input::poll(min(1000ul, future_time - current_time))) {
- if (not Runner::active)
- Config::unlock();
+ if (not Runner::active) Config::unlock();
Input::process(Input::get());
}
//? Break the loop at 1000ms intervals or if input polling was interrupted
- else
- break;
+ else break;
}
}
diff --git a/src/btop_config.cpp b/src/btop_config.cpp
index 688068c..ff6de59 100644
--- a/src/btop_config.cpp
+++ b/src/btop_config.cpp
@@ -47,6 +47,8 @@ namespace Config {
{"force_tty", "#* Set to true to force tty mode regardless if a real tty has been detected or not.\n"
"#* Will force 16-color mode and TTY theme, set all graph symbols to \"tty\" and swap out other non tty friendly symbols."},
+ {"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"
"#* \"braille\" offers the highest resolution but might not be included in all fonts.\n"
"#* \"block\" has half the resolution of braille but uses more common characters.\n"
@@ -112,7 +114,7 @@ namespace Config {
{"show_cpu_freq", "#* Show CPU frequency."},
- {"draw_clock", "#* Draw a clock at top of screen, formatting according to strftime, empty string to disable."},
+ {"clock_format", "#* Draw a clock at top of screen, formatting according to strftime, empty string to disable."},
{"background_update", "#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort."},
@@ -174,7 +176,7 @@ namespace Config {
{"cpu_sensor", "Auto"},
{"cpu_core_map", ""},
{"temp_scale", "celsius"},
- {"draw_clock", "%X"},
+ {"clock_format", "%X"},
{"custom_cpu_name", ""},
{"disks_filter", ""},
{"io_graph_speeds", ""},
@@ -188,6 +190,7 @@ namespace Config {
unordered_flat_map<string, bool> bools = {
{"theme_background", true},
{"truecolor", true},
+ {"rounded_corners", true},
{"proc_reversed", false},
{"proc_tree", false},
{"proc_colors", true},
@@ -362,6 +365,10 @@ namespace Config {
cread >> value;
if (not isint(value))
load_warnings.push_back("Got an invalid integer value for config name: " + name);
+ else if (name == "update_ms" and stoi(value) < 100) {
+ load_warnings.push_back("Config value update_ms set too low (<100), setting (100).");
+ ints.at(name) = 100;
+ }
else
ints.at(name) = stoi(value);
}
@@ -376,6 +383,8 @@ namespace Config {
load_warnings.push_back("Invalid log_level: " + value);
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
load_warnings.push_back("Invalid graph symbol identifier: " + value);
+ else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
+ load_warnings.push_back("Invalid graph symbol identifier for" + name + ": " + value);
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
load_warnings.push_back("Invalid box name(s) in shown_boxes: " + value);
else
diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp
index aa30e70..6b668a8 100644
--- a/src/btop_draw.cpp
+++ b/src/btop_draw.cpp
@@ -36,33 +36,6 @@ using namespace Tools;
namespace rng = std::ranges;
namespace Symbols {
- const string h_line = "─";
- const string v_line = "│";
- const string dotted_v_line = "╎";
- const string left_up = "┌";
- const string right_up = "┐";
- const string left_down = "└";
- const string right_down = "┘";
- const string round_left_up = "╭";
- const string round_right_up = "╮";
- const string round_left_down = "╰";
- const string round_right_down = "╯";
- const string title_left_down = "┘";
- const string title_right_down = "└";
- const string title_left = "┐";
- const string title_right = "┌";
- const string div_right = "┤";
- const string div_left = "├";
- const string div_up = "┬";
- const string div_down = "┴";
-
-
- const string up = "↑";
- const string down = "↓";
- const string left = "←";
- const string right = "→";
- const string enter = "↲";
-
const string meter = "■";
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
@@ -201,11 +174,12 @@ namespace Draw {
string out;
if (line_color.empty()) line_color = Theme::c("div_line");
const auto& tty_mode = Config::getB("tty_mode");
+ const auto& rounded = Config::getB("rounded_corners");
const string numbering = (num == 0) ? "" : Theme::c("hi_fg") + (tty_mode ? std::to_string(num) : Symbols::superscript.at(clamp(num, 0, 9)));
- const auto& right_up = (tty_mode ? Symbols::right_up : Symbols::round_right_up);
- const auto& left_up = (tty_mode ? Symbols::left_up : Symbols::round_left_up);
- const auto& right_down = (tty_mode ? Symbols::right_down : Symbols::round_right_down);
- const auto& left_down = (tty_mode ? Symbols::left_down : Symbols::round_left_down);
+ const auto& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
+ const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
+ const auto& right_down = (tty_mode or not rounded ? Symbols::right_down : Symbols::round_right_down);
+ const auto& left_down = (tty_mode or not rounded ? Symbols::left_down : Symbols::round_left_down);
out = Fx::reset + line_color;
@@ -583,14 +557,14 @@ namespace Mem {
for (const auto& name : mem_names) {
if (use_graphs)
- mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name, mem.percent.at(name)};
+ mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name, mem.percent.at(name), graph_symbol};
else
mem_meters[name] = Draw::Meter{mem_meter, name};
}
if (show_swap and has_swap) {
for (const auto& name : swap_names) {
if (use_graphs)
- mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name.substr(5), mem.percent.at(name)};
+ mem_graphs[name] = Draw::Graph{mem_meter, graph_height, name.substr(5), mem.percent.at(name), graph_symbol};
else
mem_meters[name] = Draw::Meter{mem_meter, name.substr(5)};
}
@@ -694,7 +668,7 @@ namespace Mem {
const auto& disks = mem.disks;
cx = x + mem_width - 1; 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") + Symbols::div_right + Mv::l(disks_width - 1);
+ 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 - 1);
if (io_mode) {
for (const auto& mount : mem.disks_order) {
if (not disks.contains(mount)) continue;
@@ -758,7 +732,7 @@ namespace Mem {
if (cmp_less_equal(disks.size() * 3 + (show_io_stat ? disk_ios : 0), height - 1)) {
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " Free:" + rjust(to_string(disk.free_percent) + '%', 4) : "F") + ' '
- + disk_meters_free.at(mount)(disk.free_percent) + rjust(human_free, (big_disk ? 9 : 7));
+ + disk_meters_free.at(mount)(disk.free_percent) + rjust(human_free, (big_disk ? 9 : 5));
cy++;
if (cmp_less_equal(disks.size() * 4 + (show_io_stat ? disk_ios : 0), height - 1)) cy++;
}
@@ -792,7 +766,7 @@ namespace Net {
auto& net_sync = Config::getB("net_sync");
auto& net_auto = Config::getB("net_auto");
auto& tty_mode = Config::getB("tty_mode");
- auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_proc"));
+ auto& graph_symbol = (t