mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 06:11:28 +02:00
Added menus and boxes for signal sending
This commit is contained in:
parent
5dcc1b7829
commit
db96a20e16
125
Makefile
125
Makefile
@ -1,28 +1,17 @@
|
||||
#* 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╚═════╝ ╚═╝ ╚═════╝ ╚═╝ \033[1;3;38;5;240mMakefile v1.0\033[0m
|
||||
BANNER = \n \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m████████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗ \033[38;5;196m██████\033[38;5;240m╗\n \033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗╚══\033[38;5;160m██\033[38;5;239m╔══╝\033[38;5;160m██\033[38;5;239m╔═══\033[38;5;160m██\033[38;5;239m╗\033[38;5;160m██\033[38;5;239m╔══\033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗ \033[38;5;160m██\033[38;5;239m╗\n \033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║ \033[38;5;124m██\033[38;5;238m║\033[38;5;124m██████\033[38;5;238m╔╝ \033[38;5;124m██████\033[38;5;238m╗\033[38;5;124m██████\033[38;5;238m╗\n \033[38;5;88m██\033[38;5;237m╔══\033[38;5;88m██\033[38;5;237m╗ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║ \033[38;5;88m██\033[38;5;237m║\033[38;5;88m██\033[38;5;237m╔═══╝ ╚═\033[38;5;88m██\033[38;5;237m╔═╝╚═\033[38;5;88m██\033[38;5;237m╔═╝\n \033[38;5;52m██████\033[38;5;236m╔╝ \033[38;5;52m██\033[38;5;236m║ ╚\033[38;5;52m██████\033[38;5;236m╔╝\033[38;5;52m██\033[38;5;236m║ ╚═╝ ╚═╝\n \033[38;5;235m╚═════╝ ╚═╝ ╚═════╝ ╚═╝ \033[1;3;38;5;240mMakefile v1.2\033[0m
|
||||
|
||||
BTOP_VERSION := $(shell head -n100 src/btop.cpp 2>/dev/null | grep "Version =" | cut -f2 -d"\"" || echo " unknown")
|
||||
TIMESTAMP := $(shell date +%s 2>/dev/null || echo "0")
|
||||
override BTOP_VERSION := $(shell head -n100 src/btop.cpp 2>/dev/null | grep "Version =" | cut -f2 -d"\"" || echo " unknown")
|
||||
override TIMESTAMP := $(shell date +%s 2>/dev/null || echo "0")
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
#? 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
|
||||
ifneq ($(CXX),g++-10)
|
||||
V_MAJOR = $(shell echo $(CXX_VERSION) | cut -f1 -d".")
|
||||
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
|
||||
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
||||
override CXX = g++-11
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
#? NOTICE! Manually set PLATFORM and ARCH if not compiling for host system
|
||||
PLATFORM ?= $(shell uname -s || echo unknown)
|
||||
ARCH ?= $(shell uname -p || echo unknown)
|
||||
|
||||
#? Only enable fcf-protection if on x86_64
|
||||
ARCH := $(shell uname -p || echo unknown)
|
||||
ifeq ($(ARCH),unknown)
|
||||
ARCH := $(shell uname -m || echo unknown)
|
||||
endif
|
||||
@ -30,11 +19,34 @@ ifeq ($(ARCH),x86_64)
|
||||
ADDFLAGS = -fcf-protection
|
||||
endif
|
||||
|
||||
#? Manually set this to (Linux|FreeBSD|Darwin) if not building for host platform
|
||||
PLATFORM ?= $(shell uname -s || echo unknown)
|
||||
#? Make sure PLATFORM Darwin is OSX and not Darwin
|
||||
ifeq ($(PLATFORM),Darwin)
|
||||
ifeq ($(shell sw_vers >/dev/null 2>&1; echo $$?),0)
|
||||
PLATFORM = OSX
|
||||
endif
|
||||
endif
|
||||
|
||||
#? Compiler and Linker
|
||||
CXX ?= g++
|
||||
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
|
||||
|
||||
#? Try to make sure we are using GCC/G++ version 11 or later if not instructed to use g++-10
|
||||
ifneq ($(CXX),g++-10)
|
||||
V_MAJOR = $(shell echo $(CXX_VERSION) | cut -f1 -d".")
|
||||
ifneq ($(shell test $(V_MAJOR) -ge 11; echo $$?),0)
|
||||
ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
|
||||
override CXX = g++-11
|
||||
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#? Use all CPU cores (will only be set if using Make 4.3+)
|
||||
MAKEFLAGS := --jobs=$(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||
THREADS := $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||
MAKEFLAGS := --jobs=$(THREADS)
|
||||
ifeq ($(THREADS),1)
|
||||
override THREADS := auto
|
||||
endif
|
||||
|
||||
#? The Directories, Source, Includes, Objects and Binary
|
||||
SRCDIR := src
|
||||
@ -46,10 +58,10 @@ DEPEXT := d
|
||||
OBJEXT := o
|
||||
|
||||
#? Flags, Libraries and Includes
|
||||
REQFLAGS := -std=c++20
|
||||
WARNFLAGS := -Wall -Wextra -Wno-stringop-overread -pedantic -pedantic-errors -Wfatal-errors
|
||||
OPTFLAGS := -O2 -ftree-loop-vectorize
|
||||
override LDCXXFLAGS += -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector -fstack-clash-protection -flto $(ADDFLAGS)
|
||||
override REQFLAGS := -std=c++20
|
||||
WARNFLAGS := -Wall -Wextra -pedantic -pedantic-errors -Wfatal-errors
|
||||
OPTFLAGS ?= -O2 -ftree-loop-vectorize -flto=$(THREADS)
|
||||
LDCXXFLAGS := -pthread -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector -fstack-clash-protection $(ADDFLAGS)
|
||||
override CXXFLAGS += $(REQFLAGS) $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||
override LDFLAGS += $(LDCXXFLAGS) $(OPTFLAGS) $(WARNFLAGS)
|
||||
INC := -I$(INCDIR) -I$(SRCDIR)
|
||||
@ -58,29 +70,38 @@ SU_GROUP := root
|
||||
|
||||
SOURCES := $(shell find $(SRCDIR) -maxdepth 1 -type f -name *.$(SRCEXT))
|
||||
|
||||
#? Pull in platform specific source files
|
||||
ifeq ($(PLATFORM),Linux)
|
||||
SOURCES += $(shell find $(SRCDIR)/linux -type f -name *.$(SRCEXT))
|
||||
endif
|
||||
ifeq ($(PLATFORM),FreeBSD)
|
||||
SOURCES += $(shell find $(SRCDIR)/freebsd -type f -name *.$(SRCEXT))
|
||||
endif
|
||||
ifeq ($(PLATFORM),Darwin)
|
||||
SOURCES += $(shell find $(SRCDIR)/osx -type f -name *.$(SRCEXT))
|
||||
PLATFORM_DIR = linux
|
||||
else ifeq ($(PLATFORM),FreeBSD)
|
||||
PLATFORM_DIR = freebsd
|
||||
else ifeq ($(PLATFORM),OSX)
|
||||
PLATFORM_DIR = osx
|
||||
else
|
||||
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
|
||||
endif
|
||||
|
||||
SOURCES += $(shell find $(SRCDIR)/$(PLATFORM_DIR) -type f -name *.$(SRCEXT))
|
||||
|
||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||
|
||||
#? Default Make
|
||||
all: msg directories btop
|
||||
all: pre directories btop
|
||||
|
||||
msg:
|
||||
pre:
|
||||
@printf " $(BANNER)\n"
|
||||
@printf "\033[1;97mCXX : \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 \033[1;91mbtop++ \033[1;93mv$(BTOP_VERSION) \033[0;37mfor \033[1;97m$(PLATFORM) \033[1;96m($(ARCH))\033[0m\n"
|
||||
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
|
||||
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
|
||||
@printf "\033[1;93mCXX \033[1;93m?| \033[0m$(CXX) \033[1;93m(\033[97m$(CXX_VERSION)\033[93m)\n"
|
||||
@printf "\033[1;94mTHREADS \033[1;94m:| \033[0m$(THREADS)\n"
|
||||
@printf "\033[1;92mREQFLAGS \033[1;91m!| \033[0m$(REQFLAGS)\n"
|
||||
@printf "\033[1;91mWARNFLAGS \033[1;94m:| \033[0m$(WARNFLAGS)\n"
|
||||
@printf "\033[1;94mOPTFLAGS \033[1;94m:| \033[0m$(OPTFLAGS)\n"
|
||||
@printf "\033[1;93mLDCXXFLAGS \033[1;94m:| \033[0m$(LDCXXFLAGS)\n"
|
||||
@printf "\033[1;95mCXXFLAGS \033[1;92m+| \033[0;37m\$$(\033[92mREQFLAGS\033[37m) \$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m)\n"
|
||||
@printf "\033[1;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m)\n"
|
||||
|
||||
@printf "\n\033[1;92mBuilding btop++ \033[93m(\033[97mv$(BTOP_VERSION)\033[93m)\033[0m\n"
|
||||
|
||||
help:
|
||||
@printf "\033[1;97mbtop++ makefile\033[0m\n"
|
||||
@ -96,10 +117,7 @@ help:
|
||||
#? Make the Directories
|
||||
directories:
|
||||
@mkdir -p $(TARGETDIR)
|
||||
@mkdir -p $(BUILDDIR)
|
||||
@mkdir -p $(BUILDDIR)/linux
|
||||
@mkdir -p $(BUILDDIR)/freebsd
|
||||
@mkdir -p $(BUILDDIR)/osx
|
||||
@mkdir -p $(BUILDDIR)/$(PLATFORM_DIR)
|
||||
|
||||
#? Clean only Objects
|
||||
clean:
|
||||
@ -122,7 +140,7 @@ install:
|
||||
@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 for $SU_USER in SU_GROUP, will make btop run with (root by default) privileges regardless of actual user
|
||||
#? Set SUID bit for btop as $SU_USER in $SU_GROUP
|
||||
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"
|
||||
@ -140,23 +158,28 @@ uninstall:
|
||||
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
|
||||
|
||||
#? Link
|
||||
.ONESHELL:
|
||||
btop: $(OBJECTS)
|
||||
@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 2>/dev/null || echo "0") - $(TIMESTAMP)) -u +%Mm:%Ss)\033[1;92m)\033[0m\n"
|
||||
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
||||
@printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
|
||||
@$(CXX) -o $(TARGETDIR)/btop $^ $(LDFLAGS) || exit 1
|
||||
@printf "\033[1;92m-> \033[1;37m$(TARGETDIR)/btop \033[100D\033[35C\033[1;93m(\033[1;97m$$(du -ah $(TARGETDIR)/btop | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$(date -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $${TSTAMP} 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo '')\033[92m)\033[0m\n"
|
||||
@printf "\n\033[1;92mBuild complete in \033[92m(\033[97m$$(date -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $(TIMESTAMP) 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo "unknown")\033[92m)\033[0m\n"
|
||||
|
||||
#? Compile
|
||||
.ONESHELL:
|
||||
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
||||
@sleep 0.1 2>/dev/null || true
|
||||
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
|
||||
@printf "\033[1;97mCompiling $<\033[0m\n"
|
||||
@$(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
|
||||
@$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) >/dev/null
|
||||
@$(CXX) $(CXXFLAGS) $(INC) -c -o $@ $< || exit 1
|
||||
@$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT) >/dev/null || exit 1
|
||||
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||
@sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
|
||||
@sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
|
||||
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||
@printf "\033[1;92m-> \033[1;37m$@ \033[100D\033[35C\033[1;93m(\033[1;97m$$(du -ah $@ | cut -f1)iB\033[1;93m) \033[92m(\033[97m$$(date -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $${TSTAMP} 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo '')\033[92m)\033[0m\n"
|
||||
|
||||
#? Non-File Targets
|
||||
.PHONY: all msg help
|
||||
.PHONY: all msg help pre
|
||||
|
59
src/btop.cpp
59
src/btop.cpp
@ -29,6 +29,7 @@ tab-size = 4
|
||||
#include <exception>
|
||||
#include <tuple>
|
||||
#include <regex>
|
||||
#include <chrono>
|
||||
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
@ -43,6 +44,7 @@ using std::string_literals::operator""s, std::to_string, std::future, std::async
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
using namespace Tools;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace Global {
|
||||
const vector<array<string, 2>> Banner_src = {
|
||||
@ -140,12 +142,13 @@ void term_resize(bool force) {
|
||||
|
||||
Global::resized = true;
|
||||
Runner::stop();
|
||||
auto min_size = Term::get_min_size(Config::getS("shown_boxes"));
|
||||
|
||||
while (not force) {
|
||||
auto boxes = Config::getS("shown_boxes");
|
||||
auto min_size = Term::get_min_size(boxes);
|
||||
|
||||
while (not force or (Term::width < min_size.at(0) or Term::height < min_size.at(1))) {
|
||||
sleep_ms(100);
|
||||
if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
|
||||
min_size = Term::get_min_size(Config::getS("shown_boxes"));
|
||||
cout << Term::clear << Global::bg_black << Global::fg_white << Mv::to((Term::height / 2) - 2, (Term::width / 2) - 11)
|
||||
<< "Terminal size too small:" << Mv::to((Term::height / 2) - 1, (Term::width / 2) - 10)
|
||||
<< " Width = " << (Term::width < min_size.at(1) ? Global::fg_red : Global::fg_green) << Term::width
|
||||
@ -155,6 +158,7 @@ void term_resize(bool force) {
|
||||
<< "Width = " << min_size.at(0) << " Height = " << min_size.at(1) << flush;
|
||||
while (not Term::refresh() and not Input::poll()) sleep_ms(10);
|
||||
if (Input::poll() and Input::get() == "q") exit(0);
|
||||
min_size = Term::get_min_size(boxes);
|
||||
}
|
||||
else if (not Term::refresh()) break;
|
||||
}
|
||||
@ -167,8 +171,9 @@ void clean_quit(int sig) {
|
||||
if (Global::quitting) return;
|
||||
Global::quitting = true;
|
||||
Runner::stop();
|
||||
if (pthread_join(Runner::runner_id, NULL) != 0)
|
||||
if (pthread_join(Runner::runner_id, NULL) != 0) {
|
||||
Logger::error("Failed to join _runner thread!");
|
||||
}
|
||||
|
||||
Config::write();
|
||||
Input::clear();
|
||||
@ -257,7 +262,6 @@ void banner_gen() {
|
||||
else
|
||||
letter = line[1].substr(i, 3);
|
||||
|
||||
// if (tty_mode and letter != "█" and letter != " ") letter = "░";
|
||||
b_color = (letter == "█") ? fg : bg;
|
||||
if (b_color != oc) Global::banner += b_color;
|
||||
Global::banner += letter;
|
||||
@ -266,7 +270,8 @@ void banner_gen() {
|
||||
if (++z < Global::Banner_src.size()) Global::banner += Mv::l(ulen(line[1])) + Mv::d(1);
|
||||
}
|
||||
Global::banner += Mv::r(18 - Global::Version.size())
|
||||
+ (tty_mode ? "\x1b[0;40;37m" : Theme::dec_to_color(0,0,0, lowcolor, "bg") + Theme::dec_to_color(150, 150, 150, lowcolor))
|
||||
+ (tty_mode ? "\x1b[0;40;37m" : Theme::dec_to_color(0,0,0, lowcolor, "bg")
|
||||
+ Theme::dec_to_color(150, 150, 150, lowcolor))
|
||||
+ Fx::i + "v" + Global::Version + Fx::ui;
|
||||
}
|
||||
|
||||
@ -281,11 +286,17 @@ bool update_clock() {
|
||||
};
|
||||
static time_t c_time = 0;
|
||||
static size_t clock_len = 0;
|
||||
static string old_clock;
|
||||
string new_clock;
|
||||
|
||||
if (auto n_time = time(NULL); n_time == c_time)
|
||||
return false;
|
||||
else
|
||||
else {
|
||||
c_time = n_time;
|
||||
new_clock = Tools::strf_time(clock_format);
|
||||
if (new_clock == old_clock) return false;
|
||||
old_clock = new_clock;
|
||||
}
|
||||
|
||||
auto& out = Global::clock;
|
||||
const auto& cpu_bottom = Config::getB("cpu_bottom");
|
||||
@ -294,7 +305,7 @@ bool update_clock() {
|
||||
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)) {
|
||||
@ -310,11 +321,11 @@ bool update_clock() {
|
||||
|
||||
}
|
||||
|
||||
new_clock = uresize(Tools::strf_time(new_clock), std::max(0, width - 56));
|
||||
new_clock = uresize(new_clock, std::max(0, width - 56));
|
||||
out.clear();
|
||||
|
||||
if (new_clock.size() != clock_len) {
|
||||
if (not Global::resized) out = Mv::to(y, x+(width / 2)-(clock_len / 2)) + Fx::ub + Theme::c("cpu_box") + Symbols::h_line * clock_len;
|
||||
if (not Global::resized and clock_len > 0) 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();
|
||||
}
|
||||
|
||||
@ -483,7 +494,7 @@ namespace Runner {
|
||||
else if (not proc.valid())
|
||||
throw std::runtime_error("Proc::collect() future not valid.");
|
||||
|
||||
else if (proc.wait_for(std::chrono::microseconds(10)) == future_status::ready) {
|
||||
else if (proc.wait_for(10us) == future_status::ready) {
|
||||
try {
|
||||
if (Global::debug) debug_timer("proc", draw_begin);
|
||||
|
||||
@ -511,7 +522,7 @@ namespace Runner {
|
||||
else if (not net.valid())
|
||||
throw std::runtime_error("Net::collect() future not valid.");
|
||||
|
||||
else if (net.wait_for(ZeroSec) == future_status::ready) {
|
||||
else if (net.wait_for(10us) == future_status::ready) {
|
||||
try {
|
||||
if (Global::debug) debug_timer("net", draw_begin);
|
||||
|
||||
@ -539,7 +550,7 @@ namespace Runner {
|
||||
else if (not mem.valid())
|
||||
throw std::runtime_error("Mem::collect() future not valid.");
|
||||
|
||||
else if (mem.wait_for(ZeroSec) == future_status::ready) {
|
||||
else if (mem.wait_for(10us) == future_status::ready) {
|
||||
try {
|
||||
if (Global::debug) debug_timer("mem", draw_begin);
|
||||
|
||||
@ -567,7 +578,7 @@ namespace Runner {
|
||||
else if (not cpu.valid())
|
||||
throw std::runtime_error("Cpu::collect() future not valid.");
|
||||
|
||||
else if (cpu.wait_for(ZeroSec) == future_status::ready) {
|
||||
else if (cpu.wait_for(10us) == future_status::ready) {
|
||||
try {
|
||||
if (Global::debug) debug_timer("cpu", draw_begin);
|
||||
|
||||
@ -696,7 +707,11 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not Config::conf_dir.empty()) {
|
||||
if (Config::conf_dir.empty()) {
|
||||
cout << "WARNING: Could not get path user HOME folder.\n"
|
||||
<< "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this." << endl;
|
||||
}
|
||||
else {
|
||||
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
|
||||
cout << "WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n"
|
||||
<< "Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this." << endl;
|
||||
@ -741,7 +756,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
else Logger::set(Config::getS("log_level"));
|
||||
|
||||
Logger::info("Logger set to " + Config::getS("log_level"));
|
||||
Logger::info("Logger set to " + (Global::debug ? "DEBUG" : Config::getS("log_level")));
|
||||
|
||||
for (const auto& err_str : load_warnings) Logger::warning(err_str);
|
||||
}
|
||||
@ -788,7 +803,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) {
|
||||
Config::set("tty_mode", true);
|
||||
Logger::info("Real tty detected, setting 16 color mode and using tty friendly graph symbols");
|
||||
Logger::info("Real tty detected: setting 16 color mode and using tty friendly graph symbols");
|
||||
}
|
||||
|
||||
//? Platform dependent init and error check
|
||||
@ -854,7 +869,8 @@ int main(int argc, char **argv) {
|
||||
Draw::calcSizes();
|
||||
update_clock();
|
||||
Global::resized = false;
|
||||
Runner::run("all", true);
|
||||
if (Menu::active) Menu::process();
|
||||
else Runner::run("all", true, true);
|
||||
atomic_wait(Runner::active);
|
||||
}
|
||||
|
||||
@ -864,7 +880,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
//? Start secondary collect & draw thread at the interval set by <update_ms> config value
|
||||
if (time_ms() >= future_time) {
|
||||
if (time_ms() >= future_time and not Global::resized) {
|
||||
Runner::run("all");
|
||||
update_ms = Config::getI("update_ms");
|
||||
future_time = time_ms() + update_ms;
|
||||
@ -884,11 +900,14 @@ 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();
|
||||
Input::process(Input::get());
|
||||
|
||||
if (Menu::active) Menu::process(Input::get());
|
||||
else Input::process(Input::get());
|
||||
}
|
||||
|
||||
//? Break the loop at 1000ms intervals or if input polling was interrupted
|
||||
else break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,12 +20,13 @@ tab-size = 4
|
||||
#include <ranges>
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
|
||||
using std::array, std::atomic;
|
||||
using std::array, std::atomic, std::string_view;
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
using namespace Tools;
|
||||
@ -114,7 +115,8 @@ namespace Config {
|
||||
|
||||
{"show_cpu_freq", "#* Show CPU frequency."},
|
||||
|
||||
{"clock_format", "#* 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.\n"
|
||||
"#* Special formatting: /host = hostname | /user = username | /uptime = system uptime"},
|
||||
|
||||
{"background_update", "#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort."},
|
||||
|
||||
@ -184,6 +186,7 @@ namespace Config {
|
||||
{"log_level", "WARNING"},
|
||||
{"proc_filter", ""},
|
||||
{"proc_command", ""},
|
||||
{"selected_name", ""},
|
||||
};
|
||||
unordered_flat_map<string, string> stringsTmp;
|
||||
|
||||
@ -272,6 +275,7 @@ namespace Config {
|
||||
try {
|
||||
if (Proc::shown) {
|
||||
ints.at("selected_pid") = Proc::selected_pid;
|
||||
strings.at("selected_name") = Proc::selected_name;
|
||||
ints.at("proc_start") = Proc::start;
|
||||
ints.at("proc_selected") = Proc::selected;
|
||||
}
|
||||
@ -337,7 +341,7 @@ namespace Config {
|
||||
valid_names.push_back(n[0]);
|
||||
string v_string;
|
||||
getline(cread, v_string, '\n');
|
||||
if (not v_string.ends_with(Global::Version))
|
||||
if (not s_contains(v_string, Global::Version))
|
||||
write_new = true;
|
||||
while (not cread.eof()) {
|
||||
cread >> std::ws;
|
||||
|
@ -27,6 +27,7 @@ tab-size = 4
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_input.hpp>
|
||||
#include <btop_menu.hpp>
|
||||
|
||||
|
||||
using std::round, std::views::iota, std::string_literals::operator""s, std::clamp, std::array, std::floor, std::max, std::min,
|
||||
@ -217,15 +218,13 @@ namespace Draw {
|
||||
//* Meter class ------------------------------------------------------------------------------------------------------------>
|
||||
Meter::Meter() {}
|
||||
|
||||
Meter::Meter(const int width, const string& color_gradient, const bool invert) : width(width), color_gradient(color_gradient), invert(invert) {
|
||||
cache.insert(cache.begin(), 101, "");
|
||||
}
|
||||
Meter::Meter(const int width, const string& color_gradient, const bool invert) : width(width), color_gradient(color_gradient), invert(invert) {}
|
||||
|
||||
string Meter::operator()(int value) {
|
||||
if (width < 1) return "";
|
||||
value = clamp(value, 0, 100);
|
||||
if (not cache.at(value).empty()) return cache.at(value);
|
||||
string& out = cache.at(value);
|
||||
auto& out = cache.at(value);
|
||||
for (const int& i : iota(1, width + 1)) {
|
||||
int y = round((double)i * 100.0 / width);
|
||||
if (value >= y)
|
||||
@ -292,11 +291,11 @@ namespace Draw {
|
||||
out += graphs.at(current).at(0);
|
||||
}
|
||||
else {
|
||||
for (const int& i : iota(0, height)) {
|
||||
if (i > 0) out += Mv::d(1) + Mv::l(width);
|
||||
for (const int& i : iota(1, height + 1)) {
|
||||
if (i > 1) out += Mv::d(1) + Mv::l(width);
|
||||
if (not color_gradient.empty())
|
||||
out += (invert) ? Theme::g(color_gradient).at(i * 100 / (height - 1)) : Theme::g(color_gradient).at(100 - (i * 100 / (height - 1)));
|
||||
out += (invert) ? graphs.at(current).at((height - 1) - i) : graphs.at(current).at(i);
|
||||
out += (invert) ? Theme::g(color_gradient).at((i - 1) * 100 / (height - 1)) : Theme::g(color_gradient).at(100 - (i * 100 / height));
|
||||
out += (invert) ? graphs.at(current).at(height - i) : graphs.at(current).at(i-1);
|
||||
}
|
||||
}
|
||||
if (not color_gradient.empty()) out += Fx::reset;
|
||||
@ -520,6 +519,7 @@ namespace Mem {
|
||||
int x = 1, y, width = 20, height;
|
||||
int mem_width, disks_width, divider, item_height, mem_size, mem_meter, graph_height, disk_meter;
|
||||
int disks_io_h = 0;
|
||||
int disks_io_half = 0;
|
||||
bool shown = true, redraw = true;
|
||||
string box;
|
||||
unordered_flat_map<string, Draw::Meter> mem_meters;
|
||||
@ -573,13 +573,12 @@ namespace Mem {
|
||||
//? Disk meters and io graphs
|
||||
if (show_disks) {
|
||||
if (show_io_stat or io_mode) {
|
||||
if (io_mode)
|
||||
disks_io_h = max((int)floor((double)(height - 2 - disk_ios) / max(1, disk_ios)), (io_graph_combined ? 1 : 2));
|
||||
else
|
||||
disks_io_h = 1;
|
||||
int half_height = ceil((double)disks_io_h / 2);
|
||||
|
||||
unordered_flat_map<string, int> custom_speeds;
|
||||
int half_height = 0;
|
||||
if (io_mode) {
|
||||
disks_io_h = max((int)floor((double)(height - 2 - (disk_ios * 2)) / max(1, disk_ios)), (io_graph_combined ? 1 : 2));
|
||||
half_height = ceil((double)disks_io_h / 2);
|
||||
|
||||
if (not Config::getS("io_graph_speeds").empty()) {
|
||||
auto split = ssplit(Config::getS("io_graph_speeds"));
|
||||
for (const auto& entry : split) {
|
||||
@ -588,12 +587,17 @@ namespace Mem {
|
||||
custom_speeds[vals.at(0)] = std::stoi(vals.at(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [name, disk] : mem.disks) {
|
||||
if (disk.io_read.empty()) continue;
|
||||
long long speed = (custom_speeds.contains(name) ? custom_speeds.at(name) : 10) << 20;
|
||||
|
||||
io_graphs[name + "_activity"] = Draw::Graph{disks_width - 6, 1, "available", disk.io_activity, graph_symbol, false, true};
|
||||
|
||||
if (io_mode) {
|
||||
//? Create one combined graph for IO read/write if enabled
|
||||
if (not io_mode or (io_mode and io_graph_combined)) {
|
||||
long long speed = (custom_speeds.contains(name) ? custom_speeds.at(name) : 10) << 20;
|
||||
if (io_graph_combined) {
|
||||
deque<long long> combined(disk.io_read.size(), 0);
|
||||
rng::transform(disk.io_read, disk.io_write, combined.begin(), std::plus<long long>());
|
||||
io_graphs[name] = Draw::Graph{disks_width - (io_mode ? 0 : 6), disks_io_h, "available", combined, graph_symbol, false, true, speed};
|
||||
@ -604,14 +608,15 @@ namespace Mem {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (disk_meter > 0) {
|
||||
for (int i = 0; const auto& name : mem.disks_order) {
|
||||
}
|
||||
|
||||
for (int i = 0; const auto& [name, ignored] : mem.disks) {
|
||||
if (i * 2 > height - 2) break;
|
||||
disk_meters_used[name] = Draw::Meter{disk_meter, "used"};
|
||||
if (cmp_less_equal(mem.disks_order.size() * 3, height - 1))
|
||||
if (cmp_less_equal(mem.disks.size() * 3, height - 1))
|
||||
disk_meters_free[name] = Draw::Meter{disk_meter, "free"};
|
||||
}
|
||||
}
|
||||
|
||||
out += Mv::to(y, x + width - 6) + Fx::ub + Theme::c("mem_box") + Symbols::title_left + (io_mode ? Fx::b : "") + Theme::c("hi_fg")
|
||||
+ 'i' + Theme::c("title") + 'o' + Fx::ub + Theme::c("mem_box") + Symbols::title_right;
|
||||
Input::mouse_mappings["i"] = {y, x + width - 5, 1, 2};
|
||||
@ -683,6 +688,7 @@ namespace Mem {
|
||||
const string used_percent = to_string(disk.used_percent);
|
||||
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)used_percent.size() / 2)) + Theme::c("main_fg") + used_percent + '%';
|
||||
}
|
||||
out += Mv::to(y+2+cy++, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same);
|
||||
if (++cy > height - 3) break;
|
||||
if (io_graph_combined) {
|
||||
auto comb_val = disk.io_read.back() + disk.io_write.back();
|
||||
@ -721,8 +727,8 @@ namespace Mem {
|
||||
if (big_disk and not human_io.empty())
|
||||
out += Mv::to(y+1+cy, x+1+cx + round((double)disks_width / 2) - round((double)human_io.size() / 2)) + Theme::c("main_fg") + human_io;
|
||||
if (++cy > height - 3) break;
|
||||
if (show_io_stat and io_graphs.contains(mount)) {
|
||||
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " IO: " : " IO " + Mv::l(2)) + io_graphs.at(mount)({comb_val}, redraw or data_same);
|
||||
if (show_io_stat and io_graphs.contains(mount + "_activity")) {
|
||||
out += Mv::to(y+1+cy, x+1+cx) + (big_disk ? " IO% " : " IO " + Mv::l(2)) + io_graphs.at(mount + "_activity")(disk.io_activity, redraw or data_same);
|
||||
if (not big_disk) out += Mv::to(y+1+cy, x+cx) + Theme::c("main_fg") + human_io;
|
||||
if (++cy > height - 3) break;
|
||||
}
|
||||
@ -786,6 +792,8 @@ namespace Net {
|
||||
out = box;
|
||||
//? Graphs
|
||||
graphs.clear();
|
||||
if (net.bandwidth.at("download").empty() or net.bandwidth.at("upload").empty())
|
||||
return out + Fx::reset;
|
||||
graphs["download"] = Draw::Graph{width - b_width - 2, u_graph_height, "download", net.bandwidth.at("download"), graph_symbol, false, true, down_max};
|
||||
graphs["upload"] = Draw::Graph{width - b_width - 2, d_graph_height, "upload", net.bandwidth.at("upload"), graph_symbol, true, true, up_max};
|
||||
|
||||
@ -851,6 +859,7 @@ namespace Proc {
|
||||
int start, selected, select_max;
|
||||
bool shown = true, redraw = true;
|
||||
int selected_pid = 0;
|
||||
string selected_name;
|
||||
unordered_flat_map<size_t, Draw::Graph> p_graphs;
|
||||
unordered_flat_map<size_t, int> p_counters;
|
||||
int counter = 0;
|
||||
@ -990,7 +999,7 @@ namespace Proc {
|
||||
int mouse_x = d_x + 2;
|
||||
out += Mv::to(d_y, d_x + 1);
|
||||
if (width > 55) {
|
||||
out += title_left + hi_color + Fx::b + 't' + t_color + "erminate" + Fx::ub + title_right;
|
||||
out += Fx::ub + title_left + hi_color + Fx::b + 't' + t_color + "erminate" + Fx::ub + title_right;
|
||||
if (alive and selected == 0) Input::mouse_mappings["t"] = {d_y, mouse_x, 1, 9};
|
||||
mouse_x += 11;
|
||||
}
|
||||
@ -1121,7 +1130,7 @@ namespace Proc {
|
||||
string cpu_str = (alive ? to_string(detailed.entry.cpu_p) : "");
|
||||
if (alive) {
|
||||
cpu_str.resize((detailed.entry.cpu_p < 10 or detailed.entry.cpu_p >= 100 ? 3 : 4));
|
||||
cpu_str += '%' + Mv::r(1) + (dgraph_width < 20 ? "C" : "Core") + to_string(detailed.entry.cpu_n);
|
||||
cpu_str += '%';
|
||||
}
|
||||
out += Mv::to(d_y + 1, dgraph_x + 1) + Fx::ub + detailed_cpu_graph(detailed.cpu_percent, (redraw or data_same or not alive))
|
||||
+ Mv::to(d_y + 1, dgraph_x + 1) + Theme::c("title") + Fx::b + cpu_str;
|
||||
@ -1168,7 +1177,10 @@ namespace Proc {
|
||||
for (int n=0; auto& p : plist) {
|
||||
if (n++ < start or p.filtered) continue;
|
||||
bool is_selected = (lc + 1 == selected);
|
||||
if (is_selected) selected_pid = (int)p.pid;
|
||||
if (is_selected) {
|
||||
selected_pid = (int)p.pid;
|
||||
selected_name = p.name;
|
||||
}
|
||||
|
||||
//? Update graphs for processes with above 0.0% cpu usage, delete if below 0.1% 10x times
|
||||
const bool has_graph = p_counters.contains(p.pid);
|
||||
@ -1275,7 +1287,7 @@ namespace Proc {
|
||||
//? Current selection and number of processes
|
||||
string location = to_string(start + selected) + '/' + to_string(numpids);
|
||||
string loc_clear = Symbols::h_line * max(0ul, 9 - location.size());
|
||||
out += Mv::to(y + height - 1, x+width - 3 - max(9, (int)location.size())) + Theme::c("proc_box") + loc_clear
|
||||
out += Mv::to(y + height - 1, x+width - 3 - max(9, (int)location.size())) + Fx::ub + Theme::c("proc_box") + loc_clear
|
||||
+ Symbols::title_left_down + Theme::c("title") + Fx::b + location + Fx::ub + Theme::c("proc_box") + Symbols::title_right_down;
|
||||
|
||||
//? Clear out left over graphs from dead processes at a regular interval
|
||||
@ -1293,7 +1305,10 @@ namespace Proc {
|
||||
p_counters.compact();
|
||||
}
|
||||
|
||||
if (selected == 0 and selected_pid != 0) selected_pid = 0;
|
||||
if (selected == 0 and selected_pid != 0) {
|
||||
selected_pid = 0;
|
||||
selected_name.clear();
|
||||
}
|
||||
redraw = false;
|
||||
return out + Fx::reset;
|
||||
}
|
||||
@ -1303,6 +1318,7 @@ namespace Proc {
|
||||
namespace Draw {
|
||||
void calcSizes() {
|
||||
atomic_wait(Runner::active);
|
||||
Config::unlock();
|
||||
auto& boxes = Config::getS("shown_boxes");
|
||||
auto& cpu_bottom = Config::getB("cpu_bottom");
|
||||
auto& mem_below_net = Config::getB("mem_below_net");
|
||||
@ -1314,8 +1330,10 @@ namespace Draw {
|
||||
Proc::box.clear();
|
||||
Global::clock.clear();
|
||||
Global::overlay.clear();
|
||||
if (Menu::active) Menu::redraw = true;
|
||||
|
||||
Input::mouse_mappings.clear();
|
||||
Menu::mouse_mappings.clear();
|
||||
|
||||
Cpu::x = Mem::x = Net::x = Proc::x = 1;
|
||||
Cpu::y = Mem::y = Net::y = Proc::y = 1;
|
||||
|
@ -20,10 +20,11 @@ tab-size = 4
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <robin_hood.h>
|
||||
#include <deque>
|
||||
|
||||
using std::string, std::vector, robin_hood::unordered_flat_map, std::deque;
|
||||
using std::string, std::array, std::vector, robin_hood::unordered_flat_map, std::deque;
|
||||
|
||||
namespace Symbols {
|
||||
const string h_line = "─";
|
||||
@ -76,7 +77,7 @@ namespace Draw {
|
||||
int width;
|
||||
string color_gradient;
|
||||
bool invert;
|
||||
vector<string> cache;
|
||||
array<string, 101> cache;
|
||||
public:
|
||||
Meter();
|
||||
Meter(const int width, const string& color_gradient, const bool invert = false);
|
||||
|
@ -18,6 +18,7 @@ tab-size = 4
|
||||
|
||||
#include <iostream>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include <btop_input.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
@ -27,7 +28,7 @@ tab-size = 4
|
||||
#include <btop_draw.hpp>
|
||||
#include <signal.h>
|
||||
|
||||
using std::cin, std::string_literals::operator""s;
|
||||
using std::cin, std::vector, std::string_literals::operator""s;
|
||||
using namespace Tools;
|
||||
namespace rng = std::ranges;
|
||||
|
||||
@ -80,7 +81,10 @@ namespace Input {
|
||||
bool poll(int timeout) {
|
||||
if (timeout < 1) return cin.rdbuf()->in_avail() > 0;
|
||||
while (timeout > 0) {
|
||||
if (interrupt) return interrupt = false;
|
||||
if (interrupt) {
|
||||
interrupt = false;
|
||||
return false;
|
||||
}
|
||||
if (cin.rdbuf()->in_avail() > 0) return true;
|
||||
sleep_ms(timeout < 10 ? timeout : 10);
|
||||
timeout -= 10;
|
||||
@ -178,12 +182,14 @@ namespace Input {
|
||||
if (key.empty()) return;
|
||||
try {
|
||||
auto& filtering = Config::getB("proc_filtering");
|
||||
if (not filtering and key == "q") exit(0);
|
||||
|
||||
//? Global input actions
|
||||
if (not filtering) {
|
||||
bool keep_going = false;
|
||||
if (is_in(key, "1", "2", "3", "4")) {
|
||||
if (str_to_lower(key) == "q") {
|
||||
exit(0);
|
||||
}
|
||||
else if (is_in(key, "1", "2", "3", "4")) {
|
||||
atomic_wait(Runner::active);
|
||||
static const array<string, 4> boxes = {"cpu", "mem", "net", "proc"};
|
||||
Config::toggle_box(boxes.at(std::stoi(key) - 1));
|
||||
@ -250,15 +256,9 @@ namespace Input {
|
||||
Config::set("proc_filter", ""s);
|
||||
|
||||
else if (key == "ö") {
|
||||
if (Global::overlay.empty()) {
|
||||
Global::overlay = Mv::to(Term::height / 2, Term::width / 2) + "\x1b[1;32mTESTING";
|
||||
Menu::active = true;
|
||||
}
|
||||
else {
|
||||
Global::overlay.clear();
|
||||
Menu::active = false;
|
||||
}
|
||||
Runner::run("all", true, true);
|
||||
Menu::menuMask.set(Menu::Menus::SignalSend);
|
||||
Menu::process();
|
||||
return;
|
||||
}
|
||||
else if (key.starts_with("mouse_")) {
|
||||
redraw = false;
|
||||
@ -321,16 +321,20 @@ namespace Input {
|
||||
if (key == "-" or key == "space") Proc::collapse = pid;
|
||||
no_update = false;
|
||||
}
|
||||
else if (key == "t") {
|
||||
Logger::debug(key);
|
||||
else if (is_in(key, "t", "k") and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
|
||||
atomic_wait(Runner::active);
|
||||
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
|
||||
Menu::menuMask.set(Menu::SignalSend);
|
||||
Menu::signalToSend = (key == "t" ? SIGTERM : SIGKILL);
|
||||
Menu::process();
|
||||
return;
|
||||
}
|
||||
else if (key == "k") {
|
||||
Logger::debug(key);
|
||||
return;
|
||||
}
|
||||
else if (key == "s") {
|
||||
Logger::debug(key);
|
||||
else if (key == "s" and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
|
||||
atomic_wait(Runner::active);
|
||||
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
|
||||
Menu::menuMask.set(Menu::SignalChoose);
|
||||
Menu::signalToSend = -1;
|
||||
Menu::process();
|
||||
return;
|
||||
}
|
||||
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
|
||||
|
@ -16,23 +16,45 @@ indent = tab
|
||||
tab-size = 4
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <robin_hood.h>
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <btop_menu.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_theme.hpp>
|
||||
#include <btop_draw.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
|
||||
using std::vector, std::deque, robin_hood::unordered_flat_map, std::array;
|
||||
using std::deque, robin_hood::unordered_flat_map, std::array, std::views::iota, std::ref;
|
||||
using namespace Tools;
|
||||
namespace rng = std::ranges;
|
||||
|
||||
namespace Menu {
|
||||
|
||||
atomic<bool> active (false);
|
||||
string output;
|
||||
string bg;
|
||||
bool redraw = true;
|
||||
int currentMenu = -1;
|
||||
msgBox messageBox;
|
||||
int signalToSend = 0;
|
||||
int signalKillRet = 0;
|
||||
|
||||
const array<string, 32> P_Signals = {
|
||||
"0",
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL",
|
||||
"SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
|
||||
"SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2",
|
||||
"SIGPIPE", "SIGALRM", "SIGTERM", "16", "SIGCHLD",
|
||||
"SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN",
|
||||
"SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ",
|
||||
"SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
|
||||
"SIGPWR", "SIGSYS"
|
||||
};
|
||||
|
||||
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||
|
||||
@ -74,4 +96,317 @@ namespace Menu {
|
||||
} }
|
||||
} }
|
||||
};
|
||||
msgBox::msgBox() {};
|
||||
msgBox::msgBox(int width, int boxtype, vector<string>& content, string title)
|
||||
: width(width), boxtype(boxtype) {
|
||||
const auto& tty_mode = Config::getB("tty_mode");
|
||||
const auto& rounded = Config::getB("rounded_corners");
|
||||
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);
|
||||
height = content.size() + 7;
|
||||
x = Term::width / 2 - width / 2;
|
||||
y = Term::height/2 - height/2;
|
||||
if (boxtype == 2) selected = 1;
|
||||
|
||||
|
||||
button_left = left_up + Symbols::h_line * 6 + Mv::l(7) + Mv::d(2) + left_down + Symbols::h_line * 6 + Mv::l(7) + Mv::u(1) + Symbols::v_line;
|
||||
button_right = Symbols::v_line + Mv::l(7) + Mv::u(1) + Symbols::h_line * 6 + right_up + Mv::l(7) + Mv::d(2) + Symbols::h_line * 6 + right_down + Mv::u(2);
|
||||
|
||||
box_contents = Draw::createBox(x, y, width, height, Theme::c("hi_fg"), true, title) + Mv::d(1);
|
||||
for (const auto& line : content) {
|
||||
box_contents += Mv::save + Mv::r(width / 2 - Fx::uncolor(line).size() / 2) + line + Mv::restore + Mv::d(1);
|
||||
}
|
||||
}
|
||||
|
||||
string msgBox::operator()() {
|
||||
string out;
|
||||
int pos = width / 2 - (boxtype == 0 ? 6 : 14);
|
||||
auto& first_color = (selected == 0 ? Theme::c("hi_fg") : Theme::c("div_line"));
|
||||
out = Mv::d(1) + Mv::r(pos) + Fx::b + first_color + button_left + (selected == 0 ? Theme::c("title") : Theme::c("main_fg") + Fx::ub)
|
||||
+ (boxtype == 0 ? " Ok " : " Yes ") + first_color + button_right;
|
||||
mouse_mappings["button1"] = Input::Mouse_loc{y + height - 4, x + pos + 1, 3, 12 + (boxtype > 0 ? 1 : 0)};
|
||||
if (boxtype > 0) {
|
||||
auto& second_color = (selected == 1 ? Theme::c("hi_fg") : Theme::c("div_line"));
|
||||
out += Mv::r(2) + second_color + button_left + (selected == 1 ? Theme::c("title") : Theme::c("main_fg") + Fx::ub)
|
||||
+ " No " + second_color + button_right;
|
||||
mouse_mappings["button2"] = Input::Mouse_loc{y + height - 4, x + pos + 15 + (boxtype > 0 ? 1 : 0), 3, 12};
|
||||
}
|
||||
return box_contents + out + Fx::reset;
|
||||
}
|
||||
|
||||
//? Process input
|
||||
int msgBox::input(string key) {
|
||||
if (key.empty()) return Invalid;
|
||||
|
||||
if (is_in(key, "escape", "backspace", "q") or key == "button2") {
|
||||
return No_Esc;
|
||||
}
|
||||
else if (key == "button1" or (boxtype == 0 and str_to_upper(key) == "O")) {
|
||||
return Ok_Yes;
|
||||
}
|
||||
else if (is_in(key, "enter", "space")) {
|
||||
return selected + 1;
|
||||
}
|
||||
else if (boxtype == 0) {
|
||||
return Invalid;
|
||||
}
|
||||
else if (str_to_upper(key) == "Y") {
|
||||
return Ok_Yes;
|
||||
}
|
||||
else if (str_to_upper(key) == "N") {
|
||||
return No_Esc;
|
||||
}
|
||||
else if (is_in(key, "right", "tab")) {
|
||||
if (++selected > 1) selected = 0;
|
||||
return Select;
|
||||
}
|
||||
else if (is_in(key, "left", "shift_tab")) {
|
||||
if (--selected < 0) selected = 1;
|
||||
return Select;
|
||||
}
|
||||
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
void msgBox::clear() {
|
||||
box_contents.clear();
|
||||
box_contents.shrink_to_fit();
|
||||
button_left.clear();
|
||||
button_left.shrink_to_fit();
|
||||
button_right.clear();
|
||||
button_right.shrink_to_fit();
|
||||
if (mouse_mappings.contains("button1")) mouse_mappings.erase("button1");
|
||||
if (mouse_mappings.contains("button2")) mouse_mappings.erase("button2");
|
||||
}
|
||||
|
||||
enum menuReturnCodes {
|
||||
NoChange,
|
||||
Changed,
|
||||
Closed
|
||||
};
|
||||
|
||||
int signalChoose(const string& key) {
|
||||
auto& s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
static int x = 0, y = 0, selected_signal = -1;
|
||||
if (bg.empty()) selected_signal = -1;
|
||||
auto& out = Global::overlay;
|
||||
int retval = Changed;
|
||||
|
||||
if (redraw) {
|
||||
x = Term::width/2 - 40;
|
||||
y = Term::height/2 - 9;
|
||||
bg = Draw::createBox(x, y, 80, 18, Theme::c("hi_fg"), true, "signals");
|
||||
bg += Mv::to(y+2, x+1) + Theme::c("title") + Fx::b + cjust("Send signal to PID " + to_string(s_pid) + " ("
|
||||
+ uresize((s_pid == Config::getI("detailed_pid") ? Proc::detailed.entry.name : Config::getS("selected_name")), 30) + ")", 78);
|
||||
}
|
||||
|
||||
if (is_in(key, "escape", "q")) {
|
||||
return Closed;
|
||||
}
|
||||
else if (is_in(key, "enter", "space") and selected_signal >= 0) {
|
||||
signalKillRet = 0;
|
||||
if (s_pid < 1) {
|
||||
signalKillRet = ESRCH;
|
||||
menuMask.set(SignalReturn);
|
||||
}
|
||||
else if (kill(s_pid, selected_signal) != 0) {
|
||||
signalKillRet = errno;
|
||||
menuMask.set(SignalReturn);
|
||||
}
|
||||
return Closed;
|
||||
}
|
||||
else if (key.size() == 1 and isdigit(key.at(0)) and selected_signal < 10) {
|
||||
selected_signal = std::min(std::stoi((selected_signal < 1 ? key : to_string(selected_signal) + key)), 64);
|
||||
}
|
||||
else if (key == "backspace" and selected_signal != -1) {
|
||||
selected_signal = (selected_signal < 10 ? -1 : selected_signal / 10);
|
||||
}
|
||||
else if (key == "up" and selected_signal != 16) {
|
||||
if (selected_signal < 6) selected_signal += 25;
|
||||
else {
|
||||
bool offset = (selected_signal > 16);
|
||||
selected_signal -= 5;
|
||||
if (selected_signal <= 16 and offset) selected_signal--;
|
||||
}
|
||||
}
|
||||
else if (key == "down") {
|
||||
if (selected_signal < 1 or selected_signal == 16) selected_signal = 1;
|
||||
else if (selected_signal > 26) selected_signal -= 25;
|
||||
else {
|
||||
bool offset = (selected_signal < 16);
|
||||
selected_signal += 5;
|
||||
if (selected_signal >= 16 and offset) selected_signal++;
|
||||
if (selected_signal > 31) selected_signal = 31;
|
||||
}
|
||||
}
|
||||
else if (key == "left" and selected_signal > 1 and selected_signal != 16) {
|
||||
selected_signal--;
|
||||
if (selected_signal == 16) selected_signal--;
|
||||
}
|
||||
else if (key == "right" and selected_signal < 31 and selected_signal != 16) {
|
||||
selected_signal++;
|
||||
if (selected_signal == 16) selected_signal++;
|
||||
}
|
||||
else {
|
||||
retval = NoChange;
|
||||
}
|
||||
|
||||
int cy = y+3;
|
||||
out = bg + Mv::to(cy++, x+1) + Theme::c("main_fg") + Fx::ub
|
||||
+ rjust("Enter signal number: ", 48) + (selected_signal >= 0 ? to_string(selected_signal) : "") + Fx::bl + "█" + Fx::ubl;
|
||||
|
||||
out += Mv::to(++cy, x+4);
|
||||
auto sig_str = to_string(selected_signal);
|
||||
for (int count = 0, i = 0; const auto& sig : P_Signals) {
|
||||
if (count == 0 or count == 16) { count++; continue; }
|
||||
if (i++ % 5 == 0) out += Mv::to(++cy, x+4);
|
||||
if (count == selected_signal) out += Theme::c("selected_bg") + Theme::c("selected_fg") + Fx::b + ljust(to_string(count), 3) + ljust('(' + sig + ')', 12) + Fx::reset;
|
||||
else out += Theme::c("hi_fg") + ljust(to_string(count), 3) + Theme::c("main_fg") + ljust('(' + sig + ')', 12);
|
||||
count++;
|
||||
}
|
||||
|
||||
cy++;
|
||||
out += Mv::to(++cy, x+1) + Fx::b + rjust("ENTER | ", 35) + Fx::ub + "To send signal.";
|
||||
out += Mv::to(++cy, x+1) + Fx::b + rjust( "↑ ↓ ← → | ", 35, true) + Fx::ub + "To choose signal.";
|
||||
out += Mv::to(++cy, x+1) + Fx::b + rjust("ESC or \"q\" | ", 35) + Fx::ub + "To abort.";
|
||||
|
||||
out += Fx::reset;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (redraw ? Changed : retval);
|
||||
}
|
||||
|
||||
int signalSend(const string& key) {
|
||||
auto& s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
if (s_pid == 0) return Closed;
|
||||
if (redraw) {
|
||||
atomic_wait(Runner::active);
|
||||
auto& p_name = (s_pid == Config::getI("detailed_pid") ? Proc::detailed.entry.name : Config::getS("selected_name"));
|
||||
vector<string> cont_vec = {
|
||||
Fx::b + Theme::c("main_fg") + "Send signal: " + Fx::ub + Theme::c("hi_fg") + to_string(signalToSend)
|
||||
+ (signalToSend > 0 and signalToSend <= 32 ? Theme::c("main_fg") + " (" + P_Signals.at(signalToSend) + ')' : ""),
|
||||
|
||||
Fx::b + Theme::c("main_fg") + "To PID: " + Fx::ub + Theme::c("hi_fg") + to_string(s_pid) + Theme::c("main_fg") + " ("
|
||||
+ uresize(p_name, 16) + ')' + Fx::reset,
|
||||
};
|
||||
messageBox = Menu::msgBox{50, 1, cont_vec, (signalToSend > 1 and signalToSend <= 32 and signalToSend != 17 ? P_Signals.at(signalToSend) : "signal")};
|
||||
Global::overlay = messageBox();
|
||||
}
|
||||
auto ret = messageBox.input(key);
|
||||
if (ret == msgBox::Ok_Yes) {
|
||||
signalKillRet = 0;
|
||||
if (kill(s_pid, signalToSend) != 0) {
|
||||
signalKillRet = errno;
|
||||
menuMask.set(SignalReturn);
|
||||
}
|
||||
messageBox.clear();
|
||||
return Closed;
|
||||
}
|
||||
else if (ret == msgBox::No_Esc) {
|
||||
messageBox.clear();
|
||||
return Closed;
|
||||
}
|
||||
else if (ret == msgBox::Select) {
|
||||
Global::overlay = messageBox();
|
||||
return Changed;
|
||||
}
|
||||
else if (redraw) {
|
||||
return Changed;
|
||||
}
|
||||
return NoChange;
|
||||
}
|
||||
|
||||
int signalReturn(const string& key) {
|
||||
if (redraw) {
|
||||
vector<string> cont_vec;
|
||||
cont_vec.push_back(Fx::b + Theme::g("used")[100] + "Failure:" + Theme::c("main_fg") + Fx::ub);
|
||||
if (signalKillRet == EINVAL) {
|
||||
cont_vec.push_back("Unsupported signal!" + Fx::reset);
|
||||
}
|
||||
else if (signalKillRet == EPERM) {
|
||||
cont_vec.push_back("Insufficient permissions to send signal!" + Fx::reset);
|
||||
}
|
||||
else if (signalKillRet == ESRCH) {
|
||||
cont_vec.push_back("Process not found!" + Fx::reset);
|
||||
}
|
||||
else {
|
||||
cont_vec.push_back("Unknown error! (errno: " + to_string(signalKillRet) + ')' + Fx::reset);
|
||||
}
|
||||
|
||||
messageBox = Menu::msgBox{50, 0, cont_vec, "error"};
|
||||
Global::overlay = messageBox();
|
||||
}
|
||||
|
||||
auto ret = messageBox.input(key);
|
||||
if (ret == msgBox::Ok_Yes or ret == msgBox::No_Esc) {
|
||||
messageBox.clear();
|
||||
return Closed;
|
||||
}
|
||||
else if (redraw) {
|
||||
return Changed;
|
||||
}
|
||||
return NoChange;
|
||||
}
|
||||
|
||||
int mainMenu(const string& key) {
|
||||
(void)key;
|
||||
return NoChange;
|
||||
}
|
||||
int optionsMenu(const string& key) {
|
||||
(void)key;
|
||||
return NoChange;
|
||||
}
|
||||
int helpMenu(const string& key) {
|
||||
(void)key;
|
||||
return NoChange;
|
||||
}
|
||||
|
||||
//* Add menus here and update enum Menus in header
|
||||
const auto menuFunc = vector{
|
||||
ref(signalChoose),
|
||||
ref(signalSend),
|
||||
ref(signalReturn),
|
||||
ref(optionsMenu),
|
||||
ref(helpMenu),
|
||||
ref(mainMenu),
|
||||
};
|
||||
bitset<8> menuMask;
|
||||
|
||||
void process(string key) {
|
||||
if (menuMask.none()) {
|
||||
Menu::active = false;
|
||||
Global::overlay.clear();
|
||||
Global::overlay.shrink_to_fit();
|
||||
bg.clear();
|
||||
bg.shrink_to_fit();
|
||||
currentMenu = -1;
|
||||
Runner::run("all", true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentMenu < 0 or not menuMask.test(currentMenu)) {
|
||||
Menu::active = true;
|
||||
redraw = true;
|
||||
for (const auto& i : iota(0, (int)menuMask.size())) {
|
||||
if (menuMask.test(i)) currentMenu = i;
|
||||
}
|
||||
}
|
||||
|
||||
auto retCode = menuFunc.at(currentMenu)(key);
|
||||
if (retCode == Closed) {
|
||||
menuMask.reset(currentMenu);
|
||||
process();
|
||||
}
|
||||
else if (redraw) {
|
||||
redraw = false;
|
||||
Runner::run("all", true, true);
|
||||
}
|
||||
else if (retCode == Changed)
|
||||
Runner::run("overlay");
|
||||
}
|
||||
}
|
@ -20,17 +20,63 @@ tab-size = 4
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
#include <btop_input.hpp>
|
||||
|
||||
using std::string, std::atomic;
|
||||
using std::string, std::atomic, std::vector, std::bitset;
|
||||
|
||||
namespace Menu {
|
||||
|
||||
extern atomic<bool> active;
|
||||
extern string output;
|
||||
extern int signalToSend;
|
||||
extern bool redraw;
|
||||
|
||||
//? line, col, height, width
|
||||
extern unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
|
||||
|
||||
//* Creates a message box centered on screen
|
||||
//? Height of box is determined by size of content vector
|
||||
//? Boxtypes: 0 = OK button | 1 = YES and NO with YES selected | 2 = Same as 1 but with NO selected
|
||||
//? Strings in content vector is not checked for box width overflow
|
||||
class msgBox {
|
||||
string box_contents, button_left, button_right;
|
||||
int height = 0, width = 0, boxtype = 0, selected = 0, x = 0, y = 0;
|
||||
public:
|
||||
enum msgReturn {
|
||||
Invalid,
|
||||
Ok_Yes,
|
||||
No_Esc,
|
||||
Select
|
||||
};
|
||||
msgBox();
|
||||
msgBox(int width, int boxtype, vector<string>& content, string title);
|
||||
|
||||
//? Draw and return box as a string
|
||||
string operator()();
|
||||
|
||||
//? Process input and returns value from enum Ret
|
||||
int input(string key);
|
||||
|
||||
//? Clears content vector and private strings
|
||||
void clear();
|
||||
};
|
||||
|
||||
extern bitset<8> menuMask;
|
||||
|
||||
//* Enum for functions in vector menuFuncs
|
||||
enum Menus {
|
||||
SignalChoose,
|
||||
SignalSend,
|
||||
SignalReturn,
|
||||
Options,
|
||||
Help,
|
||||
Main
|
||||
};
|
||||
|
||||
//* Handles redirection of input for menu functions and handles return codes
|
||||
void process(string key="");
|
||||
|
||||
}
|
||||
|
@ -116,9 +116,10 @@ namespace Mem {
|
||||
std::filesystem::path stat = "";
|
||||
int64_t total = 0, used = 0, free = 0;
|
||||
int used_percent = 0, free_percent = 0;
|
||||
array<int64_t, 2> old_io = {0, 0};
|
||||
array<int64_t, 3> old_io = {0, 0, 0};
|
||||
deque<long long> io_read = {};
|
||||
deque<long long> io_write = {};
|
||||
deque<long long> io_activity = {};
|
||||
};
|
||||
|
||||
struct mem_info {
|
||||
@ -175,6 +176,7 @@ namespace Proc {
|
||||
extern int select_max;
|
||||
extern atomic<int> detailed_pid;
|
||||
extern int selected_pid, start, selected, collapse, expand;
|
||||
extern string selected_name;
|
||||
|
||||
//? Contains the valid sorting options for processes
|
||||
const vector<string> sort_vector = {
|
||||
@ -208,12 +210,13 @@ namespace Proc {
|
||||
size_t pid = 0;
|
||||
string name = "", cmd = "";
|
||||
string short_cmd = "";
|
||||
size_t threads = 0, name_offset = 0;
|
||||
size_t threads = 0;
|
||||
int name_offset = 0;
|
||||
string user = "";
|
||||
uint64_t mem = 0;
|
||||
double cpu_p = 0.0, cpu_c = 0.0;
|
||||
char state = '0';
|
||||
uint64_t cpu_n = 0, p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
||||
uint64_t p_nice = 0, ppid = 0, cpu_s = 0, cpu_t = 0;
|
||||
string prefix = "";
|
||||
size_t depth = 0, tree_index = 0;
|
||||
bool collapsed = false, filtered = false;
|
||||
|
@ -65,6 +65,7 @@ namespace Fx {
|
||||
|
||||
//* Return a string with all colors and text styling removed
|
||||
inline string uncolor(const string& s) { return regex_replace(s, color_regex, ""); }
|
||||
|
||||
}
|
||||
|
||||
//* Collection of escape codes and functions for cursor manipulation
|
||||
@ -129,7 +130,6 @@ namespace Term {
|
||||
|
||||
namespace Tools {
|
||||
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
|
||||
constexpr auto ZeroSec = std::chrono::seconds(0);
|
||||
extern atomic<int> active_locks;
|
||||
|
||||
//* Return number of UTF8 characters in a string (wide=true counts UTF-8 characters with a width > 1 as 2 characters)
|
||||
|
@ -31,8 +31,8 @@ tab-size = 4
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
|
||||
using std::string, std::vector, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::round, std::max, std::min,
|
||||
std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
|
||||
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
|
||||
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
using namespace Tools;
|
||||
@ -73,8 +73,8 @@ namespace Cpu {
|
||||
unordered_flat_map<int, int> core_mapping;
|
||||
}
|
||||
|
||||
namespace Net {
|
||||
uint64_t timestamp = 0;
|
||||
namespace Mem {
|
||||
double old_uptime;
|
||||
}
|
||||
|
||||
namespace Shared {
|
||||
@ -136,12 +136,9 @@ namespace Shared {
|
||||
Cpu::core_mapping = Cpu::get_core_mapping();
|
||||
|
||||
//? Init for namespace Mem
|
||||
Mem::old_uptime = system_uptime();
|
||||
Mem::collect();
|
||||
|
||||
//? Init for namespace Net
|
||||
Net::timestamp = time_ms();
|
||||
Net::collect();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -545,7 +542,6 @@ namespace Mem {
|
||||
int disk_ios = 0;
|
||||
vector<string> last_found;
|
||||
|
||||
|
||||
mem_info current_mem {};
|
||||
|
||||
auto collect(const bool no_update) -> mem_info& {
|
||||
@ -614,6 +610,7 @@ namespace Mem {
|
||||
|
||||
//? Get disks stats
|
||||
if (show_disks) {
|
||||
double uptime = system_uptime();
|
||||
try {
|
||||
auto& disks_filter = Config::getS("disks_filter");
|
||||
bool filter_exclude = false;
|
||||
@ -753,7 +750,7 @@ namespace Mem {
|
||||
if (not is_in(name, "/", "swap")) mem.disks_order.push_back(name);
|
||||
|
||||
//? Get disks IO
|
||||
int64_t sectors_read, sectors_write;
|
||||
int64_t sectors_read, sectors_write, io_ticks;
|
||||
disk_ios = 0;
|
||||
for (auto& [ignored, disk] : disks) {
|
||||
if (disk.stat.empty() or access(disk.stat.c_str(), R_OK) != 0) continue;
|
||||
@ -777,9 +774,19 @@ namespace Mem {
|
||||
disk.io_write.push_back(max(0l, (sectors_write - disk.old_io.at(1)) * 512));
|
||||
disk.old_io.at(1) = sectors_write;
|
||||
while (cmp_greater(disk.io_write.size(), width * 2)) disk.io_write.pop_front();
|
||||
|
||||
for (int i = 0; i < 2; i++) { diskread >> std::ws; diskread.ignore(SSmax, ' '); }
|
||||
diskread >> io_ticks;
|
||||
if (disk.io_activity.empty())
|
||||
disk.io_activity.push_back(0);
|
||||
else
|
||||
disk.io_activity.push_back(clamp((long)round((double)(io_ticks - disk.old_io.at(2)) / (uptime - old_uptime) / 10), 0l, 100l));
|
||||
disk.old_io.at(2) = io_ticks;
|
||||
while (cmp_greater(disk.io_activity.size(), width * 2)) disk.io_activity.pop_front();
|
||||
}
|
||||
diskread.close();
|
||||
}
|
||||
old_uptime = uptime;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::warning("Error in Mem::collect() : " + (string)e.what());
|
||||
@ -800,6 +807,7 @@ namespace Net {
|
||||
unordered_flat_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
|
||||
unordered_flat_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
|
||||
bool rescale = true;
|
||||
uint64_t timestamp = 0;
|
||||
|
||||
//* RAII wrapper for getifaddrs
|
||||
class getifaddr_wrapper {
|
||||
@ -824,6 +832,7 @@ namespace Net {
|
||||
if (if_wrap.status != 0) {
|
||||
errors++;
|
||||
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_wrap.status));
|
||||
redraw = true;
|
||||
return empty_net;
|
||||
}
|
||||
int family = 0;
|
||||
@ -931,7 +940,9 @@ namespace Net {
|
||||
break;
|
||||
}
|
||||
//? If no interface is connected set to first available
|
||||
if (selected_iface.empty()) selected_iface = sorted_interfaces.at(0);
|
||||
if (selected_iface.empty() and not sorted_interfaces.empty()) selected_iface = sorted_interfaces.at(0);
|
||||
else if (sorted_interfaces.empty()) return empty_net;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1152,7 +1163,7 @@ 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;
|
||||
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) {
|
||||
@ -1174,6 +1185,8 @@ namespace Proc {
|
||||
}
|
||||
//* ---------------------------------------------Collection start----------------------------------------------
|
||||
else {
|
||||
should_filter = true;
|
||||
|
||||
//? Update uid_user map if /etc/passwd changed since last run
|
||||
if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != passwd_time) {
|
||||
string r_uid, r_user;
|
||||
@ -1269,20 +1282,18 @@ namespace Proc {
|
||||
|
||||
const auto& offset = new_proc.name_offset;
|
||||
short_str.clear();
|
||||
size_t x = 0, next_x = 3;
|
||||
int x = 0, next_x = 3;
|
||||
uint64_t cpu_t = 0;
|
||||
try {
|
||||
while (x < 40) {
|
||||
while (pread.good() and ++x < next_x + offset) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
}
|
||||
if (pread.bad()) break;
|
||||
|
||||
for (;;) {
|
||||
while (++x < next_x + offset) pread.ignore(SSmax, ' ');
|
||||
getline(pread, short_str, ' ');
|
||||
if (not pread.good()) break;
|
||||
|
||||
switch (x-offset) {
|
||||
case 3: //? Process state
|
||||
new_proc.state = short_str.at(0);
|
||||
if (new_proc.ppid != 0) next_x = 14;
|
||||
continue;
|
||||
case 4: //? Parent pid
|
||||
new_proc.ppid = stoull(short_str);
|
||||
@ -1313,18 +1324,14 @@ namespace Proc {
|
||||
continue;
|
||||
case 24: //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x)
|
||||
new_proc.mem = stoull(short_str) * Shared::pageSize;
|
||||
next_x = 39;
|
||||
continue;
|
||||
case 39: //? CPU number last executed on
|
||||
new_proc.cpu_n = stoull(short_str);
|
||||
x++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::invalid_argument&) { continue; }
|
||||
catch (const std::out_of_range&) { continue; }
|
||||
|
||||
pread.close();
|
||||
|
||||
if (x-offset < 24) continue;
|
||||
|
Loading…
Reference in New Issue
Block a user