mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 06:11:28 +02:00
Added clock, updated makefile and switch to using semaphore to trigger _runner thread
This commit is contained in:
parent
8bd97b4f24
commit
3f27fb24b5
112
Makefile
112
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)
|
||||
#? Default Make
|
||||
all: msg directories btop
|
||||
|
||||
#Make the Directories
|
||||
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
|
||||
|
16
README.md
16
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
|
||||
|
258
src/btop.cpp
258
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 = (tty_mode ? "tty" : Config::getS("graph_symbol_net"));
|
||||
string ip_addr = (net.ipv4.empty() ? net.ipv6 : net.ipv4);
|
||||
if (old_ip != ip_addr) {
|
||||
old_ip = ip_addr;
|
||||
@ -1191,7 +1165,7 @@ namespace Proc {
|
||||
//* Iteration over processes
|
||||
int lc = 0;
|
||||
for (int n=0; auto& p : plist) {
|
||||
if (p.filtered or n++ < start) continue;
|
||||
if (n++ < start or p.filtered) continue;
|
||||
bool is_selected = (lc + 1 == selected);
|
||||
if (is_selected) selected_pid = (int)p.pid;
|
||||
|
||||
@ -1261,7 +1235,7 @@ namespace Proc {
|
||||
out += c_color + uresize(p.name, width_left - 1) + end + ' ';
|
||||
width_left -= (ulen(p.name) + 1);
|
||||
}
|
||||
if (width_left > 7 and (not p.short_cmd.empty() or p.short_cmd == p.name)) {
|
||||
if (width_left > 7 and p.short_cmd != p.name) {
|
||||
out += g_color + '(' + uresize(p.short_cmd, width_left - 3, true) + ") ";
|
||||
width_left -= (ulen(p.short_cmd, true) + 3);
|
||||
}
|
||||
@ -1287,7 +1261,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
out += Fx::reset;
|
||||
while (lc++ < height - 4) out += Mv::to(y+lc+2, x+1) + string(width - 2, ' ');
|
||||
while (lc++ < height - 5) out += Mv::to(y+lc+1, x+1) + string(width - 2, ' ');
|
||||
|
||||
//? Draw scrollbar if needed
|
||||
if (numpids > select_max) {
|
||||
@ -1337,6 +1311,8 @@ namespace Draw {
|
||||
Mem::box.clear();
|
||||
Net::box.clear();
|
||||
Proc::box.clear();
|
||||
Global::clock.clear();
|
||||
Global::overlay.clear();
|
||||
|
||||
Input::mouse_mappings.clear();
|
||||
|
||||
|
@ -25,6 +25,35 @@ tab-size = 4
|
||||
|
||||
using std::string, std::vector, robin_hood::unordered_flat_map, std::deque;
|
||||
|
||||
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 = "↲";
|
||||
}
|
||||
|
||||
namespace Draw {
|
||||
|
||||
//* An editable text field
|
||||
|
@ -137,10 +137,10 @@ namespace Input {
|
||||
|
||||
key = mouse_event;
|
||||
|
||||
if (not Menu::active and key == "mouse_click") {
|
||||
if (key == "mouse_click") {
|
||||
const auto& [col, line] = mouse_pos;
|
||||
|
||||
for (const auto& [mapped_key, pos] : mouse_mappings) {
|
||||
for (const auto& [mapped_key, pos] : (Menu::active ? Menu::mouse_mappings : mouse_mappings)) {
|
||||
if (col >= pos.col and col < pos.col + pos.width and line >= pos.line and line < pos.line + pos.height) {
|
||||
key = mapped_key;
|
||||
break;
|
||||
@ -235,8 +235,10 @@ namespace Input {
|
||||
Proc::filter = { Config::getS("proc_filter") };
|
||||
old_filter = Proc::filter.text;
|
||||
}
|
||||
else if (key == "e")
|
||||
else if (key == "e") {
|
||||
Config::flip("proc_tree");
|
||||
no_update = false;
|
||||
}
|
||||
|
||||
else if (key == "r")
|
||||
Config::flip("proc_reversed");
|
||||
@ -313,6 +315,7 @@ namespace Input {
|
||||
auto& pid = Config::getI("selected_pid");
|
||||
if (key == "+" or key == "space") Proc::expand = pid;
|
||||
if (key == "-" or key == "space") Proc::collapse = pid;
|
||||
no_update = false;
|
||||
}
|
||||
else if (key == "t") {
|
||||
Logger::debug(key);
|
||||
|
@ -75,6 +75,10 @@ namespace Cpu {
|
||||
unordered_flat_map<int, int> core_mapping;
|
||||
}
|
||||
|
||||
namespace Net {
|
||||
uint64_t timestamp = 0;
|
||||
}
|
||||
|
||||
namespace Shared {
|
||||
|
||||
fs::path procPath, passwd_path;
|
||||
@ -137,6 +141,7 @@ namespace Shared {
|
||||
Mem::collect();
|
||||
|
||||
//? Init for namespace Net
|
||||
Net::timestamp = time_ms();
|
||||
Net::collect();
|
||||
|
||||
}
|
||||
@ -813,6 +818,7 @@ namespace Net {
|
||||
auto& config_iface = Config::getS("net_iface");
|
||||
auto& net_sync = Config::getB("net_sync");
|
||||
auto& net_auto = Config::getB("net_auto");
|
||||
auto new_timestamp = time_ms();
|
||||
|
||||
if (not no_update and errors < 3) {
|
||||
//? Get interface list using getifaddrs() wrapper
|
||||
@ -864,7 +870,7 @@ namespace Net {
|
||||
const uint64_t val = max(stoul(readfile(sys_file, "0")), saved_stat.last);
|
||||
|
||||
//? Update speed, total and top values
|
||||
saved_stat.speed = val - (saved_stat.last == 0 ? val : saved_stat.last);
|
||||
saved_stat.speed = round((double)(val - saved_stat.last) / ((double)(new_timestamp - timestamp) / 1000));
|
||||
if (saved_stat.speed > saved_stat.top) saved_stat.top = saved_stat.speed;
|
||||
if (saved_stat.offset > val) saved_stat.offset = 0;
|
||||
saved_stat.total = val - saved_stat.offset;
|
||||
@ -899,6 +905,8 @@ namespace Net {
|
||||
}
|
||||
net.compact();
|
||||
}
|
||||
|
||||
timestamp = new_timestamp;
|
||||
}
|
||||
|
||||
//? Return empty net_info struct if no interfaces was found
|
||||
@ -964,6 +972,9 @@ namespace Proc {
|
||||
|
||||
vector<proc_info> current_procs;
|
||||
unordered_flat_map<string, string> uid_user;
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev = false;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@ -976,17 +987,19 @@ namespace Proc {
|
||||
detail_container detailed;
|
||||
|
||||
//* Generate process tree list
|
||||
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false) {
|
||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<std::reference_wrapper<proc_info>>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false, const bool no_update=false, const bool should_filter=false) {
|
||||
auto cur_pos = out_procs.size();
|
||||
bool filtering = false;
|
||||
|
||||
//? If filtering, include children of matching processes
|
||||
if (not filter.empty() and not found) {
|
||||
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;
|
||||
@ -994,37 +1007,44 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
//? Add process to vector if not filtered out or currently in a collapsed sub-tree
|
||||
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
|
||||
if (not collapsed and not filtering) {
|
||||
out_procs.push_back(cur_proc);
|
||||
out_procs.push_back(std::ref(cur_proc));
|
||||
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(0, min(cmd_view.find(' '), cmd_view.size()));
|
||||
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
|
||||
out_procs.back().short_cmd = (string)cmd_view;
|
||||
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 (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
|
||||
out_procs.back().get().cpu_p += p.cpu_p;
|
||||
out_procs.back().get().mem += p.mem;
|
||||
out_procs.back().get().threads += p.threads;
|
||||
}
|
||||
if (collapsed and not filtering) {
|
||||
out_procs.back().cpu_p += p.cpu_p;
|
||||
out_procs.back().mem += p.mem;
|
||||
out_procs.back().threads += p.threads;
|
||||
cur_proc.filtered = true;
|
||||
}
|
||||
else children++;
|
||||
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found);
|
||||
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
|
||||
}
|
||||
if (collapsed or filtering) return;
|
||||
|
||||
//? Add tree terminator symbol if it's the last child in a sub-tree
|
||||
if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("]─"))
|
||||
out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ ");
|
||||
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
|
||||
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
|
||||
|
||||
//? Add collapse/expand symbols if process have any children
|
||||
out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
|
||||
out_procs.at(cur_pos).get().prefix = " │ "s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
|
||||
}
|
||||
|
||||
//* Get detailed info for selected process
|
||||
@ -1133,12 +1153,18 @@ namespace Proc {
|
||||
const auto& tree = Config::getB("proc_tree");
|
||||
const auto& show_detailed = Config::getB("show_detailed");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
const bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
const bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
ifstream pread;
|
||||
string long_string;
|
||||
string short_str;
|
||||
filter_found = 0;
|
||||
const double uptime = system_uptime();
|
||||
|
||||
const double uptime = system_uptime();
|
||||
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
@ -1193,7 +1219,7 @@ namespace Proc {
|
||||
const size_t pid = stoul(pid_str);
|
||||
found.push_back(pid);
|
||||
|
||||
//? Check if pid already exists
|
||||
//? Check if pid already exists in current_procs
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
bool no_cache = false;
|
||||
if (find_old == current_procs.end()) {
|
||||
@ -1210,6 +1236,7 @@ namespace Proc {
|
||||
if (not pread.good()) continue;
|
||||
getline(pread, new_proc.name);
|
||||
pread.close();
|
||||
//? Check for whitespace characters in name and set offset to get correct fields from stat file
|
||||
new_proc.name_offset = rng::count(new_proc.name, ' ');
|
||||
|
||||
pread.open(d.path() / "cmdline");
|
||||
@ -1241,7 +1268,6 @@ namespace Proc {
|
||||
pread.open(d.path() / "stat");
|
||||
if (not pread.good()) continue;
|
||||
|
||||
//? Check cached value for whitespace characters in name and set offset to get correct fields from stat file
|
||||
const auto& offset = new_proc.name_offset;
|
||||
short_str.clear();
|
||||
size_t x = 0, next_x = 3;
|
||||
@ -1318,19 +1344,7 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
// //? Clear dead processes from cache at a regular interval
|
||||
// if (++counter >= 1000 or (cache.size() > found.size() + 100)) {
|
||||
// counter = 0;
|
||||
// for (auto it = cache.begin(); it != cache.end();) {
|
||||
// if (not v_contains(found, it->first))
|
||||
// it = cache.erase(it);
|
||||
// else
|
||||
// it++;
|
||||
// }
|
||||
// cache.compact();
|
||||
// }
|
||||
|
||||
//? Clear dead processes from list
|
||||
//? Clear dead processes from current_procs
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element){ return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
|
||||
@ -1343,97 +1357,102 @@ namespace Proc {
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
|
||||
old_cputimes = cputimes;
|
||||
// current_procs.clear();
|
||||
// current_procs = procs;
|
||||
}
|
||||
//* ---------------------------------------------Collection done-----------------------------------------------
|
||||
|
||||
//* Sort processes
|
||||
switch (v_index(sort_vector, sorting)) {
|
||||
case 0: rng::sort(current_procs, rng::greater{}, &proc_info::pid); break;
|
||||
case 1: rng::sort(current_procs, rng::greater{}, &proc_info::name); break;
|
||||
case 2: rng::sort(current_procs, rng::greater{}, &proc_info::cmd); break;
|
||||
case 3: rng::sort(current_procs, rng::greater{}, &proc_info::threads); break;
|
||||
case 4: rng::sort(current_procs, rng::greater{}, &proc_info::user); break;
|
||||
case 5: rng::sort(current_procs, rng::greater{}, &proc_info::mem); break;
|
||||
case 6: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
|
||||
case 7: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
|
||||
}
|
||||
if (reverse) rng::reverse(current_procs);
|
||||
if (sorted_change or not no_update) {
|
||||
switch (v_index(sort_vector, sorting)) {
|
||||
case 0: rng::sort(current_procs, rng::greater{}, &proc_info::pid); break;
|
||||
case 1: rng::sort(current_procs, rng::greater{}, &proc_info::name); break;
|
||||
case 2: rng::sort(current_procs, rng::greater{}, &proc_info::cmd); break;
|
||||
case 3: rng::sort(current_procs, rng::greater{}, &proc_info::threads); break;
|
||||
case 4: rng::sort(current_procs, rng::greater{}, &proc_info::user); break;
|
||||
case 5: rng::sort(current_procs, rng::greater{}, &proc_info::mem); break;
|
||||
case 6: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
|
||||
case 7: rng::sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
|
||||
}
|
||||
if (reverse) rng::reverse(current_procs);
|
||||
|
||||
//* 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 < current_procs.size(); i++) {
|
||||
if (i <= 5 and current_procs.at(i).cpu_p > max)
|
||||
max = current_procs.at(i).cpu_p;
|
||||
else if (i == 6)
|
||||
target = (max > 30.0) ? max : 10.0;
|
||||
if (i == offset and current_procs.at(i).cpu_p > 30.0)
|
||||
offset++;
|
||||
else if (current_procs.at(i).cpu_p > target) {
|
||||
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
|
||||
if (++x > 10) 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 < current_procs.size(); i++) {
|
||||
if (i <= 5 and current_procs.at(i).cpu_p > max)
|
||||
max = current_procs.at(i).cpu_p;
|
||||
else if (i == 6)
|
||||
target = (max > 30.0) ? max : 10.0;
|
||||
if (i == offset and current_procs.at(i).cpu_p > 30.0)
|
||||
offset++;
|
||||
else if (current_procs.at(i).cpu_p > target) {
|
||||
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
|
||||
if (++x > 10) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//* Match filter if defined
|
||||
for (auto& p : current_procs) {
|
||||
if (not tree and not filter.empty()) {
|
||||
if (not s_contains(to_string(p.pid), filter)
|
||||
if (should_filter) {
|
||||
filter_found = 0;
|
||||
for (auto& p : current_procs) {
|
||||
if (not tree and not filter.empty()) {
|
||||
if (not s_contains(to_string(p.pid), filter)
|
||||
and not s_contains(p.name, filter)
|
||||
and not s_contains(p.cmd, filter)
|
||||
and not s_contains(p.user, filter)) {
|
||||
p.filtered = true;
|
||||
filter_found++;
|
||||
}
|
||||
else {
|
||||
p.filtered = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.filtered = false;
|
||||
}
|
||||
else if (not tree) {
|
||||
p.filtered = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//* Generate tree view if enabled
|
||||
if (tree) {
|
||||
if (tree and (not no_update or should_filter or sorted_change)) {
|
||||
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
|
||||
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
|
||||
if (collapser != current_procs.end()) {
|
||||
if (collapse == expand) {
|
||||
collapser->collapsed = not collapser->collapsed;
|
||||
collapse = expand = -1;
|
||||
}
|
||||
else if (collapse > -1) {
|
||||
collapser->collapsed = true;
|
||||
collapse = -1;
|
||||
}
|
||||
else if (expand > -1) {
|
||||
collapser->collapsed = false;
|
||||
expand = -1;
|
||||
}
|
||||
}
|
||||
collapse = expand = -1;
|
||||
}
|
||||
if (should_filter or not filter.empty()) filter_found = 0;
|
||||
|
||||
vector<proc_info> tree_procs;
|
||||
vector<std::reference_wrapper<proc_info>> tree_procs;
|
||||
tree_procs.reserve(current_procs.size());
|
||||
|
||||
//? Stable sort to retain selected sorting among processes with the same parent
|
||||
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
|
||||
|
||||
//? Start recursive iteration over processes with the lowest shared parent pids
|
||||
for (const auto& p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
|
||||
_tree_gen(p, current_procs, tree_procs, 0, false, filter);
|
||||
for (auto& p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
|
||||
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
|
||||
}
|
||||
|
||||
//procs.clear();
|
||||
current_procs = std::move(tree_procs);
|
||||
//? Final sort based on tree index
|
||||
rng::sort(current_procs, rng::less{}, &proc_info::tree_index);
|
||||
if (reverse) rng::reverse(current_procs);
|
||||
|
||||
}
|
||||
|
||||
numpids = (not filter.empty() ? filter_found : (int)current_procs.size());
|
||||
numpids = (int)current_procs.size() - filter_found;
|
||||
|
||||
return current_procs;
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ namespace Menu {
|
||||
atomic<bool> active (false);
|
||||
string output;
|
||||
|
||||
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||
|
||||
const unordered_flat_map<string, unordered_flat_map<string, vector<string>>> menus = {
|
||||
{ "options", {
|
||||
{ "normal", {
|
||||
|
@ -21,6 +21,8 @@ tab-size = 4
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
#include <btop_input.hpp>
|
||||
|
||||
using std::string, std::atomic;
|
||||
|
||||
namespace Menu {
|
||||
@ -28,5 +30,7 @@ namespace Menu {
|
||||
extern atomic<bool> active;
|
||||
extern string output;
|
||||
|
||||
//? line, col, height, width
|
||||
extern unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ namespace Proc {
|
||||
char state = '0';
|
||||
uint64_t cpu_n = 0, p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
||||
string prefix = "";
|
||||
size_t depth = 0;
|
||||
size_t depth = 0, tree_index = 0;
|
||||
bool collapsed = false, filtered = false;
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,7 @@ tab-size = 4
|
||||
#include <robin_hood.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
@ -121,7 +122,7 @@ namespace Term {
|
||||
void restore() {
|
||||
if (initialized) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings);
|
||||
cout << Term::mouse_off << Term::normal_screen << Term::show_cursor << flush;
|
||||
cout << mouse_off << clear << Fx::reset << normal_screen << show_cursor << flush;
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@ -167,6 +168,15 @@ namespace Tools {
|
||||
return str;
|
||||
}
|
||||
|
||||
string s_replace(const string& str, const string& from, const string& to) {
|
||||
size_t start_pos = str.find(from);
|
||||
if(start_pos == std::string::npos)
|
||||
return str;
|
||||
string out = str;
|
||||
out.replace(start_pos, from.length(), to);
|
||||
return out;
|
||||
}
|
||||
|
||||
string ltrim(const string& str, const string& t_str) {
|
||||
string_view str_v = str;
|
||||
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
|
||||
@ -310,16 +320,6 @@ namespace Tools {
|
||||
atomic_notify(this->atom);
|
||||
}
|
||||
|
||||
thread_lock::thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) {
|
||||
status = pthread_mutex_lock(&pt_mutex);
|
||||
}
|
||||
|
||||
thread_lock::~thread_lock() {
|
||||
if (status == 0) {
|
||||
pthread_mutex_unlock(&pt_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
string readfile(const std::filesystem::path& path, const string& fallback) {
|
||||
if (not fs::exists(path)) return fallback;
|
||||
string out;
|
||||
@ -345,6 +345,18 @@ namespace Tools {
|
||||
return {0, ""};
|
||||
}
|
||||
|
||||
string hostname() {
|
||||
char host[HOST_NAME_MAX];
|
||||
gethostname(host, HOST_NAME_MAX);
|
||||
return (string)host;
|
||||
}
|
||||
|
||||
string username() {
|
||||
auto user = getenv("LOGNAME");
|
||||
if (user == NULL or strcmp(user, "")) user = getenv("USER");
|
||||
return (user != NULL ? user : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Logger {
|
||||
|
@ -144,6 +144,9 @@ namespace Tools {
|
||||
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
||||
string luresize(const string str, const size_t len, const bool wide=false);
|
||||
|
||||
//* Replace <from> in <str> with <to> and return new string
|
||||
string s_replace(const string& str, const string& from, const string& to);
|
||||
|
||||
//* Capatilize <str>
|
||||
inline string capitalize(string str) {
|
||||
str.at(0) = toupper(str.at(0));
|
||||
@ -263,8 +266,11 @@ namespace Tools {
|
||||
//* Return current time in <strf> format
|
||||
string strf_time(const string& strf);
|
||||
|
||||
string hostname();
|
||||
string username();
|
||||
|
||||
#if __GNUC__ < 11
|
||||
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { while (atom.load() == old); }
|
||||
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { while (atom.load() == old) sleep_ms(1); }
|
||||
inline void atomic_notify(const atomic<bool>& atom) noexcept { (void)atom; }
|
||||
#else
|
||||
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { atom.wait(old); }
|
||||
@ -280,15 +286,6 @@ namespace Tools {
|
||||
~atomic_lock();
|
||||
};
|
||||
|
||||
//* RAII wrapper for pthread_mutex_lock & unlock
|
||||
class thread_lock {
|
||||
pthread_mutex_t& pt_mutex;
|
||||
public:
|
||||
int status;
|
||||
thread_lock(pthread_mutex_t& mtx);
|
||||
~thread_lock();
|
||||
};
|
||||
|
||||
//* Read a complete file and return as a string
|
||||
string readfile(const std::filesystem::path& path, const string& fallback="");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user