Fix parsing of keys that are prefix of others. (#58)

The ESC key generates sequences that are prefix of others. For instance:
- ESC => [27]
- F1 =>  [27, 79, 8]

As a result, we can't generate the ESC event when receiving [27],
because it might be the start of the [27, 79, 8] sequence (or not).

Application usually applies a timeout to help detecting the ESC key.
This patch introduce a timeout. It is set to 50ms.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/55
This commit is contained in:
Arthur Sonzogni
2020-10-25 01:57:56 +02:00
committed by GitHub
parent c13621d1f9
commit 406355df8c
10 changed files with 311 additions and 209 deletions

View File

@@ -1,106 +1,10 @@
#include "ftxui/component/event.hpp"
#include <iostream>
#include "ftxui/screen/string.hpp"
namespace ftxui {
namespace {
void ParseUTF8(Receiver<char>& in, Sender<Event>& out, std::string& input) {
char c;
unsigned char head = static_cast<unsigned char>(input[0]);
for (int i = 0; i < 3; ++i, head <<= 1) {
if ((head & 0b11000000) != 0b11000000)
break;
if (!in->Receive(&c))
return;
input += c;
}
out->Send(Event::Character(input));
}
void ParseCSI(Receiver<char>& in, Sender<Event>& out, std::string& input) {
char c;
while (1) {
if (!in->Receive(&c))
return;
input += c;
if (c >= '0' && c <= '9')
continue;
if (c == ';')
continue;
if (c >= ' ' && c <= '~')
return out->Send(Event::Special(input));
// Invalid ESC in CSI.
if (c == '\x1B')
return out->Send(Event::Special(input));
}
}
void ParseDCS(Receiver<char>& in, Sender<Event>& out, std::string& input) {
char c;
// Parse until the string terminator ST.
while (1) {
if (!in->Receive(&c))
return;
input += c;
if (input.back() != '\x1B')
continue;
if (!in->Receive(&c))
return;
input += c;
if (input.back() != '\\')
continue;
return out->Send(Event::Special(input));
}
}
void ParseOSC(Receiver<char>& in, Sender<Event>& out, std::string& input) {
char c;
// Parse until the string terminator ST.
while (1) {
if (!in->Receive(&c))
return;
input += c;
if (input.back() != '\x1B')
continue;
if (!in->Receive(&c))
return;
input += c;
if (input.back() != '\\')
continue;
return out->Send(Event::Special(input));
}
}
void ParseESC(Receiver<char>& in, Sender<Event>& out, std::string& input) {
char c;
if (!in->Receive(&c))
return;
input += c;
switch (c) {
case 'P':
return ParseDCS(in, out, input);
case '[':
return ParseCSI(in, out, input);
case ']':
return ParseOSC(in, out, input);
default:
if (!in->Receive(&c))
return;
input += c;
out->Send(Event::Special(input));
}
}
} // namespace
// static
Event Event::Character(const std::string& input) {
Event event;
@@ -131,30 +35,6 @@ Event Event::Special(const std::string& input) {
return event;
}
// static
void Event::Convert(Receiver<char>& in, Sender<Event>& out, char c) {
std::string input;
input += c;
unsigned char head = input[0];
switch (head) {
case 24: // CAN
case 26: // SUB
return;
case '\x1B':
return ParseESC(in, out, input);
}
if (head < 32) // C0
return out->Send(Event::Special(input));
if (head == 127) // Delete
return out->Send(Event::Special(input));
return ParseUTF8(in, out, input);
}
// --- Arrow ---
const Event Event::ArrowLeft = Event::Special("\x1B[D");
const Event Event::ArrowRight = Event::Special("\x1B[C");