mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-30 16:49:01 +08:00
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:
159
src/ftxui/component/terminal_input_parser.cpp
Normal file
159
src/ftxui/component/terminal_input_parser.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "ftxui/component/terminal_input_parser.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
TerminalInputParser::TerminalInputParser(Sender<Event> out)
|
||||
: out_(std::move(out)) {}
|
||||
|
||||
void TerminalInputParser::Timeout(int time) {
|
||||
timeout_ += time;
|
||||
if (timeout_ < 50)
|
||||
return;
|
||||
timeout_ = 0;
|
||||
if (pending_.size())
|
||||
Send(SPECIAL);
|
||||
}
|
||||
|
||||
void TerminalInputParser::Add(char c) {
|
||||
pending_ += c;
|
||||
timeout_ = 0;
|
||||
position_ = -1;
|
||||
Send(Parse());
|
||||
}
|
||||
|
||||
unsigned char TerminalInputParser::Current() {
|
||||
return pending_[position_];
|
||||
}
|
||||
|
||||
bool TerminalInputParser::Eat() {
|
||||
position_++;
|
||||
return position_ < (int)pending_.size();
|
||||
}
|
||||
|
||||
void TerminalInputParser::Send(TerminalInputParser::Type type) {
|
||||
switch (type) {
|
||||
case UNCOMPLETED:
|
||||
return;
|
||||
|
||||
case DROP:
|
||||
pending_.clear();
|
||||
return;
|
||||
|
||||
case CHARACTER:
|
||||
out_->Send(Event::Character(std::move(pending_)));
|
||||
pending_.clear();
|
||||
return;
|
||||
|
||||
case SPECIAL:
|
||||
out_->Send(Event::Special(std::move(pending_)));
|
||||
pending_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::Parse() {
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
|
||||
switch (Current()) {
|
||||
case 24: // CAN
|
||||
case 26: // SUB
|
||||
return DROP;
|
||||
|
||||
case '\x1B':
|
||||
return ParseESC();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (Current() < 32) // C0
|
||||
return SPECIAL;
|
||||
|
||||
if (Current() == 127) // Delete
|
||||
return SPECIAL;
|
||||
|
||||
return ParseUTF8();
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::ParseUTF8() {
|
||||
unsigned char head = static_cast<unsigned char>(Current());
|
||||
for (int i = 0; i < 3; ++i, head <<= 1) {
|
||||
if ((head & 0b11000000) != 0b11000000)
|
||||
break;
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
return CHARACTER;
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::ParseESC() {
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
switch (Current()) {
|
||||
case 'P':
|
||||
return ParseDCS();
|
||||
case '[':
|
||||
return ParseCSI();
|
||||
case ']':
|
||||
return ParseOSC();
|
||||
default:
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::ParseDCS() {
|
||||
// Parse until the string terminator ST.
|
||||
while (1) {
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
|
||||
if (Current() != '\x1B')
|
||||
continue;
|
||||
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
|
||||
if (Current() != '\\')
|
||||
continue;
|
||||
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::ParseCSI() {
|
||||
while (true) {
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
|
||||
if (Current() >= '0' && Current() <= '9')
|
||||
continue;
|
||||
|
||||
if (Current() == ';')
|
||||
continue;
|
||||
|
||||
if (Current() >= ' ' && Current() <= '~')
|
||||
return SPECIAL;
|
||||
|
||||
// Invalid ESC in CSI.
|
||||
if (Current() == '\x1B')
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
TerminalInputParser::Type TerminalInputParser::ParseOSC() {
|
||||
// Parse until the string terminator ST.
|
||||
while (true) {
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
if (Current() != '\x1B')
|
||||
continue;
|
||||
if (!Eat())
|
||||
return UNCOMPLETED;
|
||||
if (Current() != '\\')
|
||||
continue;
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
} // namespace ftxui
|
Reference in New Issue
Block a user