diff --git a/src/btop.cpp b/src/btop.cpp index 48d0482..e03a353 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -176,7 +176,7 @@ void term_resize(bool force) { static atomic resizing (false); if (Input::polling) { Global::resized = true; - Input::interrupt = true; + Input::interrupt(); return; } atomic_lock lck(resizing, true); @@ -246,7 +246,7 @@ void term_resize(bool force) { else if (not Term::refresh()) break; } - Input::interrupt = true; + Input::interrupt(); } //* Exit handler; stops threads, restores terminal and saves config changes @@ -321,7 +321,7 @@ void _signal_handler(const int sig) { if (Runner::active) { Global::should_quit = true; Runner::stopping = true; - Input::interrupt = true; + Input::interrupt(); } else { clean_quit(0); @@ -331,7 +331,7 @@ void _signal_handler(const int sig) { if (Runner::active) { Global::should_sleep = true; Runner::stopping = true; - Input::interrupt = true; + Input::interrupt(); } else { _sleep(); @@ -343,6 +343,9 @@ void _signal_handler(const int sig) { case SIGWINCH: term_resize(); break; + case SIGUSR1: + // Input::poll interrupt + break; } } @@ -477,7 +480,7 @@ namespace Runner { 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; + Input::interrupt(); stopping = true; } @@ -488,7 +491,7 @@ namespace Runner { if (active) { Global::exit_error_msg = "Runner thread failed to get active lock!"; Global::thread_exception = true; - Input::interrupt = true; + Input::interrupt(); stopping = true; } if (stopping or Global::resized) { @@ -558,7 +561,7 @@ namespace Runner { coreNum_reset = false; Cpu::core_mapping = Cpu::get_core_mapping(); Global::resized = true; - Input::interrupt = true; + Input::interrupt(); continue; } @@ -655,7 +658,7 @@ namespace Runner { catch (const std::exception& e) { Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()}; Global::thread_exception = true; - Input::interrupt = true; + Input::interrupt(); stopping = true; } @@ -1014,6 +1017,12 @@ int main(int argc, char **argv) { std::signal(SIGTSTP, _signal_handler); std::signal(SIGCONT, _signal_handler); std::signal(SIGWINCH, _signal_handler); + std::signal(SIGUSR1, _signal_handler); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &mask, &Input::signal_mask); //? Start runner thread Runner::thread_sem_init(); @@ -1035,9 +1044,10 @@ int main(int argc, char **argv) { { const auto [x, y] = Term::get_min_size(Config::getS("shown_boxes")); if (Term::height < y or Term::width < x) { + pthread_sigmask(SIG_SETMASK, &Input::signal_mask, &mask); term_resize(true); + pthread_sigmask(SIG_SETMASK, &mask, nullptr); Global::resized = false; - Input::interrupt = false; } } diff --git a/src/btop_input.cpp b/src/btop_input.cpp index fb9e46d..fbbae58 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -16,12 +16,13 @@ indent = tab tab-size = 4 */ -#include +#include #include #include #include #include #include +#include #include #include "btop_input.hpp" @@ -31,17 +32,6 @@ tab-size = 4 #include "btop_menu.hpp" #include "btop_draw.hpp" - -#include "btop_input.hpp" -#include "btop_tools.hpp" -#include "btop_config.hpp" -#include "btop_shared.hpp" -#include "btop_menu.hpp" -#include "btop_draw.hpp" - - -using std::cin; - using namespace Tools; using namespace std::literals; // for operator""s namespace rng = std::ranges; @@ -89,83 +79,45 @@ namespace Input { {"[24~", "f12"} }; - std::atomic interrupt (false); + sigset_t signal_mask; std::atomic polling (false); array mouse_pos; std::unordered_map mouse_mappings; deque history(50, ""); string old_filter; + string input; - struct InputThr { - InputThr() : thr(run, this) { - } - - static void run(InputThr* that) { - that->runImpl(); - } - - void runImpl() { - char ch = 0; - - // TODO(pg83): read whole buffer - while (cin.get(ch)) { - std::lock_guard g(lock); - current.push_back(ch); - if (current.size() > 100) { - current.clear(); - } - } - } - - size_t avail() { - std::lock_guard g(lock); - - return current.size(); - } - - std::string get() { - std::string res; - - { - std::lock_guard g(lock); - - res.swap(current); - } - - return res; - } - - static InputThr& instance() { - // intentional memory leak, to simplify shutdown process - static InputThr* input = new InputThr(); - - return *input; - } - - std::string current; - // TODO(pg83): use std::conditional_variable instead of sleep - std::mutex lock; - std::thread thr; - }; - - bool poll(int timeout) { + bool poll(const uint64_t timeout) { atomic_lock lck(polling); - if (timeout < 1) return InputThr::instance().avail() > 0; - while (timeout > 0) { - if (interrupt) { - interrupt = false; - return false; - } - if (InputThr::instance().avail() > 0) return true; - sleep_ms(timeout < 10 ? timeout : 10); - timeout -= 10; + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + struct timespec wait; + struct timespec *waitptr = nullptr; + + if(timeout != std::numeric_limits::max()) { + wait.tv_sec = timeout / 1000; + wait.tv_nsec = (timeout % 1000) * 1000000; + waitptr = &wait; } + + if(pselect(STDIN_FILENO + 1, &fds, nullptr, nullptr, waitptr, &signal_mask) > 0) { + input.clear(); + char buf[1024]; + ssize_t count = 0; + while((count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + input.append(std::string_view(buf, count)); + } + + return true; + } + return false; } string get() { - string key = InputThr::instance().get(); + string key = input; if (not key.empty()) { //? Remove escape code prefix if present if (key.substr(0, 2) == Fx::e) { @@ -238,12 +190,14 @@ namespace Input { } string wait() { - while (InputThr::instance().avail() < 1) { - sleep_ms(10); - } + while(not poll(std::numeric_limits::max())) {} return get(); } + void interrupt() { + kill(getpid(), SIGUSR1); + } + void clear() { // do not need it, actually } diff --git a/src/btop_input.hpp b/src/btop_input.hpp index fc6fda0..13f14fe 100644 --- a/src/btop_input.hpp +++ b/src/btop_input.hpp @@ -29,9 +29,9 @@ using std::atomic; using std::deque; using std::string; -/* The input functions relies on the following std::cin options being set: - cin.sync_with_stdio(false); - cin.tie(nullptr); +/* The input functions rely on the following termios parameters being set: + Non-canonical mode (c_lflags & ~(ICANON)) + VMIN and VTIME (c_cc) set to 0 These will automatically be set when running Term::init() from btop_tools.cpp */ @@ -45,7 +45,9 @@ namespace Input { //? line, col, height, width extern std::unordered_map mouse_mappings; - extern atomic interrupt; + //* Signal mask used during polling read + extern sigset_t signal_mask; + extern atomic polling; //* Mouse column and line position @@ -55,7 +57,7 @@ namespace Input { extern deque history; //* Poll keyboard & mouse input for ms and return input availabilty as a bool - bool poll(int timeout=0); + bool poll(const uint64_t timeout=0); //* Get a key or mouse action from input string get(); @@ -63,6 +65,9 @@ namespace Input { //* Wait until input is available and return key string wait(); + //* Interrupt poll/wait + void interrupt(); + //* Clears last entered key void clear(); diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp index e6ecbd8..62d0e2d 100644 --- a/src/btop_tools.cpp +++ b/src/btop_tools.cpp @@ -37,7 +37,6 @@ tab-size = 4 #include "btop_tools.hpp" #include "btop_config.hpp" -using std::cin; using std::cout; using std::floor; using std::flush; @@ -77,7 +76,11 @@ namespace Term { struct termios settings; if (tcgetattr(STDIN_FILENO, &settings)) return false; if (on) settings.c_lflag |= ICANON; - else settings.c_lflag &= ~(ICANON); + else { + settings.c_lflag &= ~(ICANON); + settings.c_cc[VMIN] = 0; + settings.c_cc[VTIME] = 0; + } if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false; if (on) setlinebuf(stdin); else setbuf(stdin, nullptr); @@ -152,12 +155,10 @@ namespace Term { //? Disable stream sync - this does not seem to work on OpenBSD #ifndef __OpenBSD__ - cin.sync_with_stdio(false); cout.sync_with_stdio(false); #endif //? Disable stream ties - cin.tie(nullptr); cout.tie(nullptr); echo(false); linebuffered(false);