From b8e43d92b89e8e3de66d233777df81629b7274af Mon Sep 17 00:00:00 2001 From: lvxnull <86745229+lvxnull@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:40:18 +0200 Subject: [PATCH] Use native POSIX polling syscalls to read input No more awkward manual polling --- src/btop.cpp | 28 ++++++++---- src/btop_input.cpp | 104 ++++++++++++++------------------------------- src/btop_input.hpp | 15 ++++--- src/btop_tools.cpp | 9 ++-- 4 files changed, 67 insertions(+), 89 deletions(-) diff --git a/src/btop.cpp b/src/btop.cpp index e53ec02..fd75822 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -175,7 +175,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); @@ -234,7 +234,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 @@ -304,7 +304,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); @@ -314,7 +314,7 @@ void _signal_handler(const int sig) { if (Runner::active) { Global::should_sleep = true; Runner::stopping = true; - Input::interrupt = true; + Input::interrupt(); } else { _sleep(); @@ -326,6 +326,9 @@ void _signal_handler(const int sig) { case SIGWINCH: term_resize(); break; + case SIGUSR1: + // Input::poll interrupt + break; } } @@ -451,7 +454,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; } @@ -462,7 +465,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) { @@ -505,7 +508,7 @@ namespace Runner { coreNum_reset = false; Cpu::core_mapping = Cpu::get_core_mapping(); Global::resized = true; - Input::interrupt = true; + Input::interrupt(); continue; } @@ -584,7 +587,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; } @@ -937,6 +940,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(); @@ -958,9 +967,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 ebdee3b..0fcb51c 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 "btop_input.hpp" #include "btop_tools.hpp" @@ -30,9 +31,6 @@ tab-size = 4 #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; @@ -76,83 +74,45 @@ namespace Input { {"[24~", "f12"} }; - std::atomic interrupt (false); + sigset_t signal_mask; std::atomic polling (false); array mouse_pos; unordered_flat_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) { @@ -225,12 +185,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 5a1a28e..5af8b28 100644 --- a/src/btop_input.hpp +++ b/src/btop_input.hpp @@ -30,9 +30,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 */ @@ -46,7 +46,9 @@ namespace Input { //? line, col, height, width extern unordered_flat_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 @@ -56,7 +58,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(); @@ -64,6 +66,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 f6be174..4a74107 100644 --- a/src/btop_tools.cpp +++ b/src/btop_tools.cpp @@ -36,7 +36,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); @@ -124,11 +127,9 @@ namespace Term { current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast(ttyname(STDIN_FILENO)) : "unknown"); //? Disable stream sync - cin.sync_with_stdio(false); cout.sync_with_stdio(false); //? Disable stream ties - cin.tie(nullptr); cout.tie(nullptr); echo(false); linebuffered(false);