mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-28 02:50:29 +12:00
Use native POSIX polling syscalls to read input
No more awkward manual polling
This commit is contained in:
parent
278a0e6b17
commit
b8e43d92b8
28
src/btop.cpp
28
src/btop.cpp
|
@ -175,7 +175,7 @@ void term_resize(bool force) {
|
|||
static atomic<bool> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@ indent = tab
|
|||
tab-size = 4
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <signal.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#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<bool> interrupt (false);
|
||||
sigset_t signal_mask;
|
||||
std::atomic<bool> polling (false);
|
||||
array<int, 2> mouse_pos;
|
||||
unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
||||
|
||||
deque<string> 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<std::mutex> g(lock);
|
||||
current.push_back(ch);
|
||||
if (current.size() > 100) {
|
||||
current.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t avail() {
|
||||
std::lock_guard<std::mutex> g(lock);
|
||||
|
||||
return current.size();
|
||||
}
|
||||
|
||||
std::string get() {
|
||||
std::string res;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<uint64_t>::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<uint64_t>::max())) {}
|
||||
return get();
|
||||
}
|
||||
|
||||
void interrupt() {
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
// do not need it, actually
|
||||
}
|
||||
|
|
|
@ -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<string, Mouse_loc> mouse_mappings;
|
||||
|
||||
extern atomic<bool> interrupt;
|
||||
//* Signal mask used during polling read
|
||||
extern sigset_t signal_mask;
|
||||
|
||||
extern atomic<bool> polling;
|
||||
|
||||
//* Mouse column and line position
|
||||
|
@ -56,7 +58,7 @@ namespace Input {
|
|||
extern deque<string> history;
|
||||
|
||||
//* Poll keyboard & mouse input for <timeout> 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();
|
||||
|
||||
|
|
|
@ -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<string>(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);
|
||||
|
|
Loading…
Reference in a new issue