Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use native POSIX polling syscalls to read input #624

Merged
merged 2 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 19 additions & 9 deletions src/btop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,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 @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand All @@ -343,6 +343,9 @@ void _signal_handler(const int sig) {
case SIGWINCH:
term_resize();
break;
case SIGUSR1:
// Input::poll interrupt
break;
}
}

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

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

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

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

}
Expand Down
104 changes: 29 additions & 75 deletions src/btop_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <utility>

#include "btop_input.hpp"
Expand All @@ -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;
Expand Down Expand Up @@ -89,83 +79,45 @@ namespace Input {
{"[24~", "f12"}
};

std::atomic<bool> interrupt (false);
sigset_t signal_mask;
std::atomic<bool> polling (false);
array<int, 2> mouse_pos;
std::unordered_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(const uint64_t 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 != std::numeric_limits<uint64_t>::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];
ssize_t count = 0;
while((count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
input.append(std::string_view(buf, count));
}

return res;
}

static InputThr& instance() {
// intentional memory leak, to simplify shutdown process
static InputThr* input = new InputThr();

return *input;
return true;
}

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 @@ -238,12 +190,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
}
Expand Down
15 changes: 10 additions & 5 deletions src/btop_input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand All @@ -45,7 +45,9 @@ namespace Input {
//? line, col, height, width
extern std::unordered_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 @@ -55,14 +57,17 @@ 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();

//* 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 @@ -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;
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 @@ -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);
Expand Down