27 {std::string({8}), std::string({127})},
51 {
"\x1B[1~",
"\x1B[H"},
52 {
"\x1B[4~",
"\x1B[F"},
62 {
"\x1B[[A",
"\x1BOP"},
63 {
"\x1B[[B",
"\x1BOQ"},
64 {
"\x1B[[C",
"\x1BOR"},
65 {
"\x1B[[D",
"\x1BOS"},
66 {
"\x1B[[E",
"\x1B[15~"},
69 {
"\x1B[11~",
"\x1BOP"},
70 {
"\x1B[12~",
"\x1BOQ"},
71 {
"\x1B[13~",
"\x1BOR"},
72 {
"\x1B[14~",
"\x1BOS"},
75 {
"\x1BOt",
"\x1B[15~"},
76 {
"\x1BOu",
"\x1B[17~"},
77 {
"\x1BOv",
"\x1B[18~"},
78 {
"\x1BOl",
"\x1B[19~"},
79 {
"\x1BOw",
"\x1B[20~"},
80 {
"\x1BOx",
"\x1B[21~"},
87 {
"\x1B[Q",
"\x1B[15~"},
88 {
"\x1B[R",
"\x1B[17~"},
89 {
"\x1B[S",
"\x1B[18~"},
90 {
"\x1B[T",
"\x1B[19~"},
91 {
"\x1B[U",
"\x1B[20~"},
92 {
"\x1B[V",
"\x1B[21~"},
93 {
"\x1B[W",
"\x1B[23~"},
94 {
"\x1B[X",
"\x1B[24~"},
98 : out_(std::move(out)) {}
102 const int timeout_threshold = 50;
103 if (timeout_ < timeout_threshold) {
107 if (!pending_.empty()) {
119unsigned char TerminalInputParser::Current() {
120 return pending_[position_];
123bool TerminalInputParser::Eat() {
125 return position_ < static_cast<int>(pending_.size());
128void TerminalInputParser::Send(TerminalInputParser::Output output) {
129 switch (output.type) {
138 out_(Event::Character(std::move(pending_)));
145 pending_ = it->second;
153 out_(Event::Mouse(std::move(pending_), output.mouse));
157 case CURSOR_POSITION:
158 out_(Event::CursorPosition(std::move(pending_),
165 out_(Event::CursorShape(std::move(pending_), output.cursor_shape));
169 case TERMINAL_NAME_VERSION:
170 out_(Event::TerminalNameVersion(std::move(pending_),
171 std::move(output.terminal_name),
172 output.terminal_version));
176 case TERMINAL_EMULATOR:
177 out_(Event::TerminalEmulator(std::move(pending_),
178 std::move(output.terminal_name),
179 std::move(output.terminal_version_string)));
183 case TERMINAL_CAPABILITIES:
185 std::move(pending_), std::move(output.terminal_capabilities)));
192TerminalInputParser::Output TerminalInputParser::Parse() {
197 if (Current() ==
'\x1B') {
201 if (Current() < 32) {
205 if (Current() == 127) {
228TerminalInputParser::Output TerminalInputParser::ParseUTF8() {
229 auto head = Current();
230 unsigned char selector = 0b1000'0000;
233 unsigned char mask = selector;
236 unsigned int first_zero = 8;
237 for (
unsigned int i = 0; i < 8; ++i) {
239 if (!(head & selector)) {
247 auto value = uint32_t(head & ~mask);
250 const unsigned int max_utf8_bytes = 5;
251 if (first_zero == 1 || first_zero >= max_utf8_bytes) {
256 for (
unsigned int i = 2; i <= first_zero; ++i) {
263 if ((head & 0b1100'0000) != 0b1000'0000) {
267 value += head & 0b0011'1111;
272 if (value <= 0b000'0000'0111'1111) {
274 }
else if (value <= 0b000'0111'1111'1111) {
276 }
else if (value <= 0b1111'1111'1111'1111) {
278 }
else if (value <= 0b1'0000'1111'1111'1111'1111) {
284 if (extra_byte != position_) {
291TerminalInputParser::Output TerminalInputParser::ParseESC() {
325TerminalInputParser::Output TerminalInputParser::ParseDCS() {
332 if (Current() !=
'\x1B') {
340 if (Current() !=
'\\') {
345 if (pending_.size() >= 5 && pending_[2] ==
'>' && pending_[3] ==
'|') {
348 const std::string content = pending_.substr(4, pending_.size() - 6);
349 Output output(TERMINAL_EMULATOR);
350 const size_t space = content.find(
' ');
351 const size_t open_paren = content.find(
'(');
352 if (space != std::string::npos) {
353 output.terminal_name = content.substr(0, space);
354 output.terminal_version_string = content.substr(space + 1);
355 }
else if (open_paren != std::string::npos) {
356 output.terminal_name = content.substr(0, open_paren);
357 output.terminal_version_string = content.substr(open_paren + 1);
358 if (!output.terminal_version_string.empty() &&
359 output.terminal_version_string.back() ==
')') {
360 output.terminal_version_string.pop_back();
363 output.terminal_name = content;
364 output.terminal_version_string =
"unknown";
369 if (pending_.size() == 10 &&
370 pending_[2] ==
'1' &&
371 pending_[3] ==
'$' &&
372 pending_[4] ==
'r' &&
374 Output output(CURSOR_SHAPE);
375 output.cursor_shape = pending_[5] -
'0';
383TerminalInputParser::Output TerminalInputParser::ParseCSI() {
384 bool altered_less =
false;
385 bool altered_greater =
false;
386 bool altered_question =
false;
388 std::vector<int> arguments;
394 if (Current() ==
'<') {
399 if (Current() ==
'>') {
400 altered_greater =
true;
404 if (Current() ==
'?') {
405 altered_question =
true;
409 if (Current() >=
'0' && Current() <=
'9') {
411 argument += Current() -
'0';
415 if (Current() ==
';') {
416 arguments.push_back(argument);
423 if (Current() >=
'@' && Current() <=
'~' &&
428 arguments.push_back(argument);
433 return ParseMouse(altered_less,
true, std::move(arguments));
435 return ParseMouse(altered_less,
false, std::move(arguments));
437 return ParseCursorPosition(std::move(arguments));
439 return ParseDeviceAttributes(altered_greater, altered_question,
440 std::move(arguments));
447 if (Current() ==
'\x1B') {
453TerminalInputParser::Output TerminalInputParser::ParseOSC() {
459 if (Current() !=
'\x1B') {
465 if (Current() !=
'\\') {
472TerminalInputParser::Output TerminalInputParser::ParseMouse(
475 std::vector<int> arguments) {
476 if (arguments.size() != 3) {
482 Output output(MOUSE);
495 const int button = arguments[0] & (1 + 2);
496 const bool is_shift = arguments[0] & 4;
497 const bool is_meta = arguments[0] & 8;
498 const bool is_control = arguments[0] & 16;
499 const bool is_move = arguments[0] & 32;
500 const bool is_wheel = arguments[0] & 64;
505 : Mouse::Button(button);
506 output.mouse.shift = is_shift;
507 output.mouse.meta = is_meta;
508 output.mouse.control = is_control;
509 output.mouse.x = arguments[1];
510 output.mouse.y = arguments[2];
517TerminalInputParser::Output TerminalInputParser::ParseCursorPosition(
518 std::vector<int> arguments) {
519 if (arguments.size() != 2) {
522 Output output(CURSOR_POSITION);
523 output.cursor.y = arguments[0];
524 output.cursor.x = arguments[1];
529TerminalInputParser::Output TerminalInputParser::ParseDeviceAttributes(
530 bool altered_greater,
531 bool altered_question,
532 std::vector<int> arguments) {
533 if (altered_greater) {
536 if (arguments.size() >= 3) {
540 Output output(TERMINAL_NAME_VERSION);
541 output.terminal_version = arguments[1];
542 switch (arguments[0]) {
544 output.terminal_name =
"xterm";
547 output.terminal_name =
"vt220";
550 output.terminal_name =
"vt240";
553 output.terminal_name =
"vt330";
556 output.terminal_name =
"vt340";
559 output.terminal_name =
"vt320";
562 output.terminal_name =
"vt420";
565 output.terminal_name =
"vt510";
568 output.terminal_name =
"vt520";
571 output.terminal_name =
"vt525";
574 output.terminal_name =
"tmux";
577 output.terminal_name =
"urxvt";
580 output.terminal_name =
"unknown";
587 }
else if (altered_question) {
590 Output output(TERMINAL_CAPABILITIES);
591 output.terminal_capabilities = std::move(arguments);
static Event Special(std::string_view)
An custom event whose meaning is defined by the user of the library.
const std::vector< int > & TerminalCapabilities() const
Return the terminal capabilities.
Represent an event. It can be key press event, a terminal resize, or more ...
The FTXUI ftxui:: namespace.
const std::map< std::string, std::string > g_uniformize