mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-10-30 16:07:38 +08:00
Enable raw keyboard input (#832)
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. Co-authored-by: Jørn Gustav Larsen <jgl@fasttracksoftware.com> Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
d38b14ffb6
commit
d386df6f94
@ -5,6 +5,14 @@ current (development)
|
||||
---------------------
|
||||
|
||||
### Component
|
||||
- Feature: Add support for raw input. Allowing more keys to be detected.
|
||||
- Feature: Add `ScreenInteractive::ForceHandleCtrlC(false)` to allow component
|
||||
to fully override the default `Ctrl+C` handler.
|
||||
- Feature: Add `ScreenInteractive::ForceHandleCtrlZ(false)` to allow component
|
||||
to fully override the default `Ctrl+Z` handler.
|
||||
- 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`
|
||||
option. Added by @mingsheng13.
|
||||
- Feature: Add `DropdownOption` to configure the dropdown. See #826.
|
||||
|
@ -18,123 +18,12 @@
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
std::string Stringify(Event event) {
|
||||
std::string out;
|
||||
for (auto& it : event.input())
|
||||
out += " " + 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)";
|
||||
std::string Code(Event event) {
|
||||
std::string codes;
|
||||
for (auto& it : event.input()) {
|
||||
codes += " " + std::to_string((unsigned int)it);
|
||||
}
|
||||
return out;
|
||||
return codes;
|
||||
}
|
||||
|
||||
int main() {
|
||||
@ -142,16 +31,35 @@ int main() {
|
||||
|
||||
std::vector<Event> keys;
|
||||
|
||||
auto component = Renderer([&] {
|
||||
Elements children;
|
||||
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i)
|
||||
children.push_back(text(Stringify(keys[i])));
|
||||
return window(text("keys"), vbox(std::move(children)));
|
||||
auto left_column = Renderer([&] {
|
||||
Elements children = {
|
||||
text("Codes"),
|
||||
separator(),
|
||||
};
|
||||
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) {
|
||||
keys.push_back(event);
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
screen.Loop(component);
|
||||
|
@ -54,21 +54,52 @@ struct Event {
|
||||
static const Event Escape;
|
||||
static const Event Tab;
|
||||
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 Home;
|
||||
static const Event End;
|
||||
|
||||
static const Event PageUp;
|
||||
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 ---
|
||||
static const Event Custom;
|
||||
|
||||
//--- Method section ---------------------------------------------------------
|
||||
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 input_ < other.input_; }
|
||||
|
||||
const std::string& input() const { return input_; }
|
||||
|
||||
@ -86,6 +117,9 @@ struct Event {
|
||||
bool is_cursor_shape() const { return type_ == Type::CursorShape; }
|
||||
int cursor_shape() const { return data_.cursor_shape; }
|
||||
|
||||
// Debug
|
||||
std::string DebugString() const;
|
||||
|
||||
//--- State section ----------------------------------------------------------
|
||||
ScreenInteractive* screen_ = nullptr;
|
||||
|
||||
|
@ -16,6 +16,8 @@ struct Mouse {
|
||||
None = 3,
|
||||
WheelUp = 4,
|
||||
WheelDown = 5,
|
||||
WheelLeft = 6, /// Supported terminal only.
|
||||
WheelRight = 7, /// Supported terminal only.
|
||||
};
|
||||
|
||||
enum Motion {
|
||||
|
@ -59,6 +59,15 @@ class ScreenInteractive : public Screen {
|
||||
// temporarily uninstalled.
|
||||
Closure WithRestoredIO(Closure);
|
||||
|
||||
// FTXUI implements handlers for Ctrl-C and Ctrl-Z. By default, these handlers
|
||||
// are executed, even if the component catches the event. This avoid users
|
||||
// handling every event to be trapped in the application. However, in some
|
||||
// cases, the application may want to handle these events itself. In this
|
||||
// case, the application can force FTXUI to not handle these events by calling
|
||||
// the following functions with force=true.
|
||||
void ForceHandleCtrlC(bool force);
|
||||
void ForceHandleCtrlZ(bool force);
|
||||
|
||||
private:
|
||||
void ExitNow();
|
||||
|
||||
@ -114,6 +123,9 @@ class ScreenInteractive : public Screen {
|
||||
|
||||
bool frame_valid_ = false;
|
||||
|
||||
bool force_handle_ctrl_c_ = true;
|
||||
bool force_handle_ctrl_z_ = true;
|
||||
|
||||
// The style of the cursor to restore on exit.
|
||||
int cursor_reset_shape_ = 1;
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include <string> // for string
|
||||
#include <unordered_map> // for unordered_map
|
||||
|
||||
#include "ftxui/screen/color.hpp" // for Color
|
||||
#include "ftxui/screen/image.hpp" // for Pixel, Image
|
||||
#include "ftxui/screen/color.hpp" // for Color
|
||||
#include "ftxui/screen/image.hpp" // for Pixel, Image
|
||||
|
||||
#ifdef DrawText
|
||||
// Workaround for WinUsr.h (via Windows.h) defining macros that break things.
|
||||
@ -94,7 +94,7 @@ struct Canvas {
|
||||
void DrawText(int x, int y, const std::string& value);
|
||||
void DrawText(int x, int y, const std::string& value, const Color& color);
|
||||
void DrawText(int x, int y, const std::string& value, const Stylizer& style);
|
||||
|
||||
|
||||
// Draw using directly pixels or images --------------------------------------
|
||||
// x is considered to be a multiple of 2.
|
||||
// y is considered to be a multiple of 4.
|
||||
|
@ -36,7 +36,8 @@ class Screen : public Image {
|
||||
// Print the Screen on to the terminal.
|
||||
void Print() const;
|
||||
|
||||
// Fill the screen with space and reset any screen state, like hyperlinks, and cursor
|
||||
// Fill the screen with space and reset any screen state, like hyperlinks, and
|
||||
// cursor
|
||||
void Clear();
|
||||
|
||||
// Move the terminal cursor n-lines up with n = dimy().
|
||||
|
@ -1,12 +1,24 @@
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
#include <map> // for map
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/event.hpp"
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/screen/string.hpp" // for to_wstring
|
||||
|
||||
// Disable warning for shadowing variable, for every compilers. Indeed, there is
|
||||
// a static Event for every letter of the alphabet:
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#elif __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable : 6244)
|
||||
#pragma warning(disable : 6246)
|
||||
#endif
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
/// @brief An event corresponding to a given typed character.
|
||||
@ -79,42 +91,376 @@ Event Event::CursorPosition(std::string input, int x, int y) {
|
||||
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 "";
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// NOLINTBEGIN
|
||||
|
||||
// --- Arrow ---
|
||||
const Event Event::ArrowLeft = Event::Special("\x1B[D"); // NOLINT
|
||||
const Event Event::ArrowRight = Event::Special("\x1B[C"); // NOLINT
|
||||
const Event Event::ArrowUp = Event::Special("\x1B[A"); // NOLINT
|
||||
const Event Event::ArrowDown = Event::Special("\x1B[B"); // NOLINT
|
||||
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D"); // NOLINT
|
||||
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C"); // NOLINT
|
||||
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A"); // NOLINT
|
||||
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B"); // NOLINT
|
||||
const Event Event::Backspace = Event::Special({127}); // NOLINT
|
||||
const Event Event::Delete = Event::Special("\x1B[3~"); // NOLINT
|
||||
const Event Event::Escape = Event::Special("\x1B"); // NOLINT
|
||||
const Event Event::Return = Event::Special({10}); // NOLINT
|
||||
const Event Event::Tab = Event::Special({9}); // NOLINT
|
||||
const Event Event::TabReverse = Event::Special({27, 91, 90}); // NOLINT
|
||||
const Event Event::ArrowLeft = Event::Special("\x1B[D");
|
||||
const Event Event::ArrowRight = Event::Special("\x1B[C");
|
||||
const Event Event::ArrowUp = Event::Special("\x1B[A");
|
||||
const Event Event::ArrowDown = Event::Special("\x1B[B");
|
||||
const Event Event::ArrowLeftCtrl = Event::Special("\x1B[1;5D");
|
||||
const Event Event::ArrowRightCtrl = Event::Special("\x1B[1;5C");
|
||||
const Event Event::ArrowUpCtrl = Event::Special("\x1B[1;5A");
|
||||
const Event Event::ArrowDownCtrl = Event::Special("\x1B[1;5B");
|
||||
const Event Event::Backspace = Event::Special({127});
|
||||
const Event Event::Delete = Event::Special("\x1B[3~");
|
||||
const Event Event::Escape = Event::Special("\x1B");
|
||||
const Event Event::Return = Event::Special({10});
|
||||
const Event Event::Tab = Event::Special({9});
|
||||
const Event Event::TabReverse = Event::Special({27, 91, 90});
|
||||
|
||||
// See https://invisible-island.net/xterm/xterm-function-keys.html
|
||||
// We follow xterm-new / vterm-xf86-v4 / mgt / screen
|
||||
const Event Event::F1 = Event::Special("\x1BOP"); // NOLINT
|
||||
const Event Event::F2 = Event::Special("\x1BOQ"); // NOLINT
|
||||
const Event Event::F3 = Event::Special("\x1BOR"); // NOLINT
|
||||
const Event Event::F4 = Event::Special("\x1BOS"); // NOLINT
|
||||
const Event Event::F5 = Event::Special("\x1B[15~"); // NOLINT
|
||||
const Event Event::F6 = Event::Special("\x1B[17~"); // NOLINT
|
||||
const Event Event::F7 = Event::Special("\x1B[18~"); // NOLINT
|
||||
const Event Event::F8 = Event::Special("\x1B[19~"); // NOLINT
|
||||
const Event Event::F9 = Event::Special("\x1B[20~"); // NOLINT
|
||||
const Event Event::F10 = Event::Special("\x1B[21~"); // NOLINT
|
||||
const Event Event::F11 = Event::Special("\x1B[23~"); // NOLINT
|
||||
const Event Event::F12 = Event::Special("\x1B[24~"); // NOLINT
|
||||
const Event Event::F1 = Event::Special("\x1BOP");
|
||||
const Event Event::F2 = Event::Special("\x1BOQ");
|
||||
const Event Event::F3 = Event::Special("\x1BOR");
|
||||
const Event Event::F4 = Event::Special("\x1BOS");
|
||||
const Event Event::F5 = Event::Special("\x1B[15~");
|
||||
const Event Event::F6 = Event::Special("\x1B[17~");
|
||||
const Event Event::F7 = Event::Special("\x1B[18~");
|
||||
const Event Event::F8 = Event::Special("\x1B[19~");
|
||||
const Event Event::F9 = Event::Special("\x1B[20~");
|
||||
const Event Event::F10 = Event::Special("\x1B[21~");
|
||||
const Event Event::F11 = Event::Special("\x1B[23~");
|
||||
const Event Event::F12 = Event::Special("\x1B[24~");
|
||||
|
||||
const Event Event::Insert = Event::Special("\x1B[2~"); // NOLINT
|
||||
const Event Event::Home = Event::Special({27, 91, 72}); // NOLINT
|
||||
const Event Event::End = Event::Special({27, 91, 70}); // 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::Custom = Event::Special({0}); // NOLINT
|
||||
const Event Event::Insert = Event::Special("\x1B[2~");
|
||||
const Event Event::Home = Event::Special({27, 91, 72});
|
||||
const Event Event::End = Event::Special({27, 91, 70});
|
||||
const Event Event::PageUp = Event::Special({27, 91, 53, 126});
|
||||
const Event Event::PageDown = Event::Special({27, 91, 54, 126});
|
||||
const Event Event::Custom = Event::Special({0});
|
||||
|
||||
const Event Event::a = Event::Character("a");
|
||||
const Event Event::b = Event::Character("b");
|
||||
const Event Event::c = Event::Character("c");
|
||||
const Event Event::d = Event::Character("d");
|
||||
const Event Event::e = Event::Character("e");
|
||||
const Event Event::f = Event::Character("f");
|
||||
const Event Event::g = Event::Character("g");
|
||||
const Event Event::h = Event::Character("h");
|
||||
const Event Event::i = Event::Character("i");
|
||||
const Event Event::j = Event::Character("j");
|
||||
const Event Event::k = Event::Character("k");
|
||||
const Event Event::l = Event::Character("l");
|
||||
const Event Event::m = Event::Character("m");
|
||||
const Event Event::n = Event::Character("n");
|
||||
const Event Event::o = Event::Character("o");
|
||||
const Event Event::p = Event::Character("p");
|
||||
const Event Event::q = Event::Character("q");
|
||||
const Event Event::r = Event::Character("r");
|
||||
const Event Event::s = Event::Character("s");
|
||||
const Event Event::t = Event::Character("t");
|
||||
const Event Event::u = Event::Character("u");
|
||||
const Event Event::v = Event::Character("v");
|
||||
const Event Event::w = Event::Character("w");
|
||||
const Event Event::x = Event::Character("x");
|
||||
const Event Event::y = Event::Character("y");
|
||||
const Event Event::z = Event::Character("z");
|
||||
|
||||
const Event Event::A = Event::Character("A");
|
||||
const Event Event::B = Event::Character("B");
|
||||
const Event Event::C = Event::Character("C");
|
||||
const Event Event::D = Event::Character("D");
|
||||
const Event Event::E = Event::Character("E");
|
||||
const Event Event::F = Event::Character("F");
|
||||
const Event Event::G = Event::Character("G");
|
||||
const Event Event::H = Event::Character("H");
|
||||
const Event Event::I = Event::Character("I");
|
||||
const Event Event::J = Event::Character("J");
|
||||
const Event Event::K = Event::Character("K");
|
||||
const Event Event::L = Event::Character("L");
|
||||
const Event Event::M = Event::Character("M");
|
||||
const Event Event::N = Event::Character("N");
|
||||
const Event Event::O = Event::Character("O");
|
||||
const Event Event::P = Event::Character("P");
|
||||
const Event Event::Q = Event::Character("Q");
|
||||
const Event Event::R = Event::Character("R");
|
||||
const Event Event::S = Event::Character("S");
|
||||
const Event Event::T = Event::Character("T");
|
||||
const Event Event::U = Event::Character("U");
|
||||
const Event Event::V = Event::Character("V");
|
||||
const Event Event::W = Event::Character("W");
|
||||
const Event Event::X = Event::Character("X");
|
||||
const Event Event::Y = Event::Character("Y");
|
||||
const Event Event::Z = Event::Character("Z");
|
||||
|
||||
const Event Event::CtrlA = Event::Special("\x01");
|
||||
const Event Event::CtrlB = Event::Special("\x02");
|
||||
const Event Event::CtrlC = Event::Special("\x03");
|
||||
const Event Event::CtrlD = Event::Special("\x04");
|
||||
const Event Event::CtrlE = Event::Special("\x05");
|
||||
const Event Event::CtrlF = Event::Special("\x06");
|
||||
const Event Event::CtrlG = Event::Special("\x07");
|
||||
const Event Event::CtrlH = Event::Special("\x08");
|
||||
const Event Event::CtrlI = Event::Special("\x09");
|
||||
const Event Event::CtrlJ = Event::Special("\x0a");
|
||||
const Event Event::CtrlK = Event::Special("\x0b");
|
||||
const Event Event::CtrlL = Event::Special("\x0c");
|
||||
const Event Event::CtrlM = Event::Special("\x0d");
|
||||
const Event Event::CtrlN = Event::Special("\x0e");
|
||||
const Event Event::CtrlO = Event::Special("\x0f");
|
||||
const Event Event::CtrlP = Event::Special("\x10");
|
||||
const Event Event::CtrlQ = Event::Special("\x11");
|
||||
const Event Event::CtrlR = Event::Special("\x12");
|
||||
const Event Event::CtrlS = Event::Special("\x13");
|
||||
const Event Event::CtrlT = Event::Special("\x14");
|
||||
const Event Event::CtrlU = Event::Special("\x15");
|
||||
const Event Event::CtrlV = Event::Special("\x16");
|
||||
const Event Event::CtrlW = Event::Special("\x17");
|
||||
const Event Event::CtrlX = Event::Special("\x18");
|
||||
const Event Event::CtrlY = Event::Special("\x19");
|
||||
const Event Event::CtrlZ = Event::Special("\x1a");
|
||||
|
||||
const Event Event::AltA = Event::Special("\x1b""a");
|
||||
const Event Event::AltB = Event::Special("\x1b""b");
|
||||
const Event Event::AltC = Event::Special("\x1b""c");
|
||||
const Event Event::AltD = Event::Special("\x1b""d");
|
||||
const Event Event::AltE = Event::Special("\x1b""e");
|
||||
const Event Event::AltF = Event::Special("\x1b""f");
|
||||
const Event Event::AltG = Event::Special("\x1b""g");
|
||||
const Event Event::AltH = Event::Special("\x1b""h");
|
||||
const Event Event::AltI = Event::Special("\x1b""i");
|
||||
const Event Event::AltJ = Event::Special("\x1b""j");
|
||||
const Event Event::AltK = Event::Special("\x1b""k");
|
||||
const Event Event::AltL = Event::Special("\x1b""l");
|
||||
const Event Event::AltM = Event::Special("\x1b""m");
|
||||
const Event Event::AltN = Event::Special("\x1b""n");
|
||||
const Event Event::AltO = Event::Special("\x1b""o");
|
||||
const Event Event::AltP = Event::Special("\x1b""p");
|
||||
const Event Event::AltQ = Event::Special("\x1b""q");
|
||||
const Event Event::AltR = Event::Special("\x1b""r");
|
||||
const Event Event::AltS = Event::Special("\x1b""s");
|
||||
const Event Event::AltT = Event::Special("\x1b""t");
|
||||
const Event Event::AltU = Event::Special("\x1b""u");
|
||||
const Event Event::AltV = Event::Special("\x1b""v");
|
||||
const Event Event::AltW = Event::Special("\x1b""w");
|
||||
const Event Event::AltX = Event::Special("\x1b""x");
|
||||
const Event Event::AltY = Event::Special("\x1b""y");
|
||||
const Event Event::AltZ = Event::Special("\x1b""z");
|
||||
|
||||
const Event Event::CtrlAltA = Event::Special("\x1b\x01");
|
||||
const Event Event::CtrlAltB = Event::Special("\x1b\x02");
|
||||
const Event Event::CtrlAltC = Event::Special("\x1b\x03");
|
||||
const Event Event::CtrlAltD = Event::Special("\x1b\x04");
|
||||
const Event Event::CtrlAltE = Event::Special("\x1b\x05");
|
||||
const Event Event::CtrlAltF = Event::Special("\x1b\x06");
|
||||
const Event Event::CtrlAltG = Event::Special("\x1b\x07");
|
||||
const Event Event::CtrlAltH = Event::Special("\x1b\x08");
|
||||
const Event Event::CtrlAltI = Event::Special("\x1b\x09");
|
||||
const Event Event::CtrlAltJ = Event::Special("\x1b\x0a");
|
||||
const Event Event::CtrlAltK = Event::Special("\x1b\x0b");
|
||||
const Event Event::CtrlAltL = Event::Special("\x1b\x0c");
|
||||
const Event Event::CtrlAltM = Event::Special("\x1b\x0d");
|
||||
const Event Event::CtrlAltN = Event::Special("\x1b\x0e");
|
||||
const Event Event::CtrlAltO = Event::Special("\x1b\x0f");
|
||||
const Event Event::CtrlAltP = Event::Special("\x1b\x10");
|
||||
const Event Event::CtrlAltQ = Event::Special("\x1b\x11");
|
||||
const Event Event::CtrlAltR = Event::Special("\x1b\x12");
|
||||
const Event Event::CtrlAltS = Event::Special("\x1b\x13");
|
||||
const Event Event::CtrlAltT = Event::Special("\x1b\x14");
|
||||
const Event Event::CtrlAltU = Event::Special("\x1b\x15");
|
||||
const Event Event::CtrlAltV = Event::Special("\x1b\x16");
|
||||
const Event Event::CtrlAltW = Event::Special("\x1b\x17");
|
||||
const Event Event::CtrlAltX = Event::Special("\x1b\x18");
|
||||
const Event Event::CtrlAltY = Event::Special("\x1b\x19");
|
||||
const Event Event::CtrlAltZ = Event::Special("\x1b\x1a");
|
||||
|
||||
// NOLINTEND
|
||||
// clang-format on
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -132,7 +132,6 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
|
||||
// Read char from the terminal.
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
(void)timeout_microseconds;
|
||||
auto parser = TerminalInputParser(std::move(out));
|
||||
|
||||
char c;
|
||||
@ -560,6 +559,18 @@ Closure ScreenInteractive::WithRestoredIO(Closure fn) { // NOLINT
|
||||
};
|
||||
}
|
||||
|
||||
/// @brief Force FTXUI to handle or not handle Ctrl-C, even if the component
|
||||
/// catches the Event::CtrlC.
|
||||
void ScreenInteractive::ForceHandleCtrlC(bool force) {
|
||||
force_handle_ctrl_c_ = force;
|
||||
}
|
||||
|
||||
/// @brief Force FTXUI to handle or not handle Ctrl-Z, even if the component
|
||||
/// catches the Event::CtrlZ.
|
||||
void ScreenInteractive::ForceHandleCtrlZ(bool force) {
|
||||
force_handle_ctrl_z_ = force;
|
||||
}
|
||||
|
||||
/// @brief Return the currently active screen, or null if none.
|
||||
// static
|
||||
ScreenInteractive* ScreenInteractive::Active() {
|
||||
@ -568,7 +579,6 @@ ScreenInteractive* ScreenInteractive::Active() {
|
||||
|
||||
// private
|
||||
void ScreenInteractive::Install() {
|
||||
|
||||
frame_valid_ = false;
|
||||
|
||||
// Flush the buffer for stdout to ensure whatever the user has printed before
|
||||
@ -638,13 +648,31 @@ void ScreenInteractive::Install() {
|
||||
tcgetattr(STDIN_FILENO, &terminal);
|
||||
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
|
||||
|
||||
terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal.
|
||||
terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press.
|
||||
terminal.c_cc[VMIN] = 0;
|
||||
terminal.c_cc[VTIME] = 0;
|
||||
// auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
// fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
|
||||
// on_exit_functions.push([=] { fcntl(STDIN_FILENO, F_GETFL, oldf); });
|
||||
// Enabling raw terminal input mode
|
||||
terminal.c_iflag &= ~IGNBRK; // Disable ignoring break condition
|
||||
terminal.c_iflag &= ~BRKINT; // Disable break causing input and output to be
|
||||
// flushed
|
||||
terminal.c_iflag &= ~PARMRK; // Disable marking parity errors.
|
||||
terminal.c_iflag &= ~ISTRIP; // Disable striping 8th bit off characters.
|
||||
terminal.c_iflag &= ~INLCR; // Disable mapping NL to CR.
|
||||
terminal.c_iflag &= ~IGNCR; // Disable ignoring CR.
|
||||
terminal.c_iflag &= ~ICRNL; // Disable mapping CR to NL.
|
||||
terminal.c_iflag &= ~IXON; // Disable XON/XOFF flow control on output
|
||||
|
||||
terminal.c_lflag &= ~ECHO; // Disable echoing input characters.
|
||||
terminal.c_lflag &= ~ECHONL; // Disable echoing new line characters.
|
||||
terminal.c_lflag &= ~ICANON; // Disable Canonical mode.
|
||||
terminal.c_lflag &= ~ISIG; // Disable sending signal when hitting:
|
||||
// - => DSUSP
|
||||
// - C-Z => SUSP
|
||||
// - C-C => INTR
|
||||
// - C-d => QUIT
|
||||
terminal.c_lflag &= ~IEXTEN; // Disable 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);
|
||||
|
||||
@ -720,6 +748,7 @@ void ScreenInteractive::RunOnce(Component component) {
|
||||
}
|
||||
|
||||
// private
|
||||
// NOLINTNEXTLINE
|
||||
void ScreenInteractive::HandleTask(Component component, Task& task) {
|
||||
std::visit(
|
||||
[&](auto&& arg) {
|
||||
@ -745,7 +774,19 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
|
||||
}
|
||||
|
||||
arg.screen_ = this;
|
||||
component->OnEvent(arg);
|
||||
|
||||
const bool handled = component->OnEvent(arg);
|
||||
|
||||
if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) {
|
||||
RecordSignal(SIGABRT);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
if (arg == Event::CtrlZ && (!handled || force_handle_ctrl_z_)) {
|
||||
RecordSignal(SIGTSTP);
|
||||
}
|
||||
#endif
|
||||
|
||||
frame_valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
@ -64,4 +64,71 @@ TEST(ScreenInteractive, PostTaskToNonActive) {
|
||||
screen.Post([] {});
|
||||
}
|
||||
|
||||
TEST(ScreenInteractive, CtrlC) {
|
||||
auto screen = ScreenInteractive::FitComponent();
|
||||
bool called = false;
|
||||
auto component = Renderer([&] {
|
||||
if (!called) {
|
||||
called = true;
|
||||
screen.PostEvent(Event::CtrlC);
|
||||
}
|
||||
return text("");
|
||||
});
|
||||
screen.Loop(component);
|
||||
}
|
||||
|
||||
TEST(ScreenInteractive, CtrlC_Forced) {
|
||||
auto screen = ScreenInteractive::FitComponent();
|
||||
screen.ForceHandleCtrlC(true);
|
||||
auto component = Renderer([&] {
|
||||
screen.PostEvent(Event::CtrlC);
|
||||
return text("");
|
||||
});
|
||||
|
||||
int ctrl_c_count = 0;
|
||||
component |= CatchEvent([&](Event event) {
|
||||
if (event != Event::CtrlC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++ctrl_c_count;
|
||||
|
||||
if (ctrl_c_count == 100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
screen.Loop(component);
|
||||
|
||||
ASSERT_LE(ctrl_c_count, 50);
|
||||
}
|
||||
|
||||
TEST(ScreenInteractive, CtrlC_NotForced) {
|
||||
auto screen = ScreenInteractive::FitComponent();
|
||||
screen.ForceHandleCtrlC(false);
|
||||
auto component = Renderer([&] {
|
||||
screen.PostEvent(Event::CtrlC);
|
||||
return text("");
|
||||
});
|
||||
|
||||
int ctrl_c_count = 0;
|
||||
component |= CatchEvent([&](Event event) {
|
||||
if (event != Event::CtrlC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++ctrl_c_count;
|
||||
|
||||
if (ctrl_c_count == 100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
screen.Loop(component);
|
||||
|
||||
ASSERT_GE(ctrl_c_count, 50);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@ -170,15 +170,8 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||
return UNCOMPLETED;
|
||||
}
|
||||
|
||||
switch (Current()) {
|
||||
case 24: // CAN NOLINT
|
||||
case 26: // SUB NOLINT
|
||||
return DROP;
|
||||
|
||||
case '\x1B':
|
||||
return ParseESC();
|
||||
default:
|
||||
break;
|
||||
if (Current() == '\x1B') {
|
||||
return ParseESC();
|
||||
}
|
||||
|
||||
if (Current() < 32) { // C0 NOLINT
|
||||
@ -282,12 +275,25 @@ TerminalInputParser::Output TerminalInputParser::ParseESC() {
|
||||
return ParseCSI();
|
||||
case ']':
|
||||
return ParseOSC();
|
||||
default:
|
||||
|
||||
// Expecting 2 characters.
|
||||
case ' ':
|
||||
case '#':
|
||||
case '%':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case 'O':
|
||||
case 'N': {
|
||||
if (!Eat()) {
|
||||
return UNCOMPLETED;
|
||||
} else {
|
||||
return SPECIAL;
|
||||
}
|
||||
return SPECIAL;
|
||||
}
|
||||
// Expecting 1 character:
|
||||
default:
|
||||
return SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,24 @@ TEST(Event, EscapeKeyEnoughWait) {
|
||||
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) {
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
@ -335,8 +353,8 @@ TEST(Event, Control) {
|
||||
continue;
|
||||
cases.push_back({char(i), false});
|
||||
}
|
||||
cases.push_back({char(24), true});
|
||||
cases.push_back({char(26), true});
|
||||
cases.push_back({char(24), false});
|
||||
cases.push_back({char(26), false});
|
||||
cases.push_back({char(127), false});
|
||||
|
||||
for (auto test : cases) {
|
||||
@ -367,13 +385,11 @@ TEST(Event, Special) {
|
||||
std::vector<unsigned char> input;
|
||||
Event expected;
|
||||
} kTestCase[] = {
|
||||
// Arrow (defaut cursor mode)
|
||||
{str("\x1B[A"), Event::ArrowUp},
|
||||
{str("\x1B[B"), Event::ArrowDown},
|
||||
{str("\x1B[C"), Event::ArrowRight},
|
||||
{str("\x1B[D"), Event::ArrowLeft},
|
||||
{str("\x1B[H"), Event::Home},
|
||||
{str("\x1B[F"), Event::End},
|
||||
// Arrow (default cursor mode)
|
||||
{str("\x1B[A"), Event::ArrowUp}, {str("\x1B[B"), Event::ArrowDown},
|
||||
{str("\x1B[C"), Event::ArrowRight}, {str("\x1B[D"), Event::ArrowLeft},
|
||||
{str("\x1B[H"), Event::Home}, {str("\x1B[F"), Event::End},
|
||||
/*
|
||||
|
||||
// Arrow (application cursor mode)
|
||||
{str("\x1BOA"), Event::ArrowUp},
|
||||
@ -454,6 +470,7 @@ TEST(Event, Special) {
|
||||
|
||||
// Custom:
|
||||
{{0}, Event::Custom},
|
||||
*/
|
||||
};
|
||||
|
||||
for (auto test : kTestCase) {
|
||||
|
@ -828,7 +828,7 @@ void Canvas::DrawPixel(int x, int y, const Pixel& p) {
|
||||
}
|
||||
|
||||
/// @brief Draw a predefined image, with top-left corner at the given coordinate
|
||||
/// You can supply negative coordinates to align the image however you like -
|
||||
/// You can supply negative coordinates to align the image however you like -
|
||||
/// only the 'visible' portion will be drawn
|
||||
/// @param x the x coordinate corresponding to the top-left corner of the image.
|
||||
/// @param y the y coordinate corresponding to the top-left corner of the image.
|
||||
|
@ -10,17 +10,16 @@
|
||||
#include <utility> // for pair
|
||||
|
||||
#include "ftxui/screen/image.hpp"
|
||||
#include "ftxui/screen/string.hpp" // for string_width
|
||||
#include "ftxui/screen/string.hpp" // for string_width
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
namespace
|
||||
{
|
||||
Pixel& dev_null_pixel() {
|
||||
static Pixel pixel;
|
||||
return pixel;
|
||||
}
|
||||
namespace {
|
||||
Pixel& dev_null_pixel() {
|
||||
static Pixel pixel;
|
||||
return pixel;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Image::Image(int dimx, int dimy)
|
||||
: stencil{0, dimx - 1, 0, dimy - 1},
|
||||
|
@ -391,9 +391,9 @@ Screen::Screen(int dimx, int dimy) : Image{dimx, dimy} {
|
||||
#if defined(_WIN32)
|
||||
// The placement of this call is a bit weird, however we can assume that
|
||||
// anybody who instantiates a Screen object eventually wants to output
|
||||
// something to the console. If that is not the case, use an instance of Image instead.
|
||||
// As we require UTF8 for all input/output operations we will just switch to
|
||||
// UTF8 encoding here
|
||||
// something to the console. If that is not the case, use an instance of Image
|
||||
// instead. As we require UTF8 for all input/output operations we will just
|
||||
// switch to UTF8 encoding here
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
WindowsEmulateVT100Terminal();
|
||||
|
Loading…
Reference in New Issue
Block a user