mirror of https://github.com/aristocratos/btop.git
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
|
PREFIX ?= /usr/local
|
||||||
DOCDIR ?= $(PREFIX)/share/btop/doc
|
|
||||||
|
|
||||||
#Compiler and Linker
|
#? Compiler and Linker
|
||||||
CXX ?= g++
|
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)
|
ifneq ($(CXX),g++-10)
|
||||||
CXX_VERSION = $(shell $(CXX) -dumpfullversion -dumpversion | cut -f1 -d"." || echo 0)
|
V_MAJOR = $(shell echo $(CXX_VERSION) | cut -f1 -d"." || echo 0)
|
||||||
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
|
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
|
||||||
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
||||||
override CXX = g++-11
|
override CXX = g++-11
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#Only enable fcf-protection if on x86
|
#? Only enable fcf-protection if on x86_64
|
||||||
ARCH = $(shell uname -p)
|
ARCH = $(shell uname -p ||true)
|
||||||
ifeq ($(ARCH),x86_64)
|
ifeq ($(ARCH),x86_64)
|
||||||
ADDFLAGS = -fcf-protection
|
ADDFLAGS = -fcf-protection
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),unknown)
|
||||||
|
ARCH = $(shell uname -m ||true)
|
||||||
|
endif
|
||||||
|
PLATFORM = $(shell uname -s ||true)
|
||||||
|
|
||||||
#The Target Binary Program
|
#? Use all CPU cores (will only be set if using Make >=4.3)
|
||||||
TARGET := btop
|
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
|
SRCDIR := src
|
||||||
INCDIR := include
|
INCDIR := include
|
||||||
BUILDDIR := obj
|
BUILDDIR := obj
|
||||||
|
@ -32,7 +43,7 @@ SRCEXT := cpp
|
||||||
DEPEXT := d
|
DEPEXT := d
|
||||||
OBJEXT := o
|
OBJEXT := o
|
||||||
|
|
||||||
#Flags, Libraries and Includes
|
#? Flags, Libraries and Includes
|
||||||
REQFLAGS := -std=c++20
|
REQFLAGS := -std=c++20
|
||||||
WARNFLAGS := -Wall -Wextra -Wno-stringop-overread -pedantic -pedantic-errors -Wfatal-errors
|
WARNFLAGS := -Wall -Wextra -Wno-stringop-overread -pedantic -pedantic-errors -Wfatal-errors
|
||||||
OPTFLAGS := -O2 -ftree-loop-vectorize
|
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 CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||||
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||||
INC := -I$(INCDIR) -I$(SRCDIR)
|
INC := -I$(INCDIR) -I$(SRCDIR)
|
||||||
|
SU_USER := root
|
||||||
|
SU_GROUP := root
|
||||||
|
|
||||||
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
||||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||||
|
|
||||||
#Default Make
|
#? Default Make
|
||||||
all: directories $(TARGET)
|
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:
|
directories:
|
||||||
@mkdir -p $(TARGETDIR)
|
@mkdir -p $(TARGETDIR)
|
||||||
@mkdir -p $(BUILDDIR)
|
@mkdir -p $(BUILDDIR)
|
||||||
|
|
||||||
#Clean only Objects
|
#? Clean only Objects
|
||||||
clean:
|
clean:
|
||||||
|
@printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
|
||||||
@rm -rf $(BUILDDIR)
|
@rm -rf $(BUILDDIR)
|
||||||
|
|
||||||
#Full Clean, Objects and Binaries
|
#? Clean Objects and Binaries
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
@printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
|
||||||
@rm -rf $(TARGETDIR)
|
@rm -rf $(TARGETDIR)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
@printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
|
||||||
@mkdir -p $(DESTDIR)$(PREFIX)/bin
|
@mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
@cp -p $(TARGETDIR)/btop $(DESTDIR)$(PREFIX)/bin/btop
|
@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
|
@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
|
#? Set suid bit for btop for $SU_USER in SU_GROUP, will make btop run with (root by default) privileges regardless of actual user
|
||||||
su-setuid:
|
setuid:
|
||||||
@su --session-command "chown root:root $(DESTDIR)$(PREFIX)/bin/btop && chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop" root
|
@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:
|
uninstall:
|
||||||
|
@printf "\033[1;91mRemoving: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\033[0m\n"
|
||||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
|
@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
|
@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))
|
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
|
||||||
|
|
||||||
#Link
|
#? Link
|
||||||
btop: $(OBJECTS)
|
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)
|
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
||||||
$(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
|
@sleep 0.1 2>/dev/null || true
|
||||||
@$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
|
@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
|
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||||
@sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
|
@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)
|
@sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
|
||||||
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
|
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||||
|
|
||||||
#Non-File Targets
|
#? Non-File Targets
|
||||||
.PHONY: all
|
.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:
|
For best experience, a terminal with support for:
|
||||||
|
|
||||||
* 24-bit truecolor ([See list of terminals with truecolor support](https://gist.github.com/XVilka/8346728))
|
* 24-bit truecolor ([See list of terminals with truecolor support](https://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.
|
* 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 now available as well.)
|
* 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)
|
* Wide characters (Are sometimes problematic in web-based terminals)
|
||||||
|
|
||||||
Also needs a UTF8 locale and a font that covers:
|
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.
|
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.
|
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
|
## Screenshots
|
||||||
|
@ -152,11 +153,12 @@ sudo make install
|
||||||
# only use "sudo" when installing to a NON user owned directory
|
# 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
|
``` bash
|
||||||
# run after make install and use same PREFIX if any was used at install
|
# 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
|
sudo make uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
>to remove any object files
|
>to remove any object files from source dir
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
>to remove all object files, binaries and created directories
|
>to remove all object files, binaries and created directories in source dir
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make distclean
|
make distclean
|
||||||
|
|
256
src/btop.cpp
256
src/btop.cpp
|
@ -27,6 +27,8 @@ tab-size = 4
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <tuple>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include <btop_shared.hpp>
|
#include <btop_shared.hpp>
|
||||||
#include <btop_tools.hpp>
|
#include <btop_tools.hpp>
|
||||||
|
@ -53,8 +55,8 @@ tab-size = 4
|
||||||
#error Platform not supported!
|
#error Platform not supported!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using std::string, std::string_view, std::vector, std::array, std::atomic, std::endl, std::cout, std::min;
|
using std::string, std::string_view, std::vector, std::atomic, std::endl, std::cout, std::min, std::flush, std::endl;
|
||||||
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_literals::operator""s, std::to_string, std::future, std::async, std::bitset, std::future_status;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
using namespace Tools;
|
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"
|
<< " -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_on force (ON) tty mode, max 16 colors and tty friendly graph symbols\n"
|
||||||
<< " +t, --tty_off force (OFF) tty mode\n"
|
<< " +t, --tty_off force (OFF) tty mode\n"
|
||||||
<< " --utf-foce force start even if no UTF-8 locale was detected"
|
<< " --utf-foce force start even if no UTF-8 locale was detected\n"
|
||||||
<< " --debug start in debug mode with loglevel set to DEBUG\n"
|
<< " --debug start in DEBUG mode: shows microsecond timer for information collect\n"
|
||||||
|
<< " and screen draw functions and sets loglevel to DEBUG\n"
|
||||||
<< endl;
|
<< endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -270,12 +273,89 @@ void banner_gen() {
|
||||||
+ Fx::i + "v" + Global::Version + Fx::ui;
|
+ 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
|
//* Manages secondary thread for collection and drawing of boxes
|
||||||
namespace Runner {
|
namespace Runner {
|
||||||
atomic<bool> active (false);
|
atomic<bool> active (false);
|
||||||
atomic<bool> stopping (false);
|
atomic<bool> stopping (false);
|
||||||
atomic<bool> waiting (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;
|
string output;
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
|
@ -296,6 +376,17 @@ namespace Runner {
|
||||||
cpu_present, cpu_running
|
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 proc_done = 0b0000'0011;
|
||||||
const uint_fast8_t net_done = 0b0000'1100;
|
const uint_fast8_t net_done = 0b0000'1100;
|
||||||
const uint_fast8_t mem_done = 0b0011'0000;
|
const uint_fast8_t mem_done = 0b0011'0000;
|
||||||
|
@ -314,13 +405,35 @@ namespace Runner {
|
||||||
|
|
||||||
struct runner_conf current_conf;
|
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 ----------------------------------
|
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
|
||||||
void * _runner(void * _) {
|
void * _runner(void * _) {
|
||||||
(void)_;
|
(void)_;
|
||||||
//? Block all signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
|
//? 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_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);
|
thread_lock pt_lck(mtx);
|
||||||
if (pt_lck.status != 0) {
|
if (pt_lck.status != 0) {
|
||||||
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
|
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;
|
stopping = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
||||||
while (not Global::quitting) {
|
while (not Global::quitting) {
|
||||||
atomic_wait(do_work, false);
|
thread_wait();
|
||||||
do_work = false;
|
|
||||||
if (stopping or Global::resized) {
|
if (stopping or Global::resized) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& conf = current_conf;
|
//? Atomic lock used for blocking non-thread safe actions in main thread
|
||||||
|
|
||||||
//? Secondary atomic lock used for signaling status to main thread
|
|
||||||
atomic_lock lck(active);
|
atomic_lock lck(active);
|
||||||
|
|
||||||
|
auto& conf = current_conf;
|
||||||
|
|
||||||
//! DEBUG stats
|
//! DEBUG stats
|
||||||
if (Global::debug) {
|
if (Global::debug) {
|
||||||
debug_times.clear();
|
debug_times.clear();
|
||||||
|
@ -356,12 +469,17 @@ namespace Runner {
|
||||||
future<Mem::mem_info&> mem;
|
future<Mem::mem_info&> mem;
|
||||||
future<Net::net_info&> net;
|
future<Net::net_info&> net;
|
||||||
future<vector<Proc::proc_info>&> proc;
|
future<vector<Proc::proc_info>&> proc;
|
||||||
|
|
||||||
|
//? Loop until all box flags present in bitmask have been zeroed
|
||||||
while (conf.box_mask.count() > 0) {
|
while (conf.box_mask.count() > 0) {
|
||||||
if (stopping) break;
|
if (stopping) break;
|
||||||
|
|
||||||
//? PROC
|
//? PROC
|
||||||
if (conf.box_mask.test(proc_present)) {
|
if (conf.box_mask.test(proc_present)) {
|
||||||
if (not conf.box_mask.test(proc_running)) {
|
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);
|
proc = async(Proc::collect, conf.no_update);
|
||||||
conf.box_mask.set(proc_running);
|
conf.box_mask.set(proc_running);
|
||||||
}
|
}
|
||||||
|
@ -370,16 +488,12 @@ namespace Runner {
|
||||||
|
|
||||||
else if (proc.wait_for(std::chrono::microseconds(10)) == future_status::ready) {
|
else if (proc.wait_for(std::chrono::microseconds(10)) == future_status::ready) {
|
||||||
try {
|
try {
|
||||||
if (Global::debug) {
|
if (Global::debug) debug_timer("proc", draw_begin);
|
||||||
debug_times["proc"].at(1) = time_micros();
|
|
||||||
debug_times["proc"].at(0) = debug_times["proc"].at(1) - debug_times["proc"].at(0);
|
//? Draw box
|
||||||
debug_times["total"].at(0) += debug_times["proc"].at(0);
|
|
||||||
}
|
|
||||||
output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
|
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);
|
if (Global::debug) debug_timer("proc", draw_done);
|
||||||
debug_times["total"].at(1) += debug_times["proc"].at(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Proc:: -> " + (string)e.what());
|
throw std::runtime_error("Proc:: -> " + (string)e.what());
|
||||||
|
@ -387,10 +501,13 @@ namespace Runner {
|
||||||
conf.box_mask ^= proc_done;
|
conf.box_mask ^= proc_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//? NET
|
//? NET
|
||||||
if (conf.box_mask.test(net_present)) {
|
if (conf.box_mask.test(net_present)) {
|
||||||
if (not conf.box_mask.test(net_running)) {
|
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);
|
net = async(Net::collect, conf.no_update);
|
||||||
conf.box_mask.set(net_running);
|
conf.box_mask.set(net_running);
|
||||||
}
|
}
|
||||||
|
@ -399,16 +516,12 @@ namespace Runner {
|
||||||
|
|
||||||
else if (net.wait_for(ZeroSec) == future_status::ready) {
|
else if (net.wait_for(ZeroSec) == future_status::ready) {
|
||||||
try {
|
try {
|
||||||
if (Global::debug) {
|
if (Global::debug) debug_timer("net", draw_begin);
|
||||||
debug_times["net"].at(1) = time_micros();
|
|
||||||
debug_times["net"].at(0) = debug_times["net"].at(1) - debug_times["net"].at(0);
|
//? Draw box
|
||||||
debug_times["total"].at(0) += debug_times["net"].at(0);
|
|
||||||
}
|
|
||||||
output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
|
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);
|
if (Global::debug) debug_timer("net", draw_done);
|
||||||
debug_times["total"].at(1) += debug_times["net"].at(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Net:: -> " + (string)e.what());
|
throw std::runtime_error("Net:: -> " + (string)e.what());
|
||||||
|
@ -416,10 +529,13 @@ namespace Runner {
|
||||||
conf.box_mask ^= net_done;
|
conf.box_mask ^= net_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//? MEM
|
//? MEM
|
||||||
if (conf.box_mask.test(mem_present)) {
|
if (conf.box_mask.test(mem_present)) {
|
||||||
if (not conf.box_mask.test(mem_running)) {
|
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);
|
mem = async(Mem::collect, conf.no_update);
|
||||||
conf.box_mask.set(mem_running);
|
conf.box_mask.set(mem_running);
|
||||||
}
|
}
|
||||||
|
@ -428,16 +544,12 @@ namespace Runner {
|
||||||
|
|
||||||
else if (mem.wait_for(ZeroSec) == future_status::ready) {
|
else if (mem.wait_for(ZeroSec) == future_status::ready) {
|
||||||
try {
|
try {
|
||||||
if (Global::debug) {
|
if (Global::debug) debug_timer("mem", draw_begin);
|
||||||
debug_times["mem"].at(1) = time_micros();
|
|
||||||
debug_times["mem"].at(0) = debug_times["mem"].at(1) - debug_times["mem"].at(0);
|
//? Draw box
|
||||||
debug_times["total"].at(0) += debug_times["mem"].at(0);
|
|
||||||
}
|
|
||||||
output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
|
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);
|
if (Global::debug) debug_timer("mem", draw_done);
|
||||||
debug_times["total"].at(1) += debug_times["mem"].at(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Mem:: -> " + (string)e.what());
|
throw std::runtime_error("Mem:: -> " + (string)e.what());
|
||||||
|
@ -445,10 +557,13 @@ namespace Runner {
|
||||||
conf.box_mask ^= mem_done;
|
conf.box_mask ^= mem_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//? CPU
|
//? CPU
|
||||||
if (conf.box_mask.test(cpu_present)) {
|
if (conf.box_mask.test(cpu_present)) {
|
||||||
if (not conf.box_mask.test(cpu_running)) {
|
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);
|
cpu = async(Cpu::collect, conf.no_update);
|
||||||
conf.box_mask.set(cpu_running);
|
conf.box_mask.set(cpu_running);
|
||||||
}
|
}
|
||||||
|
@ -457,16 +572,12 @@ namespace Runner {
|
||||||
|
|
||||||
else if (cpu.wait_for(ZeroSec) == future_status::ready) {
|
else if (cpu.wait_for(ZeroSec) == future_status::ready) {
|
||||||
try {
|
try {
|
||||||
if (Global::debug) {
|
if (Global::debug) debug_timer("cpu", draw_begin);
|
||||||
debug_times["cpu"].at(1) = time_micros();
|
|
||||||
debug_times["cpu"].at(0) = debug_times["cpu"].at(1) - debug_times["cpu"].at(0);
|
//? Draw box
|
||||||
debug_times["total"].at(0) += debug_times["cpu"].at(0);
|
|
||||||
}
|
|
||||||
output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
|
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);
|
if (Global::debug) debug_timer("cpu", draw_done);
|
||||||
debug_times["total"].at(1) += debug_times["cpu"].at(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Cpu:: -> " + (string)e.what());
|
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
|
//? If overlay isn't empty, print output without color and then print overlay on top
|
||||||
cout << Term::sync_start << (conf.overlay.empty()
|
cout << Term::sync_start << (conf.overlay.empty()
|
||||||
? output + conf.clock
|
? 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;
|
<< Term::sync_end << flush;
|
||||||
}
|
}
|
||||||
|
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
||||||
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
@ -516,20 +628,19 @@ namespace Runner {
|
||||||
if (stopping or Global::resized) return;
|
if (stopping or Global::resized) return;
|
||||||
|
|
||||||
if (box == "overlay") {
|
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") {
|
else if (box == "clock") {
|
||||||
if (not Global::clock.empty())
|
cout << Term::sync_start << Global::clock << Term::sync_end << flush;
|
||||||
cout << Term::sync_start << Global::clock << Term::sync_end;
|
|
||||||
}
|
}
|
||||||
else if (box.empty() and Config::current_boxes.empty()) {
|
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 {
|
else {
|
||||||
Config::unlock();
|
Config::unlock();
|
||||||
Config::lock();
|
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;
|
bitset<8> box_mask;
|
||||||
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
|
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
|
||||||
box_mask |= box_bits.at(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};
|
current_conf = {box_mask, no_update, force_redraw, Global::overlay, Global::clock};
|
||||||
|
|
||||||
do_work = true;
|
thread_trigger();
|
||||||
atomic_notify(do_work);
|
|
||||||
|
|
||||||
|
//? Wait for _runner thread to be active before returning
|
||||||
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
|
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,14 +665,9 @@ namespace Runner {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
else if (ret == EBUSY) {
|
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;
|
stopping = false;
|
||||||
}
|
}
|
||||||
|
@ -585,11 +691,6 @@ int main(int argc, char **argv) {
|
||||||
std::signal(SIGTSTP, _signal_handler);
|
std::signal(SIGTSTP, _signal_handler);
|
||||||
std::signal(SIGCONT, _signal_handler);
|
std::signal(SIGCONT, _signal_handler);
|
||||||
std::signal(SIGWINCH, _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
|
//? Setup paths for config, log and user themes
|
||||||
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
||||||
|
@ -707,6 +808,7 @@ int main(int argc, char **argv) {
|
||||||
Theme::setTheme();
|
Theme::setTheme();
|
||||||
|
|
||||||
//? Start runner thread
|
//? Start runner thread
|
||||||
|
Runner::thread_sem_init();
|
||||||
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
||||||
Global::exit_error_msg = "Failed to create _runner thread!";
|
Global::exit_error_msg = "Failed to create _runner thread!";
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -743,10 +845,16 @@ int main(int argc, char **argv) {
|
||||||
if (Global::resized) {
|
if (Global::resized) {
|
||||||
Global::resized = false;
|
Global::resized = false;
|
||||||
Draw::calcSizes();
|
Draw::calcSizes();
|
||||||
|
update_clock();
|
||||||
Runner::run("all", true);
|
Runner::run("all", true);
|
||||||
atomic_wait(Runner::active);
|
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
|
//? Start secondary collect & draw thread at the interval set by <update_ms> config value
|
||||||
if (time_ms() >= future_time) {
|
if (time_ms() >= future_time) {
|
||||||
Runner::run("all");
|
Runner::run("all");
|
||||||
|
@ -767,14 +875,12 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
//? Poll for input and process any input detected
|
//? Poll for input and process any input detected
|
||||||
else if (Input::poll(min(1000ul, future_time - current_time))) {
|
else if (Input::poll(min(1000ul, future_time - current_time))) {
|
||||||
if (not Runner::active)
|
if (not Runner::active) Config::unlock();
|
||||||
Config::unlock();
|
|
||||||
Input::process(Input::get());
|
Input::process(Input::get());
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Break the loop at 1000ms intervals or if input polling was interrupted
|
//? Break the loop at 1000ms intervals or if input polling was interrupted
|
||||||
else
|
else break;
|
||||||
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"
|
{"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."},
|
"#* 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"
|
{"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"
|
"#* \"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"
|
"#* \"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."},
|
{"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."},
|
{"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_sensor", "Auto"},
|
||||||
{"cpu_core_map", ""},
|
{"cpu_core_map", ""},
|
||||||
{"temp_scale", "celsius"},
|
{"temp_scale", "celsius"},
|
||||||
{"draw_clock", "%X"},
|
{"clock_format", "%X"},
|
||||||
{"custom_cpu_name", ""},
|
{"custom_cpu_name", ""},
|
||||||
{"disks_filter", ""},
|
{"disks_filter", ""},
|
||||||
{"io_graph_speeds", ""},
|
{"io_graph_speeds", ""},
|
||||||
|
@ -188,6 +190,7 @@ namespace Config {
|
||||||
unordered_flat_map<string, bool> bools = {
|
unordered_flat_map<string, bool> bools = {
|
||||||
{"theme_background", true},
|
{"theme_background", true},
|
||||||
{"truecolor", true},
|
{"truecolor", true},
|
||||||
|
{"rounded_corners", true},
|
||||||
{"proc_reversed", false},
|
{"proc_reversed", false},
|
||||||
{"proc_tree", false},
|
{"proc_tree", false},
|
||||||
{"proc_colors", true},
|
{"proc_colors", true},
|
||||||
|
@ -362,6 +365,10 @@ namespace Config {
|
||||||
cread >> value;
|
cread >> value;
|
||||||
if (not isint(value))
|
if (not isint(value))
|
||||||
load_warnings.push_back("Got an invalid integer value for config name: " + name);
|
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
|
else
|
||||||
ints.at(name) = stoi(value);
|
ints.at(name) = stoi(value);
|
||||||
}
|
}
|
||||||
|
@ -376,6 +383,8 @@ namespace Config {
|
||||||
load_warnings.push_back("Invalid log_level: " + value);
|
load_warnings.push_back("Invalid log_level: " + value);
|
||||||
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
|
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
|
||||||
load_warnings.push_back("Invalid graph symbol identifier: " + 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))
|
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);
|
load_warnings.push_back("Invalid box name(s) in shown_boxes: " + value);
|
||||||
else
|
else
|
||||||
|
|
|
@ -36,33 +36,6 @@ using namespace Tools;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
|
||||||
namespace Symbols {
|
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 string meter = "■";
|
||||||
|
|
||||||
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
|
const array<string, 10> superscript = { "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" };
|
||||||
|
@ -201,11 +174,12 @@ namespace Draw {
|
||||||
string out;
|
string out;
|
||||||
if (line_color.empty()) line_color = Theme::c("div_line");
|
if (line_color.empty()) line_color = Theme::c("div_line");
|
||||||
const auto& tty_mode = Config::getB("tty_mode");
|
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 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& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
|
||||||
const auto& left_up = (tty_mode ? Symbols::left_up : Symbols::round_left_up);
|
const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
|
||||||
const auto& right_down = (tty_mode ? Symbols::right_down : Symbols::round_right_down);
|
const auto& right_down = (tty_mode or not rounded ? Symbols::right_down : Symbols::round_right_down);
|
||||||
const auto& left_down = (tty_mode ? Symbols::left_down : Symbols::round_left_down);
|
const auto& left_down = (tty_mode or not rounded ? Symbols::left_down : Symbols::round_left_down);
|
||||||
|
|
||||||
out = Fx::reset + line_color;
|
out = Fx::reset + line_color;
|
||||||
|
|
||||||
|
@ -583,14 +557,14 @@ namespace Mem {
|
||||||
for (const auto& name : mem_names) {
|
for (const auto& name : mem_names) {
|
||||||
|
|
||||||
if (use_graphs)
|
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
|
else
|
||||||
mem_meters[name] = Draw::Meter{mem_meter, name};
|
mem_meters[name] = Draw::Meter{mem_meter, name};
|
||||||
}
|
}
|
||||||
if (show_swap and has_swap) {
|
if (show_swap and has_swap) {
|
||||||
for (const auto& name : swap_names) {
|
for (const auto& name : swap_names) {
|
||||||
if (use_graphs)
|
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
|
else
|
||||||
mem_meters[name] = Draw::Meter{mem_meter, name.substr(5)};
|
mem_meters[name] = Draw::Meter{mem_meter, name.substr(5)};
|
||||||
}
|
}
|
||||||
|
@ -694,7 +668,7 @@ namespace Mem {
|
||||||
const auto& disks = mem.disks;
|
const auto& disks = mem.disks;
|
||||||
cx = x + mem_width - 1; cy = 0;
|
cx = x + mem_width - 1; cy = 0;
|
||||||
const bool big_disk = disks_width >= 25;
|
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) {
|
if (io_mode) {
|
||||||
for (const auto& mount : mem.disks_order) {
|
for (const auto& mount : mem.disks_order) {
|
||||||
if (not disks.contains(mount)) continue;
|
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)) {
|
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") + ' '
|
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++;
|
cy++;
|
||||||
if (cmp_less_equal(disks.size() * 4 + (show_io_stat ? disk_ios : 0), height - 1)) 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_sync = Config::getB("net_sync");
|
||||||
auto& net_auto = Config::getB("net_auto");
|
auto& net_auto = Config::getB("net_auto");
|
||||||
auto& tty_mode = Config::getB("tty_mode");
|
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);
|
string ip_addr = (net.ipv4.empty() ? net.ipv6 : net.ipv4);
|
||||||
if (old_ip != ip_addr) {
|
if (old_ip != ip_addr) {
|
||||||
old_ip = ip_addr;
|
old_ip = ip_addr;
|
||||||
|
@ -1191,7 +1165,7 @@ namespace Proc {
|
||||||
//* Iteration over processes
|
//* Iteration over processes
|
||||||
int lc = 0;
|
int lc = 0;
|
||||||
for (int n=0; auto& p : plist) {
|
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);
|
bool is_selected = (lc + 1 == selected);
|
||||||
if (is_selected) selected_pid = (int)p.pid;
|
if (is_selected) selected_pid = (int)p.pid;
|
||||||
|
|
||||||
|
@ -1261,7 +1235,7 @@ namespace Proc {
|
||||||
out += c_color + uresize(p.name, width_left - 1) + end + ' ';
|
out += c_color + uresize(p.name, width_left - 1) + end + ' ';
|
||||||
width_left -= (ulen(p.name) + 1);
|
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) + ") ";
|
out += g_color + '(' + uresize(p.short_cmd, width_left - 3, true) + ") ";
|
||||||
width_left -= (ulen(p.short_cmd, true) + 3);
|
width_left -= (ulen(p.short_cmd, true) + 3);
|
||||||
}
|
}
|
||||||
|
@ -1287,7 +1261,7 @@ namespace Proc {
|
||||||
}
|
}
|
||||||
|
|
||||||
out += Fx::reset;
|
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
|
//? Draw scrollbar if needed
|
||||||
if (numpids > select_max) {
|
if (numpids > select_max) {
|
||||||
|
@ -1337,6 +1311,8 @@ namespace Draw {
|
||||||
Mem::box.clear();
|
Mem::box.clear();
|
||||||
Net::box.clear();
|
Net::box.clear();
|
||||||
Proc::box.clear();
|
Proc::box.clear();
|
||||||
|
Global::clock.clear();
|
||||||
|
Global::overlay.clear();
|
||||||
|
|
||||||
Input::mouse_mappings.clear();
|
Input::mouse_mappings.clear();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,35 @@ tab-size = 4
|
||||||
|
|
||||||
using std::string, std::vector, robin_hood::unordered_flat_map, std::deque;
|
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 {
|
namespace Draw {
|
||||||
|
|
||||||
//* An editable text field
|
//* An editable text field
|
||||||
|
|
|
@ -137,10 +137,10 @@ namespace Input {
|
||||||
|
|
||||||
key = mouse_event;
|
key = mouse_event;
|
||||||
|
|
||||||
if (not Menu::active and key == "mouse_click") {
|
if (key == "mouse_click") {
|
||||||
const auto& [col, line] = mouse_pos;
|
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) {
|
if (col >= pos.col and col < pos.col + pos.width and line >= pos.line and line < pos.line + pos.height) {
|
||||||
key = mapped_key;
|
key = mapped_key;
|
||||||
break;
|
break;
|
||||||
|
@ -235,8 +235,10 @@ namespace Input {
|
||||||
Proc::filter = { Config::getS("proc_filter") };
|
Proc::filter = { Config::getS("proc_filter") };
|
||||||
old_filter = Proc::filter.text;
|
old_filter = Proc::filter.text;
|
||||||
}
|
}
|
||||||
else if (key == "e")
|
else if (key == "e") {
|
||||||
Config::flip("proc_tree");
|
Config::flip("proc_tree");
|
||||||
|
no_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
else if (key == "r")
|
else if (key == "r")
|
||||||
Config::flip("proc_reversed");
|
Config::flip("proc_reversed");
|
||||||
|
@ -313,6 +315,7 @@ namespace Input {
|
||||||
auto& pid = Config::getI("selected_pid");
|
auto& pid = Config::getI("selected_pid");
|
||||||
if (key == "+" or key == "space") Proc::expand = pid;
|
if (key == "+" or key == "space") Proc::expand = pid;
|
||||||
if (key == "-" or key == "space") Proc::collapse = pid;
|
if (key == "-" or key == "space") Proc::collapse = pid;
|
||||||
|
no_update = false;
|
||||||
}
|
}
|
||||||
else if (key == "t") {
|
else if (key == "t") {
|
||||||
Logger::debug(key);
|
Logger::debug(key);
|
||||||
|
|
|
@ -75,6 +75,10 @@ namespace Cpu {
|
||||||
unordered_flat_map<int, int> core_mapping;
|
unordered_flat_map<int, int> core_mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
uint64_t timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Shared {
|
namespace Shared {
|
||||||
|
|
||||||
fs::path procPath, passwd_path;
|
fs::path procPath, passwd_path;
|
||||||
|
@ -137,6 +141,7 @@ namespace Shared {
|
||||||
Mem::collect();
|
Mem::collect();
|
||||||
|
|
||||||
//? Init for namespace Net
|
//? Init for namespace Net
|
||||||
|
Net::timestamp = time_ms();
|
||||||
Net::collect();
|
Net::collect();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -813,6 +818,7 @@ namespace Net {
|
||||||
auto& config_iface = Config::getS("net_iface");
|
auto& config_iface = Config::getS("net_iface");
|
||||||
auto& net_sync = Config::getB("net_sync");
|
auto& net_sync = Config::getB("net_sync");
|
||||||
auto& net_auto = Config::getB("net_auto");
|
auto& net_auto = Config::getB("net_auto");
|
||||||
|
auto new_timestamp = time_ms();
|
||||||
|
|
||||||
if (not no_update and errors < 3) {
|
if (not no_update and errors < 3) {
|
||||||
//? Get interface list using getifaddrs() wrapper
|
//? 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);
|
const uint64_t val = max(stoul(readfile(sys_file, "0")), saved_stat.last);
|
||||||
|
|
||||||
//? Update speed, total and top values
|
//? 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.speed > saved_stat.top) saved_stat.top = saved_stat.speed;
|
||||||
if (saved_stat.offset > val) saved_stat.offset = 0;
|
if (saved_stat.offset > val) saved_stat.offset = 0;
|
||||||
saved_stat.total = val - saved_stat.offset;
|
saved_stat.total = val - saved_stat.offset;
|
||||||
|
@ -899,6 +905,8 @@ namespace Net {
|
||||||
}
|
}
|
||||||
net.compact();
|
net.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timestamp = new_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Return empty net_info struct if no interfaces was found
|
//? Return empty net_info struct if no interfaces was found
|
||||||
|
@ -964,6 +972,9 @@ namespace Proc {
|
||||||
|
|
||||||
vector<proc_info> current_procs;
|
vector<proc_info> current_procs;
|
||||||
unordered_flat_map<string, string> uid_user;
|
unordered_flat_map<string, string> uid_user;
|
||||||
|
string current_sort;
|
||||||
|
string current_filter;
|
||||||
|
bool current_rev = false;
|
||||||
|
|
||||||
fs::file_time_type passwd_time;
|
fs::file_time_type passwd_time;
|
||||||
|
|
||||||
|
@ -976,17 +987,19 @@ namespace Proc {
|
||||||
detail_container detailed;
|
detail_container detailed;
|
||||||
|
|
||||||
//* Generate process tree list
|
//* 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();
|
auto cur_pos = out_procs.size();
|
||||||
bool filtering = false;
|
bool filtering = false;
|
||||||
|
|
||||||
//? If filtering, include children of matching processes
|
//? 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)
|
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.name, filter)
|
||||||
and not s_contains(cur_proc.cmd, filter)
|
and not s_contains(cur_proc.cmd, filter)
|
||||||
and not s_contains(cur_proc.user, filter)) {
|
and not s_contains(cur_proc.user, filter)) {
|
||||||
filtering = true;
|
filtering = true;
|
||||||
|
cur_proc.filtered = true;
|
||||||
|
filter_found++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
found = true;
|
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) {
|
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
|
//? 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()) {
|
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
|
||||||
std::string_view cmd_view = cur_proc.cmd;
|
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(0, min(cmd_view.find(' '), cmd_view.size()));
|
||||||
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, 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
|
//? Recursive iteration over all children
|
||||||
int children = 0;
|
int children = 0;
|
||||||
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
|
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) {
|
if (collapsed and not filtering) {
|
||||||
out_procs.back().cpu_p += p.cpu_p;
|
cur_proc.filtered = true;
|
||||||
out_procs.back().mem += p.mem;
|
|
||||||
out_procs.back().threads += p.threads;
|
|
||||||
}
|
}
|
||||||
else children++;
|
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;
|
if (collapsed or filtering) return;
|
||||||
|
|
||||||
//? Add tree terminator symbol if it's the last child in a sub-tree
|
//? 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("]─"))
|
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
|
||||||
out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ ");
|
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
|
||||||
|
|
||||||
//? Add collapse/expand symbols if process have any children
|
//? 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
|
//* Get detailed info for selected process
|
||||||
|
@ -1133,12 +1153,18 @@ namespace Proc {
|
||||||
const auto& tree = Config::getB("proc_tree");
|
const auto& tree = Config::getB("proc_tree");
|
||||||
const auto& show_detailed = Config::getB("show_detailed");
|
const auto& show_detailed = Config::getB("show_detailed");
|
||||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
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;
|
ifstream pread;
|
||||||
string long_string;
|
string long_string;
|
||||||
string short_str;
|
string short_str;
|
||||||
filter_found = 0;
|
|
||||||
const double uptime = system_uptime();
|
|
||||||
|
|
||||||
|
const double uptime = system_uptime();
|
||||||
|
|
||||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||||
bool got_detailed = false;
|
bool got_detailed = false;
|
||||||
|
@ -1193,7 +1219,7 @@ namespace Proc {
|
||||||
const size_t pid = stoul(pid_str);
|
const size_t pid = stoul(pid_str);
|
||||||
found.push_back(pid);
|
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);
|
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||||
bool no_cache = false;
|
bool no_cache = false;
|
||||||
if (find_old == current_procs.end()) {
|
if (find_old == current_procs.end()) {
|
||||||
|
@ -1210,6 +1236,7 @@ namespace Proc {
|
||||||
if (not pread.good()) continue;
|
if (not pread.good()) continue;
|
||||||
getline(pread, new_proc.name);
|
getline(pread, new_proc.name);
|
||||||
pread.close();
|
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, ' ');
|
new_proc.name_offset = rng::count(new_proc.name, ' ');
|
||||||
|
|
||||||
pread.open(d.path() / "cmdline");
|
pread.open(d.path() / "cmdline");
|
||||||
|
@ -1241,7 +1268,6 @@ namespace Proc {
|
||||||
pread.open(d.path() / "stat");
|
pread.open(d.path() / "stat");
|
||||||
if (not pread.good()) continue;
|
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;
|
const auto& offset = new_proc.name_offset;
|
||||||
short_str.clear();
|
short_str.clear();
|
||||||
size_t x = 0, next_x = 3;
|
size_t x = 0, next_x = 3;
|
||||||
|
@ -1318,19 +1344,7 @@ namespace Proc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// //? Clear dead processes from cache at a regular interval
|
//? Clear dead processes from current_procs
|
||||||
// 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
|
|
||||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element){ return not v_contains(found, element.pid); });
|
auto eraser = rng::remove_if(current_procs, [&](const auto& element){ return not v_contains(found, element.pid); });
|
||||||
current_procs.erase(eraser.begin(), eraser.end());
|
current_procs.erase(eraser.begin(), eraser.end());
|
||||||
|
|
||||||
|
@ -1343,14 +1357,12 @@ namespace Proc {
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
old_cputimes = cputimes;
|
old_cputimes = cputimes;
|
||||||
// current_procs.clear();
|
|
||||||
// current_procs = procs;
|
|
||||||
}
|
}
|
||||||
//* ---------------------------------------------Collection done-----------------------------------------------
|
//* ---------------------------------------------Collection done-----------------------------------------------
|
||||||
|
|
||||||
//* Sort processes
|
//* Sort processes
|
||||||
|
if (sorted_change or not no_update) {
|
||||||
switch (v_index(sort_vector, sorting)) {
|
switch (v_index(sort_vector, sorting)) {
|
||||||
case 0: rng::sort(current_procs, rng::greater{}, &proc_info::pid); break;
|
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 1: rng::sort(current_procs, rng::greater{}, &proc_info::name); break;
|
||||||
|
@ -1379,8 +1391,12 @@ namespace Proc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//* Match filter if defined
|
//* Match filter if defined
|
||||||
|
if (should_filter) {
|
||||||
|
filter_found = 0;
|
||||||
for (auto& p : current_procs) {
|
for (auto& p : current_procs) {
|
||||||
if (not tree and not filter.empty()) {
|
if (not tree and not filter.empty()) {
|
||||||
if (not s_contains(to_string(p.pid), filter)
|
if (not s_contains(to_string(p.pid), filter)
|
||||||
|
@ -1390,50 +1406,53 @@ namespace Proc {
|
||||||
p.filtered = true;
|
p.filtered = true;
|
||||||
filter_found++;
|
filter_found++;
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else if (not tree) {
|
|
||||||
p.filtered = false;
|
p.filtered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
p.filtered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//* Generate tree view if enabled
|
//* 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) {
|
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
|
||||||
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
|
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
|
||||||
if (collapser != current_procs.end()) {
|
if (collapser != current_procs.end()) {
|
||||||
if (collapse == expand) {
|
if (collapse == expand) {
|
||||||
collapser->collapsed = not collapser->collapsed;
|
collapser->collapsed = not collapser->collapsed;
|
||||||
collapse = expand = -1;
|
|
||||||
}
|
}
|
||||||
else if (collapse > -1) {
|
else if (collapse > -1) {
|
||||||
collapser->collapsed = true;
|
collapser->collapsed = true;
|
||||||
collapse = -1;
|
|
||||||
}
|
}
|
||||||
else if (expand > -1) {
|
else if (expand > -1) {
|
||||||
collapser->collapsed = false;
|
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());
|
tree_procs.reserve(current_procs.size());
|
||||||
|
|
||||||
//? Stable sort to retain selected sorting among processes with the same parent
|
//? Stable sort to retain selected sorting among processes with the same parent
|
||||||
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
|
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
|
||||||
|
|
||||||
//? Start recursive iteration over processes with the lowest shared parent pids
|
//? 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)) {
|
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);
|
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
//procs.clear();
|
//? Final sort based on tree index
|
||||||
current_procs = std::move(tree_procs);
|
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;
|
return current_procs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ namespace Menu {
|
||||||
atomic<bool> active (false);
|
atomic<bool> active (false);
|
||||||
string output;
|
string output;
|
||||||
|
|
||||||
|
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||||
|
|
||||||
const unordered_flat_map<string, unordered_flat_map<string, vector<string>>> menus = {
|
const unordered_flat_map<string, unordered_flat_map<string, vector<string>>> menus = {
|
||||||
{ "options", {
|
{ "options", {
|
||||||
{ "normal", {
|
{ "normal", {
|
||||||
|
|
|
@ -21,6 +21,8 @@ tab-size = 4
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <btop_input.hpp>
|
||||||
|
|
||||||
using std::string, std::atomic;
|
using std::string, std::atomic;
|
||||||
|
|
||||||
namespace Menu {
|
namespace Menu {
|
||||||
|
@ -28,5 +30,7 @@ namespace Menu {
|
||||||
extern atomic<bool> active;
|
extern atomic<bool> active;
|
||||||
extern string output;
|
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';
|
char state = '0';
|
||||||
uint64_t cpu_n = 0, p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
uint64_t cpu_n = 0, p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
||||||
string prefix = "";
|
string prefix = "";
|
||||||
size_t depth = 0;
|
size_t depth = 0, tree_index = 0;
|
||||||
bool collapsed = false, filtered = false;
|
bool collapsed = false, filtered = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ tab-size = 4
|
||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ namespace Term {
|
||||||
void restore() {
|
void restore() {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings);
|
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;
|
initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,6 +168,15 @@ namespace Tools {
|
||||||
return str;
|
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 ltrim(const string& str, const string& t_str) {
|
||||||
string_view str_v = str;
|
string_view str_v = str;
|
||||||
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
|
while (str_v.starts_with(t_str)) str_v.remove_prefix(t_str.size());
|
||||||
|
@ -310,16 +320,6 @@ namespace Tools {
|
||||||
atomic_notify(this->atom);
|
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) {
|
string readfile(const std::filesystem::path& path, const string& fallback) {
|
||||||
if (not fs::exists(path)) return fallback;
|
if (not fs::exists(path)) return fallback;
|
||||||
string out;
|
string out;
|
||||||
|
@ -345,6 +345,18 @@ namespace Tools {
|
||||||
return {0, ""};
|
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 {
|
namespace Logger {
|
||||||
|
|
|
@ -144,6 +144,9 @@ namespace Tools {
|
||||||
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
//* 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);
|
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>
|
//* Capatilize <str>
|
||||||
inline string capitalize(string str) {
|
inline string capitalize(string str) {
|
||||||
str.at(0) = toupper(str.at(0));
|
str.at(0) = toupper(str.at(0));
|
||||||
|
@ -263,8 +266,11 @@ namespace Tools {
|
||||||
//* Return current time in <strf> format
|
//* Return current time in <strf> format
|
||||||
string strf_time(const string& strf);
|
string strf_time(const string& strf);
|
||||||
|
|
||||||
|
string hostname();
|
||||||
|
string username();
|
||||||
|
|
||||||
#if __GNUC__ < 11
|
#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; }
|
inline void atomic_notify(const atomic<bool>& atom) noexcept { (void)atom; }
|
||||||
#else
|
#else
|
||||||
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { atom.wait(old); }
|
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept { atom.wait(old); }
|
||||||
|
@ -280,15 +286,6 @@ namespace Tools {
|
||||||
~atomic_lock();
|
~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
|
//* Read a complete file and return as a string
|
||||||
string readfile(const std::filesystem::path& path, const string& fallback="");
|
string readfile(const std::filesystem::path& path, const string& fallback="");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue