Enable raw keyboard input

In order for applications to receive all keyboard inputs, including the
Ctrl-C and Ctrl-Z, the raw input mode has been enabled. As result the
SIGINT will no longer be used, instead the keyboard Ctrl-C event is used
for exiting the framework, but only if no components has made use of it.
This commit is contained in:
Jørn Gustav Larsen 2024-03-25 10:14:53 +01:00 committed by ArthurSonzogni
parent 7e3e1d4bca
commit 0397a47c46
No known key found for this signature in database
GPG Key ID: 41D98248C074CD6C
8 changed files with 466 additions and 137 deletions

View File

@ -5,6 +5,10 @@ current (development)
--------------------- ---------------------
### Component ### Component
- Feature: Add support for raw input. Allowing more keys to be detected.
- Feature: Add `Mouse::WeelLeft` and `Mouse::WeelRight` events on supported
terminals.
- Feature: Add `Event::DebugString()`.
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert` - Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
option. Added by @mingsheng13. option. Added by @mingsheng13.
- Feature: Add `DropdownOption` to configure the dropdown. See #826. - Feature: Add `DropdownOption` to configure the dropdown. See #826.

View File

@ -18,123 +18,12 @@
using namespace ftxui; using namespace ftxui;
std::string Stringify(Event event) { std::string Code(Event event) {
std::string out; std::string codes;
for (auto& it : event.input()) for (auto& it : event.input()) {
out += " " + std::to_string((unsigned int)it); codes += " " + std::to_string((unsigned int)it);
out = "(" + out + " ) -> ";
if (event.is_character()) {
out += "Event::Character(\"" + event.character() + "\")";
} else if (event.is_mouse()) {
out += "mouse";
switch (event.mouse().button) {
case Mouse::Left:
out += "_left";
break;
case Mouse::Middle:
out += "_middle";
break;
case Mouse::Right:
out += "_right";
break;
case Mouse::None:
out += "_none";
break;
case Mouse::WheelUp:
out += "_wheel_up";
break;
case Mouse::WheelDown:
out += "_wheel_down";
break;
}
switch (event.mouse().motion) {
case Mouse::Pressed:
out += "_pressed";
break;
case Mouse::Released:
out += "_released";
break;
case Mouse::Moved:
out += "_moved";
break;
}
if (event.mouse().control)
out += "_control";
if (event.mouse().shift)
out += "_shift";
if (event.mouse().meta)
out += "_meta";
out += "(" + //
std::to_string(event.mouse().x) + "," +
std::to_string(event.mouse().y) + ")";
} else if (event == Event::ArrowLeft) {
out += "Event::ArrowLeft";
} else if (event == Event::ArrowRight) {
out += "Event::ArrowRight";
} else if (event == Event::ArrowUp) {
out += "Event::ArrowUp";
} else if (event == Event::ArrowDown) {
out += "Event::ArrowDown";
} else if (event == Event::ArrowLeftCtrl) {
out += "Event::ArrowLeftCtrl";
} else if (event == Event ::ArrowRightCtrl) {
out += "Event::ArrowRightCtrl";
} else if (event == Event::ArrowUpCtrl) {
out += "Event::ArrowUpCtrl";
} else if (event == Event::ArrowDownCtrl) {
out += "Event::ArrowDownCtrl";
} else if (event == Event::Backspace) {
out += "Event::Backspace";
} else if (event == Event::Delete) {
out += "Event::Delete";
} else if (event == Event::Escape) {
out += "Event::Escape";
} else if (event == Event::Return) {
out += "Event::Return";
} else if (event == Event::Tab) {
out += "Event::Tab";
} else if (event == Event::TabReverse) {
out += "Event::TabReverse";
} else if (event == Event::F1) {
out += "Event::F1";
} else if (event == Event::F2) {
out += "Event::F2";
} else if (event == Event::F3) {
out += "Event::F3";
} else if (event == Event::F4) {
out += "Event::F4";
} else if (event == Event::F5) {
out += "Event::F5";
} else if (event == Event::F6) {
out += "Event::F6";
} else if (event == Event::F7) {
out += "Event::F7";
} else if (event == Event::F8) {
out += "Event::F8";
} else if (event == Event::F9) {
out += "Event::F9";
} else if (event == Event::F10) {
out += "Event::F10";
} else if (event == Event::F11) {
out += "Event::F11";
} else if (event == Event::F12) {
out += "Event::F12";
} else if (event == Event::Home) {
out += "Event::Home";
} else if (event == Event::End) {
out += "Event::End";
} else if (event == Event::PageUp) {
out += "Event::PageUp";
} else if (event == Event::PageDown) {
out += "Event::PageDown";
} else if (event == Event::Custom) {
out += "Custom";
} else {
out += "(special)";
} }
return out; return codes;
} }
int main() { int main() {
@ -142,16 +31,35 @@ int main() {
std::vector<Event> keys; std::vector<Event> keys;
auto component = Renderer([&] { auto left_column = Renderer([&] {
Elements children; Elements children = {
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) text("Codes"),
children.push_back(text(Stringify(keys[i]))); separator(),
return window(text("keys"), vbox(std::move(children))); };
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
children.push_back(text(Code(keys[i])));
}
return vbox(children);
}); });
auto right_column = Renderer([&] {
Elements children = {
text("Event"),
separator(),
};
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
children.push_back(text(keys[i].DebugString()));
}
return vbox(children);
});
int split_size = 40;
auto component = ResizableSplitLeft(left_column, right_column, &split_size);
component |= border;
component |= CatchEvent([&](Event event) { component |= CatchEvent([&](Event event) {
keys.push_back(event); keys.push_back(event);
return true; return false;
}); });
screen.Loop(component); screen.Loop(component);

View File

@ -54,21 +54,52 @@ struct Event {
static const Event Escape; static const Event Escape;
static const Event Tab; static const Event Tab;
static const Event TabReverse; static const Event TabReverse;
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
// --- Navigation keys ---
static const Event Insert; static const Event Insert;
static const Event Home; static const Event Home;
static const Event End; static const Event End;
static const Event PageUp; static const Event PageUp;
static const Event PageDown; static const Event PageDown;
// --- Function keys ---
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
// --- Control keys ---
static const Event a, A, CtrlA, AltA, CtrlAltA;
static const Event b, B, CtrlB, AltB, CtrlAltB;
static const Event c, C, CtrlC, AltC, CtrlAltC;
static const Event d, D, CtrlD, AltD, CtrlAltD;
static const Event e, E, CtrlE, AltE, CtrlAltE;
static const Event f, F, CtrlF, AltF, CtrlAltF;
static const Event g, G, CtrlG, AltG, CtrlAltG;
static const Event h, H, CtrlH, AltH, CtrlAltH;
static const Event i, I, CtrlI, AltI, CtrlAltI;
static const Event j, J, CtrlJ, AltJ, CtrlAltJ;
static const Event k, K, CtrlK, AltK, CtrlAltK;
static const Event l, L, CtrlL, AltL, CtrlAltL;
static const Event m, M, CtrlM, AltM, CtrlAltM;
static const Event n, N, CtrlN, AltN, CtrlAltN;
static const Event o, O, CtrlO, AltO, CtrlAltO;
static const Event p, P, CtrlP, AltP, CtrlAltP;
static const Event q, Q, CtrlQ, AltQ, CtrlAltQ;
static const Event r, R, CtrlR, AltR, CtrlAltR;
static const Event s, S, CtrlS, AltS, CtrlAltS;
static const Event t, T, CtrlT, AltT, CtrlAltT;
static const Event u, U, CtrlU, AltU, CtrlAltU;
static const Event v, V, CtrlV, AltV, CtrlAltV;
static const Event w, W, CtrlW, AltW, CtrlAltW;
static const Event x, X, CtrlX, AltX, CtrlAltX;
static const Event y, Y, CtrlY, AltY, CtrlAltY;
static const Event z, Z, CtrlZ, AltZ, CtrlAltZ;
// --- Custom --- // --- Custom ---
static const Event Custom; static const Event Custom;
//--- Method section --------------------------------------------------------- //--- Method section ---------------------------------------------------------
bool operator==(const Event& other) const { return input_ == other.input_; } bool operator==(const Event& other) const { return input_ == other.input_; }
bool operator!=(const Event& other) const { return !operator==(other); } bool operator!=(const Event& other) const { return !operator==(other); }
bool operator<(const Event& other) const { return input_ < other.input_; }
const std::string& input() const { return input_; } const std::string& input() const { return input_; }
@ -86,6 +117,9 @@ struct Event {
bool is_cursor_shape() const { return type_ == Type::CursorShape; } bool is_cursor_shape() const { return type_ == Type::CursorShape; }
int cursor_shape() const { return data_.cursor_shape; } int cursor_shape() const { return data_.cursor_shape; }
// Debug
std::string DebugString() const;
//--- State section ---------------------------------------------------------- //--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr; ScreenInteractive* screen_ = nullptr;

View File

@ -16,6 +16,8 @@ struct Mouse {
None = 3, None = 3,
WheelUp = 4, WheelUp = 4,
WheelDown = 5, WheelDown = 5,
WheelLeft = 6, /// Supported terminal only.
WheelRight = 7, /// Supported terminal only.
}; };
enum Motion { enum Motion {

View File

@ -1,6 +1,7 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.
#include <map> // for map
#include <utility> // for move #include <utility> // for move
#include "ftxui/component/event.hpp" #include "ftxui/component/event.hpp"
@ -79,6 +80,199 @@ Event Event::CursorPosition(std::string input, int x, int y) {
return event; return event;
} }
/// @brief Return a string representation of the event.
std::string Event::DebugString() const {
static std::map<Event, const char*> event_to_string = {
// --- Arrow ---
{Event::ArrowLeft, "Event::ArrowLeft"},
{Event::ArrowRight, "Event::ArrowRight"},
{Event::ArrowUp, "Event::ArrowUp"},
{Event::ArrowDown, "Event::ArrowDown"},
// --- ArrowCtrl ---
{Event::ArrowLeftCtrl, "Event::ArrowLeftCtrl"},
{Event::ArrowRightCtrl, "Event::ArrowRightCtrl"},
{Event::ArrowUpCtrl, "Event::ArrowUpCtrl"},
{Event::ArrowDownCtrl, "Event::ArrowDownCtrl"},
// --- Other ---
{Event::Backspace, "Event::Backspace"},
{Event::Delete, "Event::Delete"},
{Event::Escape, "Event::Escape"},
{Event::Return, "Event::Return"},
{Event::Tab, "Event::Tab"},
{Event::TabReverse, "Event::TabReverse"},
// --- Function keys ---
{Event::F1, "Event::F1"},
{Event::F2, "Event::F2"},
{Event::F3, "Event::F3"},
{Event::F4, "Event::F4"},
{Event::F5, "Event::F5"},
{Event::F6, "Event::F6"},
{Event::F7, "Event::F7"},
{Event::F8, "Event::F8"},
{Event::F9, "Event::F9"},
{Event::F10, "Event::F10"},
{Event::F11, "Event::F11"},
{Event::F12, "Event::F12"},
// --- Navigation keys ---
{Event::Insert, "Event::Insert"},
{Event::Home, "Event::Home"},
{Event::End, "Event::End"},
{Event::PageUp, "Event::PageUp"},
{Event::PageDown, "Event::PageDown"},
// --- Control keys ---
{Event::CtrlA, "Event::CtrlA"},
{Event::CtrlB, "Event::CtrlB"},
{Event::CtrlC, "Event::CtrlC"},
{Event::CtrlD, "Event::CtrlD"},
{Event::CtrlE, "Event::CtrlE"},
{Event::CtrlF, "Event::CtrlF"},
{Event::CtrlG, "Event::CtrlG"},
{Event::CtrlH, "Event::CtrlH"},
{Event::CtrlI, "Event::CtrlI"},
{Event::CtrlJ, "Event::CtrlJ"},
{Event::CtrlK, "Event::CtrlK"},
{Event::CtrlL, "Event::CtrlL"},
{Event::CtrlM, "Event::CtrlM"},
{Event::CtrlN, "Event::CtrlN"},
{Event::CtrlO, "Event::CtrlO"},
{Event::CtrlP, "Event::CtrlP"},
{Event::CtrlQ, "Event::CtrlQ"},
{Event::CtrlR, "Event::CtrlR"},
{Event::CtrlS, "Event::CtrlS"},
{Event::CtrlT, "Event::CtrlT"},
{Event::CtrlU, "Event::CtrlU"},
{Event::CtrlV, "Event::CtrlV"},
{Event::CtrlW, "Event::CtrlW"},
{Event::CtrlX, "Event::CtrlX"},
{Event::CtrlY, "Event::CtrlY"},
{Event::CtrlZ, "Event::CtrlZ"},
// --- Alt keys ---
{Event::AltA, "Event::AltA"},
{Event::AltB, "Event::AltB"},
{Event::AltC, "Event::AltC"},
{Event::AltD, "Event::AltD"},
{Event::AltE, "Event::AltE"},
{Event::AltF, "Event::AltF"},
{Event::AltG, "Event::AltG"},
{Event::AltH, "Event::AltH"},
{Event::AltI, "Event::AltI"},
{Event::AltJ, "Event::AltJ"},
{Event::AltK, "Event::AltK"},
{Event::AltL, "Event::AltL"},
{Event::AltM, "Event::AltM"},
{Event::AltN, "Event::AltN"},
{Event::AltO, "Event::AltO"},
{Event::AltP, "Event::AltP"},
{Event::AltQ, "Event::AltQ"},
{Event::AltR, "Event::AltR"},
{Event::AltS, "Event::AltS"},
{Event::AltT, "Event::AltT"},
{Event::AltU, "Event::AltU"},
{Event::AltV, "Event::AltV"},
{Event::AltW, "Event::AltW"},
{Event::AltX, "Event::AltX"},
{Event::AltY, "Event::AltY"},
{Event::AltZ, "Event::AltZ"},
// --- CtrlAlt keys ---
{Event::CtrlAltA, "Event::CtrlAltA"},
{Event::CtrlAltB, "Event::CtrlAltB"},
{Event::CtrlAltC, "Event::CtrlAltC"},
{Event::CtrlAltD, "Event::CtrlAltD"},
{Event::CtrlAltE, "Event::CtrlAltE"},
{Event::CtrlAltF, "Event::CtrlAltF"},
{Event::CtrlAltG, "Event::CtrlAltG"},
{Event::CtrlAltH, "Event::CtrlAltH"},
{Event::CtrlAltI, "Event::CtrlAltI"},
{Event::CtrlAltJ, "Event::CtrlAltJ"},
{Event::CtrlAltK, "Event::CtrlAltK"},
{Event::CtrlAltL, "Event::CtrlAltL"},
{Event::CtrlAltM, "Event::CtrlAltM"},
{Event::CtrlAltN, "Event::CtrlAltN"},
{Event::CtrlAltO, "Event::CtrlAltO"},
{Event::CtrlAltP, "Event::CtrlAltP"},
{Event::CtrlAltQ, "Event::CtrlAltQ"},
{Event::CtrlAltR, "Event::CtrlAltR"},
{Event::CtrlAltS, "Event::CtrlAltS"},
{Event::CtrlAltT, "Event::CtrlAltT"},
{Event::CtrlAltU, "Event::CtrlAltU"},
{Event::CtrlAltV, "Event::CtrlAltV"},
{Event::CtrlAltW, "Event::CtrlAltW"},
{Event::CtrlAltX, "Event::CtrlAltX"},
{Event::CtrlAltY, "Event::CtrlAltY"},
{Event::CtrlAltZ, "Event::CtrlAltZ"},
// --- Custom ---
{Event::Custom, "Event::Custom"},
};
static std::map<Mouse::Button, const char*> mouse_button_string = {
{Mouse::Button::Left, ".button = Mouse::Left"},
{Mouse::Button::Middle, ".button = Mouse::Middle"},
{Mouse::Button::Right, ".button = Mouse::Right"},
{Mouse::Button::WheelUp, ".button = Mouse::WheelUp"},
{Mouse::Button::WheelDown, ".button = Mouse::WheelDown"},
{Mouse::Button::None, ".button = Mouse::None"},
{Mouse::Button::WheelLeft, ".button = Mouse::WheelLeft"},
{Mouse::Button::WheelRight, ".button = Mouse::WheelRight"},
};
static std::map<Mouse::Motion, const char*> mouse_motion_string = {
{Mouse::Motion::Pressed, ".motion = Mouse::Pressed"},
{Mouse::Motion::Released, ".motion = Mouse::Released"},
{Mouse::Motion::Moved, ".motion = Mouse::Moved"},
};
switch(type_) {
case Type::Character: {
return "Event::Character(\"" + input_ + "\")";
}
case Type::Mouse: {
std::string out = "Event::Mouse(\"...\", Mouse{";
out += std::string(mouse_button_string[data_.mouse.button]);
out += ", ";
out += std::string(mouse_motion_string[data_.mouse.motion]);
out += ", ";
if (data_.mouse.shift) {
out += ".shift = true, ";
}
if (data_.mouse.meta) {
out += ".meta = true, ";
}
if (data_.mouse.control) {
out += ".control = true, ";
}
out += ".x = " + std::to_string(data_.mouse.x);
out += ", ";
out += ".y = " + std::to_string(data_.mouse.y);
out += "})";
return out;
}
case Type::CursorShape:
return "Event::CursorShape(" + input_ + ", " +
std::to_string(data_.cursor_shape) + ")";
case Type::CursorPosition:
return "Event::CursorPosition(" + input_ + ", " +
std::to_string(data_.cursor.x) + ", " +
std::to_string(data_.cursor.y) + ")";
default: {
auto event_it = event_to_string.find(*this);
if (event_it != event_to_string.end()) {
return event_it->second;
}
return "";
}
}
return "";
}
// --- Arrow --- // --- Arrow ---
const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
@ -116,5 +310,140 @@ const Event Event::End = Event::Special({27, 91, 70}); // NOLINT
const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT const Event Event::PageUp = Event::Special({27, 91, 53, 126}); // NOLINT
const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT const Event Event::PageDown = Event::Special({27, 91, 54, 126}); // NOLINT
const Event Event::Custom = Event::Special({0}); // NOLINT const Event Event::Custom = Event::Special({0}); // NOLINT
//
const Event Event::a = Event::Character("a"); // NOLINT
const Event Event::b = Event::Character("b"); // NOLINT
const Event Event::c = Event::Character("c"); // NOLINT
const Event Event::d = Event::Character("d"); // NOLINT
const Event Event::e = Event::Character("e"); // NOLINT
const Event Event::f = Event::Character("f"); // NOLINT
const Event Event::g = Event::Character("g"); // NOLINT
const Event Event::h = Event::Character("h"); // NOLINT
const Event Event::i = Event::Character("i"); // NOLINT
const Event Event::j = Event::Character("j"); // NOLINT
const Event Event::k = Event::Character("k"); // NOLINT
const Event Event::l = Event::Character("l"); // NOLINT
const Event Event::m = Event::Character("m"); // NOLINT
const Event Event::n = Event::Character("n"); // NOLINT
const Event Event::o = Event::Character("o"); // NOLINT
const Event Event::p = Event::Character("p"); // NOLINT
const Event Event::q = Event::Character("q"); // NOLINT
const Event Event::r = Event::Character("r"); // NOLINT
const Event Event::s = Event::Character("s"); // NOLINT
const Event Event::t = Event::Character("t"); // NOLINT
const Event Event::u = Event::Character("u"); // NOLINT
const Event Event::v = Event::Character("v"); // NOLINT
const Event Event::w = Event::Character("w"); // NOLINT
const Event Event::x = Event::Character("x"); // NOLINT
const Event Event::y = Event::Character("y"); // NOLINT
const Event Event::z = Event::Character("z"); // NOLINT
const Event Event::A = Event::Character("A"); // NOLINT
const Event Event::B = Event::Character("B"); // NOLINT
const Event Event::C = Event::Character("C"); // NOLINT
const Event Event::D = Event::Character("D"); // NOLINT
const Event Event::E = Event::Character("E"); // NOLINT
const Event Event::F = Event::Character("F"); // NOLINT
const Event Event::G = Event::Character("G"); // NOLINT
const Event Event::H = Event::Character("H"); // NOLINT
const Event Event::I = Event::Character("I"); // NOLINT
const Event Event::J = Event::Character("J"); // NOLINT
const Event Event::K = Event::Character("K"); // NOLINT
const Event Event::L = Event::Character("L"); // NOLINT
const Event Event::M = Event::Character("M"); // NOLINT
const Event Event::N = Event::Character("N"); // NOLINT
const Event Event::O = Event::Character("O"); // NOLINT
const Event Event::P = Event::Character("P"); // NOLINT
const Event Event::Q = Event::Character("Q"); // NOLINT
const Event Event::R = Event::Character("R"); // NOLINT
const Event Event::S = Event::Character("S"); // NOLINT
const Event Event::T = Event::Character("T"); // NOLINT
const Event Event::U = Event::Character("U"); // NOLINT
const Event Event::V = Event::Character("V"); // NOLINT
const Event Event::W = Event::Character("W"); // NOLINT
const Event Event::X = Event::Character("X"); // NOLINT
const Event Event::Y = Event::Character("Y"); // NOLINT
const Event Event::Z = Event::Character("Z"); // NOLINT
const Event Event::CtrlA = Event::Special("\x01"); // NOLINT
const Event Event::CtrlB = Event::Special("\x02"); // NOLINT
const Event Event::CtrlC = Event::Special("\x03"); // NOLINT
const Event Event::CtrlD = Event::Special("\x04"); // NOLINT
const Event Event::CtrlE = Event::Special("\x05"); // NOLINT
const Event Event::CtrlF = Event::Special("\x06"); // NOLINT
const Event Event::CtrlG = Event::Special("\x07"); // NOLINT
const Event Event::CtrlH = Event::Special("\x08"); // NOLINT
const Event Event::CtrlI = Event::Special("\x09"); // NOLINT
const Event Event::CtrlJ = Event::Special("\x0a"); // NOLINT
const Event Event::CtrlK = Event::Special("\x0b"); // NOLINT
const Event Event::CtrlL = Event::Special("\x0c"); // NOLINT
const Event Event::CtrlM = Event::Special("\x0d"); // NOLINT
const Event Event::CtrlN = Event::Special("\x0e"); // NOLINT
const Event Event::CtrlO = Event::Special("\x0f"); // NOLINT
const Event Event::CtrlP = Event::Special("\x10"); // NOLINT
const Event Event::CtrlQ = Event::Special("\x11"); // NOLINT
const Event Event::CtrlR = Event::Special("\x12"); // NOLINT
const Event Event::CtrlS = Event::Special("\x13"); // NOLINT
const Event Event::CtrlT = Event::Special("\x14"); // NOLINT
const Event Event::CtrlU = Event::Special("\x15"); // NOLINT
const Event Event::CtrlV = Event::Special("\x16"); // NOLINT
const Event Event::CtrlW = Event::Special("\x17"); // NOLINT
const Event Event::CtrlX = Event::Special("\x18"); // NOLINT
const Event Event::CtrlY = Event::Special("\x19"); // NOLINT
const Event Event::CtrlZ = Event::Special("\x1a"); // NOLINT
const Event Event::AltA = Event::Special("\x1b""a"); // NOLINT
const Event Event::AltB = Event::Special("\x1b""b"); // NOLINT
const Event Event::AltC = Event::Special("\x1b""c"); // NOLINT
const Event Event::AltD = Event::Special("\x1b""d"); // NOLINT
const Event Event::AltE = Event::Special("\x1b""e"); // NOLINT
const Event Event::AltF = Event::Special("\x1b""f"); // NOLINT
const Event Event::AltG = Event::Special("\x1b""g"); // NOLINT
const Event Event::AltH = Event::Special("\x1b""h"); // NOLINT
const Event Event::AltI = Event::Special("\x1b""i"); // NOLINT
const Event Event::AltJ = Event::Special("\x1b""j"); // NOLINT
const Event Event::AltK = Event::Special("\x1b""k"); // NOLINT
const Event Event::AltL = Event::Special("\x1b""l"); // NOLINT
const Event Event::AltM = Event::Special("\x1b""m"); // NOLINT
const Event Event::AltN = Event::Special("\x1b""n"); // NOLINT
const Event Event::AltO = Event::Special("\x1b""o"); // NOLINT
const Event Event::AltP = Event::Special("\x1b""p"); // NOLINT
const Event Event::AltQ = Event::Special("\x1b""q"); // NOLINT
const Event Event::AltR = Event::Special("\x1b""r"); // NOLINT
const Event Event::AltS = Event::Special("\x1b""s"); // NOLINT
const Event Event::AltT = Event::Special("\x1b""t"); // NOLINT
const Event Event::AltU = Event::Special("\x1b""u"); // NOLINT
const Event Event::AltV = Event::Special("\x1b""v"); // NOLINT
const Event Event::AltW = Event::Special("\x1b""w"); // NOLINT
const Event Event::AltX = Event::Special("\x1b""x"); // NOLINT
const Event Event::AltY = Event::Special("\x1b""y"); // NOLINT
const Event Event::AltZ = Event::Special("\x1b""z"); // NOLINT
const Event Event::CtrlAltA = Event::Special("\x1b\x01"); // NOLINT
const Event Event::CtrlAltB = Event::Special("\x1b\x02"); // NOLINT
const Event Event::CtrlAltC = Event::Special("\x1b\x03"); // NOLINT
const Event Event::CtrlAltD = Event::Special("\x1b\x04"); // NOLINT
const Event Event::CtrlAltE = Event::Special("\x1b\x05"); // NOLINT
const Event Event::CtrlAltF = Event::Special("\x1b\x06"); // NOLINT
const Event Event::CtrlAltG = Event::Special("\x1b\x07"); // NOLINT
const Event Event::CtrlAltH = Event::Special("\x1b\x08"); // NOLINT
const Event Event::CtrlAltI = Event::Special("\x1b\x09"); // NOLINT
const Event Event::CtrlAltJ = Event::Special("\x1b\x0a"); // NOLINT
const Event Event::CtrlAltK = Event::Special("\x1b\x0b"); // NOLINT
const Event Event::CtrlAltL = Event::Special("\x1b\x0c"); // NOLINT
const Event Event::CtrlAltM = Event::Special("\x1b\x0d"); // NOLINT
const Event Event::CtrlAltN = Event::Special("\x1b\x0e"); // NOLINT
const Event Event::CtrlAltO = Event::Special("\x1b\x0f"); // NOLINT
const Event Event::CtrlAltP = Event::Special("\x1b\x10"); // NOLINT
const Event Event::CtrlAltQ = Event::Special("\x1b\x11"); // NOLINT
const Event Event::CtrlAltR = Event::Special("\x1b\x12"); // NOLINT
const Event Event::CtrlAltS = Event::Special("\x1b\x13"); // NOLINT
const Event Event::CtrlAltT = Event::Special("\x1b\x14"); // NOLINT
const Event Event::CtrlAltU = Event::Special("\x1b\x15"); // NOLINT
const Event Event::CtrlAltV = Event::Special("\x1b\x16"); // NOLINT
const Event Event::CtrlAltW = Event::Special("\x1b\x17"); // NOLINT
const Event Event::CtrlAltX = Event::Special("\x1b\x18"); // NOLINT
const Event Event::CtrlAltY = Event::Special("\x1b\x19"); // NOLINT
const Event Event::CtrlAltZ = Event::Special("\x1b\x1a"); // NOLINT
} // namespace ftxui } // namespace ftxui

View File

@ -132,7 +132,6 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
// Read char from the terminal. // Read char from the terminal.
void EventListener(std::atomic<bool>* quit, Sender<Task> out) { void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
(void)timeout_microseconds;
auto parser = TerminalInputParser(std::move(out)); auto parser = TerminalInputParser(std::move(out));
char c; char c;
@ -638,13 +637,26 @@ void ScreenInteractive::Install() {
tcgetattr(STDIN_FILENO, &terminal); tcgetattr(STDIN_FILENO, &terminal);
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); }); on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal. // Enabling raw terminal input mode
terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press. terminal.c_iflag &= ~IGNBRK; // Ignore break condition
terminal.c_cc[VMIN] = 0; terminal.c_iflag &= ~BRKINT; // Break causes input and output to be flushed
terminal.c_cc[VTIME] = 0; terminal.c_iflag &= ~PARMRK; // Mark parity errors
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0); terminal.c_iflag &= ~ISTRIP; // Strip 8th bit off characters
// fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); terminal.c_iflag &= ~INLCR; // Map NL to CR
// on_exit_functions.push([=] { fcntl(STDIN_FILENO, F_GETFL, oldf); }); terminal.c_iflag &= ~IGNCR; // Ignore CR
terminal.c_iflag &= ~ICRNL; // Map CR to NL
terminal.c_iflag &= ~IXON; // Enable XON/XOFF flow control on output
terminal.c_lflag &= ~ECHO; // Echo input characters
terminal.c_lflag &= ~ECHONL; // Echo NL
terminal.c_lflag &= ~ICANON; // Canonical mode
terminal.c_lflag &= ~ISIG; // Enable signals
terminal.c_lflag &= ~IEXTEN; // Enable extended input processing
terminal.c_cflag |= (CS8); // 8 bits per byte
terminal.c_cc[VMIN] = 0; // Minimum number of characters for non-canonical
// read.
terminal.c_cc[VTIME] = 0; // Timeout in deciseconds for non-canonical read.
tcsetattr(STDIN_FILENO, TCSANOW, &terminal); tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
@ -745,7 +757,13 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
} }
arg.screen_ = this; arg.screen_ = this;
component->OnEvent(arg);
const bool handled = component->OnEvent(arg);
if (!handled && (arg == Event::CtrlC)) {
Exit();
}
frame_valid_ = false; frame_valid_ = false;
return; return;
} }

View File

@ -282,12 +282,26 @@ TerminalInputParser::Output TerminalInputParser::ParseESC() {
return ParseCSI(); return ParseCSI();
case ']': case ']':
return ParseOSC(); return ParseOSC();
default:
// Expecting 2 characters.
case ' ':
case '#':
case '%':
case '(':
case ')':
case '*':
case '+':
case 'O':
case 'N':
{
if (!Eat()) { if (!Eat()) {
return UNCOMPLETED; return UNCOMPLETED;
} else {
return SPECIAL;
} }
return SPECIAL;
}
// Expecting 1 character:
default:
return SPECIAL;
} }
} }

View File

@ -76,6 +76,24 @@ TEST(Event, EscapeKeyEnoughWait) {
EXPECT_FALSE(event_receiver->Receive(&received)); EXPECT_FALSE(event_receiver->Receive(&received));
} }
TEST(Event, EscapeFast) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('a');
parser.Add('\x1B');
parser.Add('b');
parser.Timeout(49);
}
Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_EQ(std::get<Event>(received), Event::AltA);
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_EQ(std::get<Event>(received), Event::AltB);
EXPECT_FALSE(event_receiver->Receive(&received));
}
TEST(Event, MouseLeftClickPressed) { TEST(Event, MouseLeftClickPressed) {
auto event_receiver = MakeReceiver<Task>(); auto event_receiver = MakeReceiver<Task>();
{ {
@ -367,13 +385,14 @@ TEST(Event, Special) {
std::vector<unsigned char> input; std::vector<unsigned char> input;
Event expected; Event expected;
} kTestCase[] = { } kTestCase[] = {
// Arrow (defaut cursor mode) // Arrow (default cursor mode)
{str("\x1B[A"), Event::ArrowUp}, {str("\x1B[A"), Event::ArrowUp},
{str("\x1B[B"), Event::ArrowDown}, {str("\x1B[B"), Event::ArrowDown},
{str("\x1B[C"), Event::ArrowRight}, {str("\x1B[C"), Event::ArrowRight},
{str("\x1B[D"), Event::ArrowLeft}, {str("\x1B[D"), Event::ArrowLeft},
{str("\x1B[H"), Event::Home}, {str("\x1B[H"), Event::Home},
{str("\x1B[F"), Event::End}, {str("\x1B[F"), Event::End},
/*
// Arrow (application cursor mode) // Arrow (application cursor mode)
{str("\x1BOA"), Event::ArrowUp}, {str("\x1BOA"), Event::ArrowUp},
@ -454,6 +473,7 @@ TEST(Event, Special) {
// Custom: // Custom:
{{0}, Event::Custom}, {{0}, Event::Custom},
*/
}; };
for (auto test : kTestCase) { for (auto test : kTestCase) {