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);
|
static atomic<bool> resizing (false);
|
||||||
if (Input::polling) {
|
if (Input::polling) {
|
||||||
Global::resized = true;
|
Global::resized = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
atomic_lock lck(resizing, true);
|
atomic_lock lck(resizing, true);
|
||||||
|
@ -234,7 +234,7 @@ void term_resize(bool force) {
|
||||||
else if (not Term::refresh()) break;
|
else if (not Term::refresh()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Exit handler; stops threads, restores terminal and saves config changes
|
//* Exit handler; stops threads, restores terminal and saves config changes
|
||||||
|
@ -304,7 +304,7 @@ void _signal_handler(const int sig) {
|
||||||
if (Runner::active) {
|
if (Runner::active) {
|
||||||
Global::should_quit = true;
|
Global::should_quit = true;
|
||||||
Runner::stopping = true;
|
Runner::stopping = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
clean_quit(0);
|
clean_quit(0);
|
||||||
|
@ -314,7 +314,7 @@ void _signal_handler(const int sig) {
|
||||||
if (Runner::active) {
|
if (Runner::active) {
|
||||||
Global::should_sleep = true;
|
Global::should_sleep = true;
|
||||||
Runner::stopping = true;
|
Runner::stopping = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_sleep();
|
_sleep();
|
||||||
|
@ -326,6 +326,9 @@ void _signal_handler(const int sig) {
|
||||||
case SIGWINCH:
|
case SIGWINCH:
|
||||||
term_resize();
|
term_resize();
|
||||||
break;
|
break;
|
||||||
|
case SIGUSR1:
|
||||||
|
// Input::poll interrupt
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,7 +454,7 @@ namespace Runner {
|
||||||
if (pt_lck.status != 0) {
|
if (pt_lck.status != 0) {
|
||||||
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
|
Global::exit_error_msg = "Exception in runner thread -> pthread_mutex_lock error id: " + to_string(pt_lck.status);
|
||||||
Global::thread_exception = true;
|
Global::thread_exception = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
stopping = true;
|
stopping = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +465,7 @@ namespace Runner {
|
||||||
if (active) {
|
if (active) {
|
||||||
Global::exit_error_msg = "Runner thread failed to get active lock!";
|
Global::exit_error_msg = "Runner thread failed to get active lock!";
|
||||||
Global::thread_exception = true;
|
Global::thread_exception = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
stopping = true;
|
stopping = true;
|
||||||
}
|
}
|
||||||
if (stopping or Global::resized) {
|
if (stopping or Global::resized) {
|
||||||
|
@ -505,7 +508,7 @@ namespace Runner {
|
||||||
coreNum_reset = false;
|
coreNum_reset = false;
|
||||||
Cpu::core_mapping = Cpu::get_core_mapping();
|
Cpu::core_mapping = Cpu::get_core_mapping();
|
||||||
Global::resized = true;
|
Global::resized = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +587,7 @@ namespace Runner {
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
|
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
|
||||||
Global::thread_exception = true;
|
Global::thread_exception = true;
|
||||||
Input::interrupt = true;
|
Input::interrupt();
|
||||||
stopping = true;
|
stopping = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,6 +940,12 @@ int main(int argc, char **argv) {
|
||||||
std::signal(SIGTSTP, _signal_handler);
|
std::signal(SIGTSTP, _signal_handler);
|
||||||
std::signal(SIGCONT, _signal_handler);
|
std::signal(SIGCONT, _signal_handler);
|
||||||
std::signal(SIGWINCH, _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
|
//? Start runner thread
|
||||||
Runner::thread_sem_init();
|
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"));
|
const auto [x, y] = Term::get_min_size(Config::getS("shown_boxes"));
|
||||||
if (Term::height < y or Term::width < x) {
|
if (Term::height < y or Term::width < x) {
|
||||||
|
pthread_sigmask(SIG_SETMASK, &Input::signal_mask, &mask);
|
||||||
term_resize(true);
|
term_resize(true);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &mask, nullptr);
|
||||||
Global::resized = false;
|
Global::resized = false;
|
||||||
Input::interrupt = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,13 @@ indent = tab
|
||||||
tab-size = 4
|
tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <limits>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
#include "btop_input.hpp"
|
#include "btop_input.hpp"
|
||||||
#include "btop_tools.hpp"
|
#include "btop_tools.hpp"
|
||||||
|
@ -30,9 +31,6 @@ tab-size = 4
|
||||||
#include "btop_menu.hpp"
|
#include "btop_menu.hpp"
|
||||||
#include "btop_draw.hpp"
|
#include "btop_draw.hpp"
|
||||||
|
|
||||||
|
|
||||||
using std::cin;
|
|
||||||
|
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
using namespace std::literals; // for operator""s
|
using namespace std::literals; // for operator""s
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
@ -76,83 +74,45 @@ namespace Input {
|
||||||
{"[24~", "f12"}
|
{"[24~", "f12"}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::atomic<bool> interrupt (false);
|
sigset_t signal_mask;
|
||||||
std::atomic<bool> polling (false);
|
std::atomic<bool> polling (false);
|
||||||
array<int, 2> mouse_pos;
|
array<int, 2> mouse_pos;
|
||||||
unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
||||||
|
|
||||||
deque<string> history(50, "");
|
deque<string> history(50, "");
|
||||||
string old_filter;
|
string old_filter;
|
||||||
|
string input;
|
||||||
|
|
||||||
struct InputThr {
|
bool poll(const uint64_t timeout) {
|
||||||
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) {
|
|
||||||
atomic_lock lck(polling);
|
atomic_lock lck(polling);
|
||||||
if (timeout < 1) return InputThr::instance().avail() > 0;
|
fd_set fds;
|
||||||
while (timeout > 0) {
|
FD_ZERO(&fds);
|
||||||
if (interrupt) {
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
interrupt = false;
|
struct timespec wait;
|
||||||
return false;
|
struct timespec *waitptr = nullptr;
|
||||||
}
|
|
||||||
if (InputThr::instance().avail() > 0) return true;
|
if(timeout != std::numeric_limits<uint64_t>::max()) {
|
||||||
sleep_ms(timeout < 10 ? timeout : 10);
|
wait.tv_sec = timeout / 1000;
|
||||||
timeout -= 10;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string get() {
|
string get() {
|
||||||
string key = InputThr::instance().get();
|
string key = input;
|
||||||
if (not key.empty()) {
|
if (not key.empty()) {
|
||||||
//? Remove escape code prefix if present
|
//? Remove escape code prefix if present
|
||||||
if (key.substr(0, 2) == Fx::e) {
|
if (key.substr(0, 2) == Fx::e) {
|
||||||
|
@ -225,12 +185,14 @@ namespace Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
string wait() {
|
string wait() {
|
||||||
while (InputThr::instance().avail() < 1) {
|
while(not poll(std::numeric_limits<uint64_t>::max())) {}
|
||||||
sleep_ms(10);
|
|
||||||
}
|
|
||||||
return get();
|
return get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void interrupt() {
|
||||||
|
kill(getpid(), SIGUSR1);
|
||||||
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
// do not need it, actually
|
// do not need it, actually
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,9 @@ using std::atomic;
|
||||||
using std::deque;
|
using std::deque;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
/* The input functions relies on the following std::cin options being set:
|
/* The input functions rely on the following termios parameters being set:
|
||||||
cin.sync_with_stdio(false);
|
Non-canonical mode (c_lflags & ~(ICANON))
|
||||||
cin.tie(nullptr);
|
VMIN and VTIME (c_cc) set to 0
|
||||||
These will automatically be set when running Term::init() from btop_tools.cpp
|
These will automatically be set when running Term::init() from btop_tools.cpp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -46,7 +46,9 @@ namespace Input {
|
||||||
//? line, col, height, width
|
//? line, col, height, width
|
||||||
extern unordered_flat_map<string, Mouse_loc> mouse_mappings;
|
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;
|
extern atomic<bool> polling;
|
||||||
|
|
||||||
//* Mouse column and line position
|
//* Mouse column and line position
|
||||||
|
@ -56,7 +58,7 @@ namespace Input {
|
||||||
extern deque<string> history;
|
extern deque<string> history;
|
||||||
|
|
||||||
//* Poll keyboard & mouse input for <timeout> ms and return input availabilty as a bool
|
//* 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
|
//* Get a key or mouse action from input
|
||||||
string get();
|
string get();
|
||||||
|
@ -64,6 +66,9 @@ namespace Input {
|
||||||
//* Wait until input is available and return key
|
//* Wait until input is available and return key
|
||||||
string wait();
|
string wait();
|
||||||
|
|
||||||
|
//* Interrupt poll/wait
|
||||||
|
void interrupt();
|
||||||
|
|
||||||
//* Clears last entered key
|
//* Clears last entered key
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ tab-size = 4
|
||||||
#include "btop_tools.hpp"
|
#include "btop_tools.hpp"
|
||||||
#include "btop_config.hpp"
|
#include "btop_config.hpp"
|
||||||
|
|
||||||
using std::cin;
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::floor;
|
using std::floor;
|
||||||
using std::flush;
|
using std::flush;
|
||||||
|
@ -77,7 +76,11 @@ namespace Term {
|
||||||
struct termios settings;
|
struct termios settings;
|
||||||
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
if (tcgetattr(STDIN_FILENO, &settings)) return false;
|
||||||
if (on) settings.c_lflag |= ICANON;
|
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 (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
||||||
if (on) setlinebuf(stdin);
|
if (on) setlinebuf(stdin);
|
||||||
else setbuf(stdin, nullptr);
|
else setbuf(stdin, nullptr);
|
||||||
|
@ -124,11 +127,9 @@ namespace Term {
|
||||||
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
|
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
|
||||||
|
|
||||||
//? Disable stream sync
|
//? Disable stream sync
|
||||||
cin.sync_with_stdio(false);
|
|
||||||
cout.sync_with_stdio(false);
|
cout.sync_with_stdio(false);
|
||||||
|
|
||||||
//? Disable stream ties
|
//? Disable stream ties
|
||||||
cin.tie(nullptr);
|
|
||||||
cout.tie(nullptr);
|
cout.tie(nullptr);
|
||||||
echo(false);
|
echo(false);
|
||||||
linebuffered(false);
|
linebuffered(false);
|
||||||
|
|
Loading…
Reference in a new issue