Skip to content

Commit

Permalink
Use native POSIX polling syscalls to read input
Browse files Browse the repository at this point in the history
No more awkward manual polling
  • Loading branch information
lvxnull committed Sep 22, 2023
1 parent 2c3ac48 commit 63289c6
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 84 deletions.
28 changes: 19 additions & 9 deletions src/btop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand All @@ -326,6 +326,9 @@ void _signal_handler(const int sig) {
case SIGWINCH:
term_resize();
break;
case SIGUSR1:
// Input::poll interrupt
break;
}
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
Expand All @@ -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;
}

}
Expand Down
96 changes: 29 additions & 67 deletions src/btop_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ indent = tab
tab-size = 4
*/

#include <iostream>
#include <ranges>
#include <vector>
#include <thread>
#include <mutex>
#include <signal.h>
#include <sys/select.h>

#include "btop_input.hpp"
#include "btop_tools.hpp"
Expand All @@ -30,9 +30,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;
Expand Down Expand Up @@ -76,83 +73,46 @@ 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();
bool poll(int timeout) {
atomic_lock lck(polling);
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
struct timespec wait;
struct timespec *waitptr = nullptr;

if(timeout < 0) timeout = 0;
if(timeout != INT_MAX) {
wait.tv_sec = timeout / 1000;
wait.tv_nsec = (timeout % 1000) * 1000000;
waitptr = &wait;
}

std::string get() {
std::string res;

{
std::lock_guard<std::mutex> g(lock);

res.swap(current);
if(pselect(STDIN_FILENO + 1, &fds, nullptr, nullptr, waitptr, &signal_mask) > 0) {
input.clear();
char buf[1024];
int count = 0;
while((count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
input.append(std::string_view(buf, count));
}

return res;
return true;
}

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);
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;
}
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) {
Expand Down Expand Up @@ -225,12 +185,14 @@ namespace Input {
}

string wait() {
while (InputThr::instance().avail() < 1) {
sleep_ms(10);
}
while(not poll(INT_MAX)) {}
return get();
}

void interrupt() {
kill(getpid(), SIGUSR1);
}

void clear() {
// do not need it, actually
}
Expand Down
13 changes: 9 additions & 4 deletions src/btop_input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand All @@ -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
Expand All @@ -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();

Expand Down
9 changes: 5 additions & 4 deletions src/btop_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 63289c6

Please sign in to comment.