Merge branch 'disable_mouse_tracking'

This commit is contained in:
Mahmoud Al-Qudsi 2021-02-06 17:25:36 -06:00
commit ae1c53cc19
4 changed files with 135 additions and 13 deletions

View File

@ -159,6 +159,7 @@ static const input_function_metadata_t input_function_metadata[] = {
{readline_cmd_t::redo, L"redo"},
{readline_cmd_t::begin_undo_group, L"begin-undo-group"},
{readline_cmd_t::end_undo_group, L"end-undo-group"},
{readline_cmd_t::disable_mouse_tracking, L"disable-mouse-tracking"},
};
static_assert(sizeof(input_function_metadata) / sizeof(input_function_metadata[0]) ==
@ -383,10 +384,7 @@ void inputter_t::mapping_execute(const input_mapping_t &m,
if (has_commands && !command_handler) {
// We don't want to run commands yet. Put the characters back and return check_exit.
for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end;
++it) {
event_queue_.push_front(*it);
}
event_queue_.insert_front(m.seq.cbegin(), m.seq.cend());
event_queue_.push_front(char_event_type_t::check_exit);
return; // skip the input_set_bind_mode
} else if (has_functions && !has_commands) {
@ -424,7 +422,7 @@ bool inputter_t::mapping_is_match(const input_mapping_t &m) {
auto evt = timed ? event_queue_.readch_timed() : event_queue_.readch();
if (!evt.is_char() || evt.get_char() != str[i]) {
// We didn't match the bind sequence/input mapping, (it timed out or they entered
// something else) Undo consumption of the read characters since we didn't match the
// something else). Undo consumption of the read characters since we didn't match the
// bind sequence and abort.
event_queue_.push_front(evt);
while (i--) event_queue_.push_front(str[i]);
@ -470,8 +468,108 @@ maybe_t<input_mapping_t> inputter_t::find_mapping() {
return generic ? maybe_t<input_mapping_t>(*generic) : none();
}
template <size_t N = 16>
class event_queue_peeker_t {
private:
input_event_queue_t &event_queue_;
std::array<char_event_t, N> peeked_;
size_t count = 0;
bool consumed_ = false;
public:
event_queue_peeker_t(input_event_queue_t &event_queue)
: event_queue_(event_queue) {
}
char_event_t next(bool timed = false) {
assert(count < N && "Insufficient backing array size!");
auto event = timed ? event_queue_.readch_timed() : event_queue_.readch();
peeked_[count++] = event;
return event;
}
size_t len() {
return count;
}
void consume() {
consumed_ = true;
}
void restart() {
if (count > 0) {
event_queue_.insert_front(peeked_.cbegin(), peeked_.cbegin() + count);
count = 0;
}
}
~event_queue_peeker_t() {
if (!consumed_) {
restart();
}
}
};
bool inputter_t::have_mouse_tracking_csi() {
event_queue_peeker_t<12> peeker(event_queue_);
// Check for the CSI first
if (peeker.next().maybe_char() != L'\x1B'
|| peeker.next(true /* timed */).maybe_char() != L'[') {
return false;
}
auto next = peeker.next().maybe_char();
size_t length = 0;
if (next == L'M') {
// Generic X10 or modified VT200 sequence. It doesn't matter which, they're both 6 chars
// reporting the button that was clicked and its location.
length = 6;
} else if (next == L'P') {
// VT200 mouse highlighting. 12 characters, comes after generic button press event.
length = 12;
} else if (next == L't') {
// VT200 button released in mouse highlighting mode at valid text location. 5 chars.
length = 5;
} else if (next == L'T') {
// VT200 button released in mouse highlighting mode past end-of-line. 9 characters.
length = 9;
} else {
return false;
}
// Consume however many characters it takes to prevent the mouse tracking sequence from reaching
// the prompt, dependent on the class of mouse reporting as detected above.
peeker.consume();
while (peeker.len() != length) {
auto _ = peeker.next();
}
return true;
}
void inputter_t::mapping_execute_matching_or_generic(const command_handler_t &command_handler) {
if (auto mapping = find_mapping()) {
// Check for mouse-tracking CSI before mappings to prevent the generic mapping handler from
// taking over.
if (have_mouse_tracking_csi()) {
// fish recognizes but does not actually support mouse reporting. We never turn it on, and
// it's only ever enabled if a program we spawned enabled it and crashed or forgot to turn
// it off before exiting. We swallow the events to prevent garbage from piling up at the
// prompt, but don't do anything further with the received codes. To prevent this from
// breaking user interaction with the tty emulator, wasting CPU, and adding latency to the
// event queue, we turn off mouse reporting here.
//
// Since this is only called when we detect an incoming mouse reporting payload, we know the
// terminal emulator supports the xterm ANSI extensions for mouse reporting and can safely
// issue this without worrying about termcap.
FLOGF(reader, "Disabling mouse tracking");
// We can't/shouldn't directly manipulate stdout from `input.cpp`, so request the execution
// of a helper function to disable mouse tracking.
// writembs(outputter_t::stdoutput(), "\x1B[?1000l");
event_queue_.push_front(char_event_t(readline_cmd_t::disable_mouse_tracking, L""));
}
else if (auto mapping = find_mapping()) {
mapping_execute(*mapping, command_handler);
} else {
FLOGF(reader, L"no generic found, ignoring char...");
@ -496,10 +594,8 @@ char_event_t inputter_t::read_characters_no_readline() {
break;
}
}
// Restore any readline functions, in reverse to preserve their original order.
for (auto iter = saved_events.rbegin(); iter != saved_events.rend(); ++iter) {
event_queue_.push_front(*iter);
}
// Restore any readline functions
event_queue_.insert_front(saved_events.cbegin(), saved_events.cend());
return evt_to_return;
}
@ -516,9 +612,7 @@ char_event_t inputter_t::readch(const command_handler_t &command_handler) {
case readline_cmd_t::self_insert_notfirst: {
// Typically self-insert is generated by the generic (empty) binding.
// However if it is generated by a real sequence, then insert that sequence.
for (auto iter = evt.seq.crbegin(); iter != evt.seq.crend(); ++iter) {
event_queue_.push_front(*iter);
}
event_queue_.insert_front(evt.seq.cbegin(), evt.seq.cend());
// Issue #1595: ensure we only insert characters, not readline functions. The
// common case is that this will be empty.
char_event_t res = read_characters_no_readline();

View File

@ -68,6 +68,7 @@ class inputter_t {
void mapping_execute(const input_mapping_t &m, const command_handler_t &command_handler);
void mapping_execute_matching_or_generic(const command_handler_t &command_handler);
bool mapping_is_match(const input_mapping_t &m);
bool have_mouse_tracking_csi();
maybe_t<input_mapping_t> find_mapping();
char_event_t read_characters_no_readline();
};

View File

@ -83,6 +83,7 @@ enum class readline_cmd_t {
begin_undo_group,
end_undo_group,
repeat_jump,
disable_mouse_tracking,
// NOTE: This one has to be last.
reverse_repeat_jump
};
@ -107,6 +108,9 @@ enum class char_event_type_t : uint8_t {
/// An event was handled internally, or an interrupt was received. Check to see if the reader
/// loop should exit.
check_exit,
/// There is no event. This should never happen, or is an assertion failure.
none,
};
/// Hackish: the input style, which describes how char events (only) are applied to the command
@ -154,11 +158,21 @@ class char_event_t {
return v_.c;
}
maybe_t<wchar_t> maybe_char() const {
if (type == char_event_type_t::charc) {
return v_.c;
} else {
return none();
}
}
readline_cmd_t get_readline() const {
assert(type == char_event_type_t::readline && "Not a readline type");
return v_.rl;
}
explicit char_event_t() : type(char_event_type_t::none) { }
/* implicit */ char_event_t(wchar_t c) : type(char_event_type_t::charc) { v_.c = c; }
/* implicit */ char_event_t(readline_cmd_t rl, wcstring seq = {})
@ -221,6 +235,14 @@ class input_event_queue_t {
/// Add a character or a readline function to the front of the queue of unread characters. This
/// will be the next character returned by readch.
void push_front(const char_event_t& ch);
/// Add multiple characters or readline events to the front of the queue of unread characters.
/// The order of the provided events is not changed, i.e. they are not inserted in reverse
/// order.
template<typename Iterator>
void insert_front(const Iterator begin, const Iterator end) {
queue_.insert(queue_.begin(), begin, end);
}
};
#endif

View File

@ -3781,6 +3781,11 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat
el->end_edit_group();
break;
}
case rl::disable_mouse_tracking: {
outputter_t &outp = outputter_t::stdoutput();
outp.writestr(L"\x1B[?1000l");
break;
}
// Some commands should have been handled internally by inputter_t::readch().
case rl::self_insert:
case rl::self_insert_notfirst: