2021-05-07 06:32:03 +12:00
|
|
|
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
indent = tab
|
|
|
|
tab-size = 4
|
|
|
|
*/
|
|
|
|
|
2021-06-10 05:47:49 +12:00
|
|
|
#include <csignal>
|
2021-08-04 09:47:46 +12:00
|
|
|
#include <pthread.h>
|
2021-08-11 06:20:33 +12:00
|
|
|
#include <thread>
|
|
|
|
#include <future>
|
2021-08-11 18:00:13 +12:00
|
|
|
#include <bitset>
|
2021-05-20 09:21:56 +12:00
|
|
|
#include <numeric>
|
2021-05-17 08:58:16 +12:00
|
|
|
#include <ranges>
|
2021-05-22 12:13:56 +12:00
|
|
|
#include <unistd.h>
|
2021-06-20 00:57:27 +12:00
|
|
|
#include <cmath>
|
2021-06-20 08:48:31 +12:00
|
|
|
#include <iostream>
|
2021-07-04 11:18:48 +12:00
|
|
|
#include <exception>
|
2021-05-24 08:25:07 +12:00
|
|
|
|
2021-06-20 10:49:13 +12:00
|
|
|
#include <btop_shared.hpp>
|
2021-06-20 08:48:31 +12:00
|
|
|
#include <btop_tools.hpp>
|
|
|
|
#include <btop_config.hpp>
|
|
|
|
#include <btop_input.hpp>
|
|
|
|
#include <btop_theme.hpp>
|
|
|
|
#include <btop_draw.hpp>
|
|
|
|
#include <btop_menu.hpp>
|
2021-05-09 00:56:48 +12:00
|
|
|
|
|
|
|
#if defined(__linux__)
|
2021-06-10 05:47:49 +12:00
|
|
|
#define LINUX
|
2021-06-22 08:52:55 +12:00
|
|
|
#elif defined(__unix__) or not defined(__APPLE__) and defined(__MACH__)
|
2021-05-09 00:56:48 +12:00
|
|
|
#include <sys/param.h>
|
|
|
|
#if defined(BSD)
|
2021-05-20 09:21:56 +12:00
|
|
|
#error BSD support not yet implemented!
|
2021-05-09 00:56:48 +12:00
|
|
|
#endif
|
2021-06-22 08:52:55 +12:00
|
|
|
#elif defined(__APPLE__) and defined(__MACH__)
|
2021-05-09 00:56:48 +12:00
|
|
|
#include <TargetConditionals.h>
|
|
|
|
#if TARGET_OS_MAC == 1
|
2021-06-10 05:47:49 +12:00
|
|
|
#define OSX
|
2021-05-20 09:21:56 +12:00
|
|
|
#error OSX support not yet implemented!
|
2021-05-09 00:56:48 +12:00
|
|
|
#endif
|
2021-05-20 09:21:56 +12:00
|
|
|
#else
|
2021-05-24 08:25:07 +12:00
|
|
|
#error Platform not supported!
|
2021-05-09 00:56:48 +12:00
|
|
|
#endif
|
2021-05-07 06:32:03 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
using std::string, std::string_view, std::vector, std::array, std::atomic, std::endl, std::cout, std::min;
|
2021-08-11 18:00:13 +12:00
|
|
|
using std::flush, std::endl, std::string_literals::operator""s, std::to_string, std::future, std::async, std::bitset, std::future_status;
|
2021-07-21 13:17:34 +12:00
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace rng = std::ranges;
|
|
|
|
using namespace Tools;
|
|
|
|
|
2021-06-20 00:57:27 +12:00
|
|
|
namespace Global {
|
2021-07-21 13:17:34 +12:00
|
|
|
const vector<array<string, 2>> Banner_src = {
|
2021-06-20 00:57:27 +12:00
|
|
|
{"#E62525", "██████╗ ████████╗ ██████╗ ██████╗"},
|
|
|
|
{"#CD2121", "██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗ ██╗ ██╗"},
|
|
|
|
{"#B31D1D", "██████╔╝ ██║ ██║ ██║██████╔╝ ██████╗██████╗"},
|
|
|
|
{"#9A1919", "██╔══██╗ ██║ ██║ ██║██╔═══╝ ╚═██╔═╝╚═██╔═╝"},
|
|
|
|
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
|
|
|
|
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
|
|
|
|
};
|
2021-08-16 09:20:55 +12:00
|
|
|
const string Version = "0.5.0";
|
2021-05-07 06:32:03 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
int coreCount;
|
2021-05-15 04:54:37 +12:00
|
|
|
string banner;
|
2021-06-03 07:33:26 +12:00
|
|
|
size_t banner_width = 0;
|
2021-07-21 13:17:34 +12:00
|
|
|
string overlay;
|
2021-07-30 09:40:56 +12:00
|
|
|
string clock;
|
2021-05-23 11:59:13 +12:00
|
|
|
|
2021-07-26 11:06:34 +12:00
|
|
|
fs::path self_path;
|
|
|
|
|
2021-07-19 01:44:32 +12:00
|
|
|
string exit_error_msg;
|
|
|
|
atomic<bool> thread_exception (false);
|
|
|
|
|
2021-05-29 12:32:36 +12:00
|
|
|
bool debuginit = false;
|
|
|
|
bool debug = false;
|
2021-07-21 13:17:34 +12:00
|
|
|
bool utf_force = false;
|
2021-05-29 12:32:36 +12:00
|
|
|
|
|
|
|
uint64_t start_time;
|
|
|
|
|
2021-07-16 09:49:16 +12:00
|
|
|
atomic<bool> resized (false);
|
2021-08-11 06:20:33 +12:00
|
|
|
atomic<int> resizing (0);
|
2021-07-04 11:18:48 +12:00
|
|
|
atomic<bool> quitting (false);
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
bool arg_tty = false;
|
2021-06-22 08:52:55 +12:00
|
|
|
bool arg_low_color = false;
|
2021-05-09 00:56:48 +12:00
|
|
|
}
|
2021-05-07 06:32:03 +12:00
|
|
|
|
|
|
|
|
2021-05-09 00:56:48 +12:00
|
|
|
//* A simple argument parser
|
2021-07-21 13:17:34 +12:00
|
|
|
void argumentParser(const int& argc, char **argv) {
|
2021-05-08 12:38:51 +12:00
|
|
|
for(int i = 1; i < argc; i++) {
|
2021-08-13 08:25:18 +12:00
|
|
|
const string argument = argv[i];
|
|
|
|
if (is_in(argument, "-h", "--help")) {
|
2021-06-20 00:57:27 +12:00
|
|
|
cout << "usage: btop [-h] [-v] [-/+t] [--debug]\n\n"
|
|
|
|
<< "optional arguments:\n"
|
2021-06-22 08:52:55 +12:00
|
|
|
<< " -h, --help show this help message and exit\n"
|
|
|
|
<< " -v, --version show version info and exit\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_off force (OFF) tty mode\n"
|
2021-07-21 13:17:34 +12:00
|
|
|
<< " --utf-foce force start even if no UTF-8 locale was detected"
|
2021-08-18 08:33:21 +12:00
|
|
|
<< " --debug start in debug mode with loglevel set to DEBUG\n"
|
2021-06-20 00:57:27 +12:00
|
|
|
<< endl;
|
2021-05-08 12:38:51 +12:00
|
|
|
exit(0);
|
2021-05-29 12:32:36 +12:00
|
|
|
}
|
2021-08-13 08:25:18 +12:00
|
|
|
if (is_in(argument, "-v", "--version")) {
|
2021-07-21 13:17:34 +12:00
|
|
|
cout << "btop version: " << Global::Version << endl;
|
|
|
|
exit(0);
|
|
|
|
}
|
2021-08-13 08:25:18 +12:00
|
|
|
else if (is_in(argument, "-lc", "--low-color")) {
|
2021-07-21 13:17:34 +12:00
|
|
|
Global::arg_low_color = true;
|
|
|
|
}
|
2021-08-13 08:25:18 +12:00
|
|
|
else if (is_in(argument, "-t", "--tty_on")) {
|
2021-06-20 00:57:27 +12:00
|
|
|
Config::set("tty_mode", true);
|
|
|
|
Global::arg_tty = true;
|
|
|
|
}
|
2021-08-13 08:25:18 +12:00
|
|
|
else if (is_in(argument, "+t", "--tty_off")) {
|
2021-06-20 00:57:27 +12:00
|
|
|
Config::set("tty_mode", false);
|
|
|
|
Global::arg_tty = true;
|
|
|
|
}
|
2021-07-21 13:17:34 +12:00
|
|
|
else if (argument == "--utf-force")
|
|
|
|
Global::utf_force = true;
|
|
|
|
else if (argument == "--debug")
|
|
|
|
Global::debug = true;
|
2021-05-29 12:32:36 +12:00
|
|
|
else {
|
2021-05-08 12:38:51 +12:00
|
|
|
cout << " Unknown argument: " << argument << "\n" <<
|
|
|
|
" Use -h or --help for help." << endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-19 04:04:49 +12:00
|
|
|
//* Handler for SIGWINCH and general resizing events, does nothing if terminal hasn't been resized unless force=true
|
2021-07-30 09:40:56 +12:00
|
|
|
void term_resize(bool force) {
|
2021-08-11 06:20:33 +12:00
|
|
|
if (auto refreshed = Term::refresh(); refreshed or force) {
|
2021-07-19 04:04:49 +12:00
|
|
|
if (force and refreshed) force = false;
|
2021-07-16 09:49:16 +12:00
|
|
|
}
|
|
|
|
else return;
|
|
|
|
|
2021-08-11 06:20:33 +12:00
|
|
|
auto rez_state = ++Global::resizing;
|
|
|
|
if (rez_state > 1) return;
|
|
|
|
Global::resized = true;
|
|
|
|
Runner::stop();
|
2021-08-16 09:20:55 +12:00
|
|
|
auto min_size = Term::get_min_size(Config::getS("shown_boxes"));
|
|
|
|
|
2021-07-19 04:04:49 +12:00
|
|
|
while (not force) {
|
2021-07-16 09:49:16 +12:00
|
|
|
sleep_ms(100);
|
2021-08-11 06:20:33 +12:00
|
|
|
if (rez_state != Global::resizing) rez_state = --Global::resizing;
|
2021-08-16 09:20:55 +12:00
|
|
|
else if (not Term::refresh() and Term::width >= min_size[0] and Term::height >= min_size[1]) break;
|
2021-07-16 09:49:16 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
Input::interrupt = true;
|
2021-08-11 06:20:33 +12:00
|
|
|
Global::resizing = 0;
|
2021-07-16 09:49:16 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
//* Exit handler; stops threads, restores terminal and saves config changes
|
2021-08-11 06:20:33 +12:00
|
|
|
void clean_quit(int sig) {
|
2021-06-03 07:33:26 +12:00
|
|
|
if (Global::quitting) return;
|
2021-07-16 09:49:16 +12:00
|
|
|
Global::quitting = true;
|
2021-07-19 01:44:32 +12:00
|
|
|
Runner::stop();
|
2021-08-18 08:33:21 +12:00
|
|
|
if (pthread_join(Runner::runner_id, NULL) != 0)
|
|
|
|
Logger::error("Failed to join _runner thread!");
|
2021-08-11 06:20:33 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
if (not Global::exit_error_msg.empty()) {
|
2021-08-11 06:20:33 +12:00
|
|
|
sig = 1;
|
2021-07-21 13:17:34 +12:00
|
|
|
Logger::error(Global::exit_error_msg);
|
2021-07-30 09:40:56 +12:00
|
|
|
std::cerr << "ERROR: " << Global::exit_error_msg << endl;
|
2021-07-21 13:17:34 +12:00
|
|
|
}
|
2021-06-10 05:47:49 +12:00
|
|
|
Config::write();
|
2021-07-30 09:40:56 +12:00
|
|
|
Input::clear();
|
2021-06-14 09:12:11 +12:00
|
|
|
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
|
2021-08-04 09:47:46 +12:00
|
|
|
|
2021-08-11 06:20:33 +12:00
|
|
|
//? Wait for any remaining Tools::atomic_lock destructors to finish for max 1000ms
|
|
|
|
for (int i = 0; Tools::active_locks > 0 and i < 100; i++) {
|
|
|
|
sleep_ms(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Term::initialized) {
|
|
|
|
Term::restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
//? Assume error if still not cleaned up and call quick_exit to avoid a segfault from Tools::atomic_lock destructor
|
|
|
|
if (Tools::active_locks > 0) {
|
|
|
|
quick_exit((sig != -1 ? sig : 0));
|
|
|
|
}
|
2021-08-04 09:47:46 +12:00
|
|
|
|
2021-06-10 05:47:49 +12:00
|
|
|
if (sig != -1) exit(sig);
|
|
|
|
}
|
|
|
|
|
2021-07-16 09:49:16 +12:00
|
|
|
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
|
2021-07-21 13:17:34 +12:00
|
|
|
void _sleep() {
|
2021-07-19 01:44:32 +12:00
|
|
|
Runner::stop();
|
2021-07-30 09:40:56 +12:00
|
|
|
Term::restore();
|
2021-06-10 05:47:49 +12:00
|
|
|
std::raise(SIGSTOP);
|
|
|
|
}
|
|
|
|
|
2021-07-16 09:49:16 +12:00
|
|
|
//* Handler for SIGCONT; re-initialize terminal and force a resize event
|
2021-07-21 13:17:34 +12:00
|
|
|
void _resume() {
|
2021-06-10 05:47:49 +12:00
|
|
|
Term::init();
|
2021-07-19 04:04:49 +12:00
|
|
|
term_resize(true);
|
2021-06-03 07:33:26 +12:00
|
|
|
}
|
|
|
|
|
2021-07-16 09:49:16 +12:00
|
|
|
void _exit_handler() {
|
|
|
|
clean_quit(-1);
|
|
|
|
}
|
2021-06-03 07:33:26 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
void _signal_handler(const int sig) {
|
2021-06-10 05:47:49 +12:00
|
|
|
switch (sig) {
|
|
|
|
case SIGINT:
|
|
|
|
clean_quit(0);
|
|
|
|
break;
|
|
|
|
case SIGTSTP:
|
2021-07-16 09:49:16 +12:00
|
|
|
_sleep();
|
2021-06-10 05:47:49 +12:00
|
|
|
break;
|
|
|
|
case SIGCONT:
|
2021-07-16 09:49:16 +12:00
|
|
|
_resume();
|
|
|
|
break;
|
|
|
|
case SIGWINCH:
|
2021-07-19 04:04:49 +12:00
|
|
|
term_resize();
|
2021-06-10 05:47:49 +12:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//* Generate the btop++ banner
|
2021-06-03 07:33:26 +12:00
|
|
|
void banner_gen() {
|
|
|
|
Global::banner.clear();
|
|
|
|
Global::banner_width = 0;
|
2021-07-21 13:17:34 +12:00
|
|
|
string b_color, bg, fg, oc, letter;
|
|
|
|
auto& lowcolor = Config::getB("lowcolor");
|
|
|
|
auto& tty_mode = Config::getB("tty_mode");
|
|
|
|
for (size_t z = 0; const auto& line : Global::Banner_src) {
|
|
|
|
if (const auto w = ulen(line[1]); w > Global::banner_width) Global::banner_width = w;
|
2021-06-22 08:52:55 +12:00
|
|
|
if (tty_mode) {
|
|
|
|
fg = (z > 2) ? "\x1b[31m" : "\x1b[91m";
|
|
|
|
bg = (z > 2) ? "\x1b[90m" : "\x1b[37m";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fg = Theme::hex_to_color(line[0], lowcolor);
|
2021-07-21 13:17:34 +12:00
|
|
|
int bg_i = 120 - z * 12;
|
2021-06-22 08:52:55 +12:00
|
|
|
bg = Theme::dec_to_color(bg_i, bg_i, bg_i, lowcolor);
|
|
|
|
}
|
2021-05-19 08:11:34 +12:00
|
|
|
for (size_t i = 0; i < line[1].size(); i += 3) {
|
2021-06-20 00:57:27 +12:00
|
|
|
if (line[1][i] == ' ') {
|
2021-05-15 04:54:37 +12:00
|
|
|
letter = ' ';
|
|
|
|
i -= 2;
|
2021-05-07 06:32:03 +12:00
|
|
|
}
|
2021-06-20 00:57:27 +12:00
|
|
|
else
|
|
|
|
letter = line[1].substr(i, 3);
|
|
|
|
|
2021-06-22 08:52:55 +12:00
|
|
|
// if (tty_mode and letter != "█" and letter != " ") letter = "░";
|
2021-05-15 04:54:37 +12:00
|
|
|
b_color = (letter == "█") ? fg : bg;
|
2021-06-03 07:33:26 +12:00
|
|
|
if (b_color != oc) Global::banner += b_color;
|
|
|
|
Global::banner += letter;
|
2021-05-15 04:54:37 +12:00
|
|
|
oc = b_color;
|
2021-05-07 06:32:03 +12:00
|
|
|
}
|
2021-06-03 07:33:26 +12:00
|
|
|
if (++z < Global::Banner_src.size()) Global::banner += Mv::l(ulen(line[1])) + Mv::d(1);
|
2021-05-07 06:32:03 +12:00
|
|
|
}
|
2021-06-22 08:52:55 +12:00
|
|
|
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))
|
|
|
|
+ Fx::i + "v" + Global::Version + Fx::ui;
|
2021-05-15 04:54:37 +12:00
|
|
|
}
|
2021-05-07 06:32:03 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//* Manages secondary thread for collection and drawing of boxes
|
2021-07-05 08:02:31 +12:00
|
|
|
namespace Runner {
|
|
|
|
atomic<bool> active (false);
|
2021-07-19 01:44:32 +12:00
|
|
|
atomic<bool> stopping (false);
|
2021-08-11 06:20:33 +12:00
|
|
|
atomic<bool> waiting (false);
|
2021-08-18 08:33:21 +12:00
|
|
|
atomic<bool> do_work (false);
|
2021-07-19 01:44:32 +12:00
|
|
|
|
|
|
|
string output;
|
2021-08-04 09:47:46 +12:00
|
|
|
sigset_t mask;
|
2021-08-18 08:33:21 +12:00
|
|
|
pthread_t runner_id;
|
2021-08-11 06:20:33 +12:00
|
|
|
pthread_mutex_t mtx;
|
2021-08-11 18:00:13 +12:00
|
|
|
|
|
|
|
const unordered_flat_map<string, uint_fast8_t> box_bits = {
|
|
|
|
{"proc", 0b0000'0001},
|
2021-08-18 08:33:21 +12:00
|
|
|
{"net", 0b0000'0100},
|
|
|
|
{"mem", 0b0001'0000},
|
2021-08-11 18:00:13 +12:00
|
|
|
{"cpu", 0b0100'0000},
|
|
|
|
};
|
|
|
|
|
2021-08-12 06:25:11 +12:00
|
|
|
enum bit_pos {
|
|
|
|
proc_present, proc_running,
|
|
|
|
net_present, net_running,
|
2021-08-18 08:33:21 +12:00
|
|
|
mem_present, mem_running,
|
2021-08-12 06:25:11 +12:00
|
|
|
cpu_present, cpu_running
|
|
|
|
};
|
|
|
|
|
2021-08-11 18:00:13 +12:00
|
|
|
const uint_fast8_t proc_done = 0b0000'0011;
|
2021-08-18 08:33:21 +12:00
|
|
|
const uint_fast8_t net_done = 0b0000'1100;
|
|
|
|
const uint_fast8_t mem_done = 0b0011'0000;
|
2021-08-11 18:00:13 +12:00
|
|
|
const uint_fast8_t cpu_done = 0b1100'0000;
|
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
string debug_bg;
|
|
|
|
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
|
|
|
|
|
2021-08-04 09:47:46 +12:00
|
|
|
struct runner_conf {
|
2021-08-13 08:25:18 +12:00
|
|
|
bitset<8> box_mask;
|
|
|
|
bool no_update;
|
|
|
|
bool force_redraw;
|
|
|
|
string overlay;
|
|
|
|
string clock;
|
2021-08-04 09:47:46 +12:00
|
|
|
};
|
|
|
|
|
|
|
|
struct runner_conf current_conf;
|
2021-07-19 01:44:32 +12:00
|
|
|
|
2021-08-11 18:00:13 +12:00
|
|
|
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
|
2021-08-18 08:33:21 +12:00
|
|
|
void * _runner(void * _) {
|
|
|
|
(void)_;
|
2021-08-11 18:00:13 +12:00
|
|
|
//? Block all signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
|
2021-08-04 09:47:46 +12:00
|
|
|
pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
2021-08-11 18:00:13 +12:00
|
|
|
|
|
|
|
//? pthread_mutex_lock to make sure this thread is a single instance thread
|
|
|
|
thread_lock pt_lck(mtx);
|
|
|
|
if (pt_lck.status != 0) {
|
|
|
|
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
|
|
|
|
Global::thread_exception = true;
|
|
|
|
Input::interrupt = true;
|
|
|
|
stopping = true;
|
|
|
|
}
|
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
while (not Global::quitting) {
|
|
|
|
atomic_wait(do_work, false);
|
|
|
|
do_work = false;
|
|
|
|
if (stopping or Global::resized) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-11 18:00:13 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
auto& conf = current_conf;
|
|
|
|
|
|
|
|
//? Secondary atomic lock used for signaling status to main thread
|
|
|
|
atomic_lock lck(active);
|
|
|
|
|
|
|
|
//! DEBUG stats
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times.clear();
|
|
|
|
debug_times["total"] = {0, 0};
|
|
|
|
}
|
|
|
|
|
|
|
|
output.clear();
|
2021-08-11 18:00:13 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
//* Start collection functions for all boxes in async threads and draw in this thread when finished
|
|
|
|
//? Starting order below based on mean time to finish
|
|
|
|
try {
|
|
|
|
future<Cpu::cpu_info&> cpu;
|
|
|
|
future<Mem::mem_info&> mem;
|
|
|
|
future<Net::net_info&> net;
|
|
|
|
future<vector<Proc::proc_info>&> proc;
|
|
|
|
while (conf.box_mask.count() > 0) {
|
|
|
|
if (stopping) break;
|
|
|
|
//? PROC
|
|
|
|
if (conf.box_mask.test(proc_present)) {
|
|
|
|
if (not conf.box_mask.test(proc_running)) {
|
|
|
|
if (Global::debug) debug_times["proc"].at(0) = time_micros();
|
|
|
|
proc = async(Proc::collect, conf.no_update);
|
|
|
|
conf.box_mask.set(proc_running);
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
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) {
|
|
|
|
try {
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["proc"].at(1) = time_micros();
|
|
|
|
debug_times["proc"].at(0) = debug_times["proc"].at(1) - debug_times["proc"].at(0);
|
|
|
|
debug_times["total"].at(0) += debug_times["proc"].at(0);
|
|
|
|
}
|
|
|
|
output += Proc::draw(proc.get(), conf.force_redraw, conf.no_update);
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["proc"].at(1) = time_micros() - debug_times["proc"].at(1);
|
|
|
|
debug_times["total"].at(1) += debug_times["proc"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
throw std::runtime_error("Proc:: -> " + (string)e.what());
|
|
|
|
}
|
|
|
|
conf.box_mask ^= proc_done;
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
//? NET
|
|
|
|
if (conf.box_mask.test(net_present)) {
|
|
|
|
if (not conf.box_mask.test(net_running)) {
|
|
|
|
if (Global::debug) debug_times["net"].at(0) = time_micros();
|
|
|
|
net = async(Net::collect, conf.no_update);
|
|
|
|
conf.box_mask.set(net_running);
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
else if (not net.valid())
|
|
|
|
throw std::runtime_error("Net::collect() future not valid.");
|
|
|
|
|
|
|
|
else if (net.wait_for(ZeroSec) == future_status::ready) {
|
|
|
|
try {
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["net"].at(1) = time_micros();
|
|
|
|
debug_times["net"].at(0) = debug_times["net"].at(1) - debug_times["net"].at(0);
|
|
|
|
debug_times["total"].at(0) += debug_times["net"].at(0);
|
|
|
|
}
|
|
|
|
output += Net::draw(net.get(), conf.force_redraw, conf.no_update);
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["net"].at(1) = time_micros() - debug_times["net"].at(1);
|
|
|
|
debug_times["total"].at(1) += debug_times["net"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
throw std::runtime_error("Net:: -> " + (string)e.what());
|
|
|
|
}
|
|
|
|
conf.box_mask ^= net_done;
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
//? MEM
|
|
|
|
if (conf.box_mask.test(mem_present)) {
|
|
|
|
if (not conf.box_mask.test(mem_running)) {
|
|
|
|
if (Global::debug) debug_times["mem"].at(0) = time_micros();
|
|
|
|
mem = async(Mem::collect, conf.no_update);
|
|
|
|
conf.box_mask.set(mem_running);
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
else if (not mem.valid())
|
|
|
|
throw std::runtime_error("Mem::collect() future not valid.");
|
|
|
|
|
|
|
|
else if (mem.wait_for(ZeroSec) == future_status::ready) {
|
|
|
|
try {
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["mem"].at(1) = time_micros();
|
|
|
|
debug_times["mem"].at(0) = debug_times["mem"].at(1) - debug_times["mem"].at(0);
|
|
|
|
debug_times["total"].at(0) += debug_times["mem"].at(0);
|
|
|
|
}
|
|
|
|
output += Mem::draw(mem.get(), conf.force_redraw, conf.no_update);
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["mem"].at(1) = time_micros() - debug_times["mem"].at(1);
|
|
|
|
debug_times["total"].at(1) += debug_times["mem"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
throw std::runtime_error("Mem:: -> " + (string)e.what());
|
|
|
|
}
|
|
|
|
conf.box_mask ^= mem_done;
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
//? CPU
|
|
|
|
if (conf.box_mask.test(cpu_present)) {
|
|
|
|
if (not conf.box_mask.test(cpu_running)) {
|
|
|
|
if (Global::debug) debug_times["cpu"].at(0) = time_micros();
|
|
|
|
cpu = async(Cpu::collect, conf.no_update);
|
|
|
|
conf.box_mask.set(cpu_running);
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
else if (not cpu.valid())
|
|
|
|
throw std::runtime_error("Cpu::collect() future not valid.");
|
|
|
|
|
|
|
|
else if (cpu.wait_for(ZeroSec) == future_status::ready) {
|
|
|
|
try {
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["cpu"].at(1) = time_micros();
|
|
|
|
debug_times["cpu"].at(0) = debug_times["cpu"].at(1) - debug_times["cpu"].at(0);
|
|
|
|
debug_times["total"].at(0) += debug_times["cpu"].at(0);
|
|
|
|
}
|
|
|
|
output += Cpu::draw(cpu.get(), conf.force_redraw, conf.no_update);
|
|
|
|
if (Global::debug) {
|
|
|
|
debug_times["cpu"].at(1) = time_micros() - debug_times["cpu"].at(1);
|
|
|
|
debug_times["total"].at(1) += debug_times["cpu"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
throw std::runtime_error("Cpu:: -> " + (string)e.what());
|
|
|
|
}
|
|
|
|
conf.box_mask ^= cpu_done;
|
2021-08-11 18:00:13 +12:00
|
|
|
}
|
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
}
|
|
|
|
}
|
2021-08-18 08:33:21 +12:00
|
|
|
catch (const std::exception& e) {
|
|
|
|
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
|
|
|
|
Global::thread_exception = true;
|
|
|
|
Input::interrupt = true;
|
|
|
|
stopping = true;
|
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
if (stopping) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
//! DEBUG stats -->
|
|
|
|
if (Global::debug) {
|
|
|
|
output += debug_bg + Theme::c("title") + Fx::b + ljust(" Box", 9) + ljust("Collect μs", 12, true) + ljust("Draw μs", 9, true) + Theme::c("main_fg") + Fx::ub;
|
|
|
|
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
|
|
|
|
if (not debug_times.contains(name)) debug_times[name] = {0,0};
|
|
|
|
const auto& [time_collect, time_draw] = debug_times.at(name);
|
|
|
|
if (name == "total") output += Fx::b;
|
|
|
|
output += Mv::l(29) + Mv::d(1) + ljust(name, 8) + ljust(to_string(time_collect), 12) + ljust(to_string(time_draw), 9);
|
|
|
|
}
|
|
|
|
}
|
2021-07-21 13:17:34 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
//? If overlay isn't empty, print output without color and then print overlay on top
|
|
|
|
cout << Term::sync_start << (conf.overlay.empty()
|
|
|
|
? output + conf.clock
|
|
|
|
: Theme::c("inactive_fg") + Fx::ub + Fx::uncolor(output + conf.clock) + conf.overlay)
|
|
|
|
<< Term::sync_end << flush;
|
|
|
|
}
|
2021-07-21 13:17:34 +12:00
|
|
|
|
2021-08-04 09:47:46 +12:00
|
|
|
pthread_exit(NULL);
|
2021-07-19 01:44:32 +12:00
|
|
|
}
|
2021-08-11 06:20:33 +12:00
|
|
|
//? ------------------------------------------ Secondary thread end -----------------------------------------------
|
2021-07-19 01:44:32 +12:00
|
|
|
|
2021-08-13 08:25:18 +12:00
|
|
|
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
2021-07-23 11:41:00 +12:00
|
|
|
void run(const string& box, const bool no_update, const bool force_redraw) {
|
2021-08-11 06:20:33 +12:00
|
|
|
atomic_lock lck(waiting);
|
2021-07-30 09:40:56 +12:00
|
|
|
atomic_wait(active);
|
2021-08-11 06:20:33 +12:00
|
|
|
if (stopping or Global::resized) return;
|
2021-07-30 09:40:56 +12:00
|
|
|
|
|
|
|
if (box == "overlay") {
|
|
|
|
cout << Term::sync_start << Global::overlay << Term::sync_end;
|
|
|
|
}
|
|
|
|
else if (box == "clock") {
|
|
|
|
if (not Global::clock.empty())
|
|
|
|
cout << Term::sync_start << Global::clock << Term::sync_end;
|
|
|
|
}
|
2021-08-11 18:00:13 +12:00
|
|
|
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;
|
|
|
|
}
|
2021-07-30 09:40:56 +12:00
|
|
|
else {
|
|
|
|
Config::unlock();
|
|
|
|
Config::lock();
|
|
|
|
|
2021-08-13 08:25:18 +12:00
|
|
|
//? Setup bitmask for selected boxes instead of parsing strings
|
|
|
|
bitset<8> box_mask;
|
|
|
|
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
|
|
|
|
box_mask |= box_bits.at(box);
|
|
|
|
}
|
|
|
|
|
|
|
|
current_conf = {box_mask, no_update, force_redraw, Global::overlay, Global::clock};
|
2021-08-04 09:47:46 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
do_work = true;
|
|
|
|
atomic_notify(do_work);
|
2021-08-11 06:20:33 +12:00
|
|
|
|
|
|
|
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
|
2021-07-30 09:40:56 +12:00
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
}
|
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
//* Stops any work being done in runner thread and checks for thread errors
|
2021-07-19 01:44:32 +12:00
|
|
|
void stop() {
|
|
|
|
stopping = true;
|
2021-08-11 06:20:33 +12:00
|
|
|
int ret = pthread_mutex_trylock(&mtx);
|
2021-08-18 08:33:21 +12:00
|
|
|
if (ret != EBUSY and not Global::quitting) {
|
2021-08-11 06:20:33 +12:00
|
|
|
if (active) active = false;
|
|
|
|
Global::exit_error_msg = "Runner thread died unexpectedly!";
|
2021-08-18 08:33:21 +12:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
else if (ret == EBUSY) {
|
|
|
|
if (not active) {
|
|
|
|
do_work = true;
|
|
|
|
atomic_notify(do_work);
|
|
|
|
sleep_ms(1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
atomic_wait(active);
|
|
|
|
}
|
2021-08-11 06:20:33 +12:00
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
stopping = false;
|
|
|
|
}
|
|
|
|
|
2021-07-05 08:02:31 +12:00
|
|
|
}
|
2021-05-10 08:25:41 +12:00
|
|
|
|
2021-05-09 00:56:48 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//* --------------------------------------------- Main starts here! ---------------------------------------------------
|
|
|
|
int main(int argc, char **argv) {
|
2021-05-07 06:32:03 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? ------------------------------------------------ INIT ---------------------------------------------------------
|
2021-05-07 06:32:03 +12:00
|
|
|
|
2021-05-29 12:32:36 +12:00
|
|
|
Global::start_time = time_s();
|
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Call argument parser if launched with arguments
|
2021-05-07 06:32:03 +12:00
|
|
|
if (argc > 1) argumentParser(argc, argv);
|
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Setup signal handlers for CTRL-C, CTRL-Z, resume and terminal resize
|
2021-05-29 12:32:36 +12:00
|
|
|
std::atexit(_exit_handler);
|
2021-06-10 05:47:49 +12:00
|
|
|
std::signal(SIGINT, _signal_handler);
|
|
|
|
std::signal(SIGTSTP, _signal_handler);
|
|
|
|
std::signal(SIGCONT, _signal_handler);
|
2021-07-16 09:49:16 +12:00
|
|
|
std::signal(SIGWINCH, _signal_handler);
|
2021-08-04 09:47:46 +12:00
|
|
|
sigemptyset(&Runner::mask);
|
|
|
|
sigaddset(&Runner::mask, SIGINT);
|
|
|
|
sigaddset(&Runner::mask, SIGTSTP);
|
|
|
|
sigaddset(&Runner::mask, SIGWINCH);
|
|
|
|
sigaddset(&Runner::mask, SIGTERM);
|
2021-05-29 12:32:36 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Setup paths for config, log and user themes
|
|
|
|
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
2021-06-22 08:52:55 +12:00
|
|
|
if (getenv(env) != NULL and access(getenv(env), W_OK) != -1) {
|
2021-05-29 12:32:36 +12:00
|
|
|
Config::conf_dir = fs::path(getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
2021-05-23 11:59:13 +12:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-06-22 08:52:55 +12:00
|
|
|
if (not Config::conf_dir.empty()) {
|
|
|
|
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
|
2021-07-30 09:40:56 +12:00
|
|
|
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;
|
2021-05-23 11:59:13 +12:00
|
|
|
}
|
|
|
|
else {
|
2021-05-29 12:32:36 +12:00
|
|
|
Config::conf_file = Config::conf_dir / "btop.conf";
|
|
|
|
Logger::logfile = Config::conf_dir / "btop.log";
|
|
|
|
Theme::user_theme_dir = Config::conf_dir / "themes";
|
2021-06-22 08:52:55 +12:00
|
|
|
if (not fs::exists(Theme::user_theme_dir) and not fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear();
|
2021-05-23 11:59:13 +12:00
|
|
|
}
|
|
|
|
}
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Try to find global btop theme path relative to binary path
|
2021-07-26 11:06:34 +12:00
|
|
|
#if defined(LINUX)
|
|
|
|
{ std::error_code ec;
|
|
|
|
Global::self_path = fs::read_symlink("/proc/self/exe", ec).remove_filename();
|
|
|
|
}
|
|
|
|
#endif
|
2021-06-22 08:52:55 +12:00
|
|
|
if (std::error_code ec; not Global::self_path.empty()) {
|
2021-05-29 12:32:36 +12:00
|
|
|
Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec);
|
2021-07-04 11:18:48 +12:00
|
|
|
if (ec or not fs::is_directory(Theme::theme_dir) or access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear();
|
2021-05-29 12:32:36 +12:00
|
|
|
}
|
2021-07-21 13:17:34 +12:00
|
|
|
//? If relative path failed, check two most common absolute paths
|
2021-05-29 12:32:36 +12:00
|
|
|
if (Theme::theme_dir.empty()) {
|
|
|
|
for (auto theme_path : {"/usr/local/share/btop/themes", "/usr/share/btop/themes"}) {
|
2021-07-04 11:18:48 +12:00
|
|
|
if (fs::is_directory(fs::path(theme_path)) and access(theme_path, R_OK) != -1) {
|
2021-05-29 12:32:36 +12:00
|
|
|
Theme::theme_dir = fs::path(theme_path);
|
|
|
|
break;
|
|
|
|
}
|
2021-05-23 11:59:13 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 09:12:11 +12:00
|
|
|
//? Config init
|
2021-07-30 09:40:56 +12:00
|
|
|
{ vector<string> load_warnings;
|
|
|
|
Config::load(Config::conf_file, load_warnings);
|
2021-06-13 04:49:27 +12:00
|
|
|
|
2021-07-30 09:40:56 +12:00
|
|
|
if (Config::current_boxes.empty()) Config::check_boxes(Config::getS("shown_boxes"));
|
2021-06-22 08:52:55 +12:00
|
|
|
Config::set("lowcolor", (Global::arg_low_color ? true : not Config::getB("truecolor")));
|
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
if (Global::debug) {
|
|
|
|
Logger::set("DEBUG");
|
|
|
|
Logger::debug("Starting in DEBUG mode!");
|
|
|
|
}
|
2021-06-20 00:57:27 +12:00
|
|
|
else Logger::set(Config::getS("log_level"));
|
2021-06-13 04:49:27 +12:00
|
|
|
|
2021-06-22 08:52:55 +12:00
|
|
|
Logger::info("Logger set to " + Config::getS("log_level"));
|
2021-06-13 04:49:27 +12:00
|
|
|
|
2021-07-30 09:40:56 +12:00
|
|
|
for (const auto& err_str : load_warnings) Logger::warning(err_str);
|
2021-06-13 04:49:27 +12:00
|
|
|
}
|
2021-05-24 08:25:07 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Try to find and set a UTF-8 locale
|
|
|
|
if (bool found = false; not str_to_upper((string)std::setlocale(LC_ALL, NULL)).ends_with("UTF-8")) {
|
|
|
|
if (const string lang = (string)getenv("LANG"); str_to_upper(lang).ends_with("UTF-8")) {
|
|
|
|
found = true;
|
|
|
|
std::setlocale(LC_ALL, lang.c_str());
|
|
|
|
}
|
|
|
|
else if (const string loc = std::locale("").name(); not loc.empty()) {
|
|
|
|
try {
|
|
|
|
for (auto& l : ssplit(loc, ';')) {
|
|
|
|
if (str_to_upper(l).ends_with("UTF-8")) {
|
|
|
|
found = true;
|
|
|
|
std::setlocale(LC_ALL, l.substr(l.find('=') + 1).c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::out_of_range&) { found = false; }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not found and Global::utf_force)
|
|
|
|
Logger::warning("No UTF-8 locale detected! Forcing start with --utf-force argument.");
|
|
|
|
else if (not found) {
|
|
|
|
Global::exit_error_msg = "No UTF-8 locale detected! Use --utf-force argument to start anyway.";
|
2021-08-13 08:25:18 +12:00
|
|
|
exit(1);
|
2021-07-21 13:17:34 +12:00
|
|
|
}
|
|
|
|
else
|
|
|
|
Logger::debug("Setting LC_ALL=" + (string)std::setlocale(LC_ALL, NULL));
|
2021-05-24 08:25:07 +12:00
|
|
|
}
|
2021-05-23 11:59:13 +12:00
|
|
|
|
2021-05-09 10:18:51 +12:00
|
|
|
//? Initialize terminal and set options
|
2021-06-22 08:52:55 +12:00
|
|
|
if (not Term::init()) {
|
2021-07-30 09:40:56 +12:00
|
|
|
Global::exit_error_msg = "No tty detected!\nbtop++ needs an interactive shell to run.";
|
2021-08-13 08:25:18 +12:00
|
|
|
exit(1);
|
2021-05-07 06:32:03 +12:00
|
|
|
}
|
|
|
|
|
2021-06-22 08:52:55 +12:00
|
|
|
Logger::info("Running on " + Term::current_tty);
|
|
|
|
if (not Global::arg_tty and Config::getB("force_tty")) {
|
2021-06-20 00:57:27 +12:00
|
|
|
Config::set("tty_mode", true);
|
|
|
|
Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols");
|
|
|
|
}
|
2021-06-22 08:52:55 +12:00
|
|
|
else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) {
|
2021-06-20 00:57:27 +12:00
|
|
|
Config::set("tty_mode", true);
|
|
|
|
Logger::info("Real tty detected, setting 16 color mode and using tty friendly graph symbols");
|
|
|
|
}
|
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Platform dependent init and error check
|
2021-07-24 12:13:26 +12:00
|
|
|
try {
|
|
|
|
Shared::init();
|
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
Global::exit_error_msg = "Exception in Shared::init() -> " + (string)e.what();
|
2021-08-13 08:25:18 +12:00
|
|
|
exit(1);
|
2021-07-24 12:13:26 +12:00
|
|
|
}
|
2021-06-26 09:58:19 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Update list of available themes and generate the selected theme
|
2021-06-26 09:58:19 +12:00
|
|
|
Theme::updateThemes();
|
|
|
|
Theme::setTheme();
|
2021-05-09 10:18:51 +12:00
|
|
|
|
2021-08-18 08:33:21 +12:00
|
|
|
//? Start runner thread
|
|
|
|
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
|
|
|
Global::exit_error_msg = "Failed to create _runner thread!";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Global::debug) {
|
|
|
|
Runner::debug_bg = Draw::createBox(2, 2, 32, 8, "", true, "debug");
|
|
|
|
}
|
|
|
|
|
2021-05-09 10:18:51 +12:00
|
|
|
//? Create the btop++ banner
|
2021-06-03 07:33:26 +12:00
|
|
|
banner_gen();
|
2021-05-09 10:18:51 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Calculate sizes of all boxes
|
|
|
|
Draw::calcSizes();
|
2021-05-08 12:38:51 +12:00
|
|
|
|
2021-07-30 09:40:56 +12:00
|
|
|
//? Print out box outlines
|
2021-07-21 13:17:34 +12:00
|
|
|
cout << Term::sync_start << Cpu::box << Mem::box << Net::box << Proc::box << Term::sync_end << flush;
|
2021-05-24 08:25:07 +12:00
|
|
|
|
2021-07-05 08:02:31 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? ------------------------------------------------ MAIN LOOP ----------------------------------------------------
|
2021-05-10 08:25:41 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
uint64_t update_ms = Config::getI("update_ms");
|
|
|
|
auto future_time = time_ms();
|
2021-05-18 11:16:22 +12:00
|
|
|
|
2021-07-19 01:44:32 +12:00
|
|
|
try {
|
2021-07-19 04:04:49 +12:00
|
|
|
while (not true not_eq not false) {
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Check for exceptions in secondary thread and exit with fail signal if true
|
2021-08-11 06:20:33 +12:00
|
|
|
if (Global::thread_exception) exit(1);
|
2021-07-19 04:04:49 +12:00
|
|
|
|
|
|
|
//? Make sure terminal size hasn't changed (in case of SIGWINCH not working properly)
|
|
|
|
term_resize();
|
2021-05-28 08:29:36 +12:00
|
|
|
|
2021-08-11 06:20:33 +12:00
|
|
|
//? Trigger secondary thread to redraw if terminal has been resized
|
2021-07-19 04:04:49 +12:00
|
|
|
if (Global::resized) {
|
|
|
|
Global::resized = false;
|
2021-08-11 06:20:33 +12:00
|
|
|
Draw::calcSizes();
|
|
|
|
Runner::run("all", true);
|
|
|
|
atomic_wait(Runner::active);
|
2021-07-19 04:04:49 +12:00
|
|
|
}
|
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Start secondary collect & draw thread at the interval set by <update_ms> config value
|
2021-07-19 01:44:32 +12:00
|
|
|
if (time_ms() >= future_time) {
|
2021-07-19 04:04:49 +12:00
|
|
|
Runner::run("all");
|
2021-07-21 13:17:34 +12:00
|
|
|
update_ms = Config::getI("update_ms");
|
2021-07-19 04:04:49 +12:00
|
|
|
future_time = time_ms() + update_ms;
|
2021-06-14 09:12:11 +12:00
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
|
2021-07-21 13:17:34 +12:00
|
|
|
//? Loop over input polling and input action processing
|
2021-08-11 06:20:33 +12:00
|
|
|
for (auto current_time = time_ms(); current_time < future_time; current_time = time_ms()) {
|
2021-07-21 13:17:34 +12:00
|
|
|
|
2021-08-04 09:47:46 +12:00
|
|
|
//? Check for external clock changes and for changes to the update timer
|
|
|
|
if (update_ms != (uint64_t)Config::getI("update_ms")) {
|
|
|
|
update_ms = Config::getI("update_ms");
|
|
|
|
future_time = time_ms() + update_ms;
|
|
|
|
}
|
|
|
|
else if (future_time - current_time > update_ms)
|
2021-07-19 04:04:49 +12:00
|
|
|
future_time = current_time;
|
2021-07-21 13:17:34 +12:00
|
|
|
|
|
|
|
//? Poll for input and process any input detected
|
|
|
|
else if (Input::poll(min(1000ul, future_time - current_time))) {
|
|
|
|
if (not Runner::active)
|
|
|
|
Config::unlock();
|
2021-07-19 01:44:32 +12:00
|
|
|
Input::process(Input::get());
|
2021-07-21 13:17:34 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
//? Break the loop at 1000ms intervals or if input polling was interrupted
|
2021-07-19 01:44:32 +12:00
|
|
|
else
|
|
|
|
break;
|
2021-06-14 09:12:11 +12:00
|
|
|
}
|
2021-07-19 04:04:49 +12:00
|
|
|
|
2021-07-16 09:49:16 +12:00
|
|
|
}
|
2021-07-19 01:44:32 +12:00
|
|
|
}
|
|
|
|
catch (std::exception& e) {
|
|
|
|
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
|
2021-08-13 08:25:18 +12:00
|
|
|
exit(1);
|
2021-05-13 13:11:29 +12:00
|
|
|
}
|
2021-05-11 09:46:41 +12:00
|
|
|
|
2021-05-07 06:32:03 +12:00
|
|
|
}
|