2016-05-02 10:54:25 +08:00
|
|
|
// Implementation file for the low level input library.
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
2019-10-14 06:50:48 +08:00
|
|
|
|
2019-05-05 18:09:25 +08:00
|
|
|
#include <cstring>
|
2006-08-11 03:02:46 +08:00
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
2016-04-21 14:00:54 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2016-05-07 12:22:28 +08:00
|
|
|
#include <sys/time.h>
|
2016-05-02 10:54:25 +08:00
|
|
|
#include <sys/types.h>
|
2017-02-11 10:47:02 +08:00
|
|
|
|
2019-10-14 06:50:48 +08:00
|
|
|
#include <cwchar>
|
2017-02-11 10:47:02 +08:00
|
|
|
#include <deque>
|
|
|
|
#include <list>
|
2016-05-02 10:54:25 +08:00
|
|
|
#include <memory>
|
2017-02-11 10:47:02 +08:00
|
|
|
#include <type_traits>
|
2018-02-19 10:39:03 +08:00
|
|
|
#include <utility>
|
2006-01-19 20:22:07 +08:00
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "common.h"
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "env.h"
|
2016-05-02 10:54:25 +08:00
|
|
|
#include "env_universal_common.h"
|
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2020-01-19 21:27:23 +08:00
|
|
|
#include "flog.h"
|
2019-05-05 08:32:40 +08:00
|
|
|
#include "global_safety.h"
|
2016-05-02 10:54:25 +08:00
|
|
|
#include "input_common.h"
|
2011-12-27 13:05:25 +08:00
|
|
|
#include "iothread.h"
|
2016-11-23 12:24:03 +08:00
|
|
|
#include "wutil.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-07 12:22:28 +08:00
|
|
|
/// Time in milliseconds to wait for another byte to be available for reading
|
2018-06-18 13:01:32 +08:00
|
|
|
/// after \x1B is read before assuming that escape key was pressed, and not an
|
2016-05-07 12:22:28 +08:00
|
|
|
/// escape sequence.
|
2019-02-07 19:08:24 +08:00
|
|
|
#define WAIT_ON_ESCAPE_DEFAULT 30
|
2016-01-15 13:46:53 +08:00
|
|
|
static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT;
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2021-04-11 10:49:56 +08:00
|
|
|
input_event_queue_t::input_event_queue_t(int in) : in_(in) {}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
/// Internal function used by readch to read one byte.
|
|
|
|
/// This calls select() on three fds: input (e.g. stdin), the ioport notifier fd (for main thread
|
|
|
|
/// requests), and the uvar notifier. This returns either the byte which was read, or one of the
|
|
|
|
/// special values below.
|
|
|
|
enum {
|
|
|
|
// The in fd has been closed.
|
|
|
|
readb_eof = -1,
|
|
|
|
|
|
|
|
// select() was interrupted by a signal.
|
|
|
|
readb_interrupted = -2,
|
|
|
|
|
|
|
|
// Our uvar notifier reported a change (either through poll() or its fd).
|
|
|
|
readb_uvar_notified = -3,
|
|
|
|
|
|
|
|
// Our ioport reported a change, so service main thread requests.
|
|
|
|
readb_ioport_notified = -4,
|
|
|
|
};
|
|
|
|
using readb_result_t = int;
|
|
|
|
|
2021-07-03 19:57:21 +08:00
|
|
|
static readb_result_t readb(int in_fd) {
|
2021-04-11 08:34:28 +08:00
|
|
|
assert(in_fd >= 0 && "Invalid in fd");
|
|
|
|
universal_notifier_t& notifier = universal_notifier_t::default_notifier();
|
2022-01-03 07:25:12 +08:00
|
|
|
fd_readable_set_t fdset;
|
2019-04-29 13:08:40 +08:00
|
|
|
for (;;) {
|
2021-04-11 07:45:26 +08:00
|
|
|
fdset.clear();
|
2021-04-11 08:34:28 +08:00
|
|
|
fdset.add(in_fd);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
// Add the completion ioport.
|
|
|
|
int ioport_fd = iothread_port();
|
|
|
|
fdset.add(ioport_fd);
|
2016-05-02 10:54:25 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
// Get the uvar notifier fd (possibly none).
|
2014-05-01 06:50:03 +08:00
|
|
|
int notifier_fd = notifier.notification_fd();
|
2021-04-11 08:34:28 +08:00
|
|
|
fdset.add(notifier_fd);
|
2016-05-02 10:54:25 +08:00
|
|
|
|
|
|
|
// Get its suggested delay (possibly none).
|
2021-04-11 08:34:28 +08:00
|
|
|
// Note a 0 here means do not poll.
|
2022-01-03 07:25:12 +08:00
|
|
|
uint64_t timeout = fd_readable_set_t::kNoTimeout;
|
2021-04-11 08:34:28 +08:00
|
|
|
if (uint64_t usecs_delay = notifier.usec_delay_between_polls()) {
|
|
|
|
timeout = usecs_delay;
|
2014-04-30 08:03:00 +08:00
|
|
|
}
|
2021-04-11 08:34:28 +08:00
|
|
|
|
|
|
|
// Here's where we call select().
|
2022-01-03 07:25:12 +08:00
|
|
|
int select_res = fdset.check_readable(timeout);
|
2021-04-11 08:34:28 +08:00
|
|
|
if (select_res < 0) {
|
2016-10-23 11:32:25 +08:00
|
|
|
if (errno == EINTR || errno == EAGAIN) {
|
2021-04-11 08:34:28 +08:00
|
|
|
// A signal.
|
|
|
|
return readb_interrupted;
|
2016-10-23 11:32:25 +08:00
|
|
|
} else {
|
2021-04-11 08:34:28 +08:00
|
|
|
// Some fd was invalid, so probably the tty has been closed.
|
|
|
|
return readb_eof;
|
2014-04-30 08:03:00 +08:00
|
|
|
}
|
2021-04-11 08:34:28 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
// select() did not return an error, so we may have a readable fd.
|
|
|
|
// The priority order is: uvars, stdin, ioport.
|
|
|
|
// Check to see if we want a universal variable barrier.
|
|
|
|
// This may come about through readability, or through a call to poll().
|
2021-05-04 02:51:00 +08:00
|
|
|
if ((fdset.test(notifier_fd) && notifier.notification_fd_became_readable(notifier_fd)) ||
|
|
|
|
notifier.poll()) {
|
2021-07-19 00:35:47 +08:00
|
|
|
return readb_uvar_notified;
|
2021-04-11 08:34:28 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
// Check stdin.
|
|
|
|
if (fdset.test(in_fd)) {
|
|
|
|
unsigned char arr[1];
|
|
|
|
if (read_blocked(in_fd, arr, 1) != 1) {
|
|
|
|
// The terminal has been closed.
|
|
|
|
return readb_eof;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2021-04-11 08:34:28 +08:00
|
|
|
// The common path is to return a (non-negative) char.
|
|
|
|
return static_cast<int>(arr[0]);
|
|
|
|
}
|
2019-06-24 07:19:13 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
// Check for iothread completions only if there is no data to be read from the stdin.
|
|
|
|
// This gives priority to the foreground.
|
|
|
|
if (fdset.test(ioport_fd)) {
|
|
|
|
return readb_ioport_notified;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2019-04-29 13:08:40 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-02 10:54:25 +08:00
|
|
|
// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being
|
|
|
|
// set.
|
2020-01-17 07:14:21 +08:00
|
|
|
void update_wait_on_escape_ms(const environment_t& vars) {
|
2018-09-18 12:26:21 +08:00
|
|
|
auto escape_time_ms = vars.get(L"fish_escape_delay_ms");
|
2016-05-02 10:54:25 +08:00
|
|
|
if (escape_time_ms.missing_or_empty()) {
|
2016-01-15 13:46:53 +08:00
|
|
|
wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-28 15:25:41 +08:00
|
|
|
long tmp = fish_wcstol(escape_time_ms->as_string().c_str());
|
2016-11-23 12:24:03 +08:00
|
|
|
if (errno || tmp < 10 || tmp >= 5000) {
|
2019-03-13 05:06:01 +08:00
|
|
|
std::fwprintf(stderr,
|
2019-05-05 18:09:25 +08:00
|
|
|
L"ignoring fish_escape_delay_ms: value '%ls' "
|
|
|
|
L"is not an integer or is < 10 or >= 5000 ms\n",
|
|
|
|
escape_time_ms->as_string().c_str());
|
2016-05-02 10:54:25 +08:00
|
|
|
} else {
|
2019-11-19 09:08:16 +08:00
|
|
|
wait_on_escape_ms = static_cast<int>(tmp);
|
2016-01-15 13:46:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 04:40:53 +08:00
|
|
|
maybe_t<char_event_t> input_event_queue_t::try_pop() {
|
|
|
|
if (queue_.empty()) {
|
|
|
|
return none();
|
|
|
|
}
|
|
|
|
auto result = std::move(queue_.front());
|
2019-06-03 06:41:23 +08:00
|
|
|
queue_.pop_front();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
char_event_t input_event_queue_t::readch() {
|
2019-05-05 08:32:40 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2021-04-11 08:34:28 +08:00
|
|
|
wchar_t res{};
|
2019-03-15 16:11:15 +08:00
|
|
|
mbstate_t state = {};
|
2021-04-11 08:34:28 +08:00
|
|
|
for (;;) {
|
|
|
|
// Do we have something enqueued already?
|
|
|
|
// Note this may be initially true, or it may become true through calls to
|
|
|
|
// iothread_service_main() or env_universal_barrier() below.
|
|
|
|
if (auto mevt = try_pop()) {
|
|
|
|
return mevt.acquire();
|
2019-03-17 03:35:49 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 11:41:33 +08:00
|
|
|
// We are going to block; but first allow any override to inject events.
|
|
|
|
this->prepare_to_select();
|
|
|
|
if (auto mevt = try_pop()) {
|
|
|
|
return mevt.acquire();
|
|
|
|
}
|
|
|
|
|
2021-07-03 19:57:21 +08:00
|
|
|
readb_result_t rr = readb(in_);
|
2021-04-11 08:34:28 +08:00
|
|
|
switch (rr) {
|
|
|
|
case readb_eof:
|
|
|
|
return char_event_type_t::eof;
|
|
|
|
|
|
|
|
case readb_interrupted:
|
|
|
|
// FIXME: here signals may break multibyte sequences.
|
2021-04-11 10:49:56 +08:00
|
|
|
this->select_interrupted();
|
2021-04-11 08:34:28 +08:00
|
|
|
break;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
case readb_uvar_notified:
|
2021-07-03 19:57:21 +08:00
|
|
|
env_universal_barrier();
|
2021-04-11 08:34:28 +08:00
|
|
|
break;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2021-04-11 08:34:28 +08:00
|
|
|
case readb_ioport_notified:
|
|
|
|
iothread_service_main();
|
2019-03-15 16:11:15 +08:00
|
|
|
break;
|
2021-04-11 08:34:28 +08:00
|
|
|
|
2019-05-05 18:09:25 +08:00
|
|
|
default: {
|
2021-04-11 08:34:28 +08:00
|
|
|
assert(rr >= 0 && rr <= UCHAR_MAX &&
|
|
|
|
"Read byte out of bounds - missing error case?");
|
|
|
|
char read_byte = static_cast<char>(static_cast<unsigned char>(rr));
|
|
|
|
if (MB_CUR_MAX == 1) {
|
|
|
|
// single-byte locale, all values are legal
|
|
|
|
res = read_byte;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
size_t sz = std::mbrtowc(&res, &read_byte, 1, &state);
|
|
|
|
switch (sz) {
|
|
|
|
case static_cast<size_t>(-1):
|
|
|
|
std::memset(&state, '\0', sizeof(state));
|
|
|
|
FLOG(reader, L"Illegal input");
|
|
|
|
return char_event_type_t::check_exit;
|
|
|
|
|
|
|
|
case static_cast<size_t>(-2):
|
|
|
|
// Sequence not yet complete.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
// Actual nul char.
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Sequence complete.
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
break;
|
2019-05-05 18:09:25 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2019-03-15 16:11:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 04:40:53 +08:00
|
|
|
maybe_t<char_event_t> input_event_queue_t::readch_timed() {
|
|
|
|
if (auto evt = try_pop()) {
|
|
|
|
return evt;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2021-04-11 04:40:53 +08:00
|
|
|
const uint64_t usec_per_msec = 1000;
|
|
|
|
uint64_t timeout_usec = static_cast<uint64_t>(wait_on_escape_ms) * usec_per_msec;
|
2022-01-03 07:25:12 +08:00
|
|
|
if (fd_readable_set_t::is_fd_readable(in_, timeout_usec)) {
|
2021-04-11 04:40:53 +08:00
|
|
|
return readch();
|
2019-03-15 16:11:15 +08:00
|
|
|
}
|
2021-04-11 04:40:53 +08:00
|
|
|
return none();
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2019-12-27 13:54:21 +08:00
|
|
|
void input_event_queue_t::push_back(const char_event_t& ch) { queue_.push_back(ch); }
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2019-12-27 13:54:21 +08:00
|
|
|
void input_event_queue_t::push_front(const char_event_t& ch) { queue_.push_front(ch); }
|
2021-04-11 10:49:56 +08:00
|
|
|
|
2021-04-11 11:41:33 +08:00
|
|
|
void input_event_queue_t::prepare_to_select() {}
|
2021-04-11 10:49:56 +08:00
|
|
|
void input_event_queue_t::select_interrupted() {}
|
|
|
|
input_event_queue_t::~input_event_queue_t() = default;
|