Skip to content

Commit

Permalink
Enable raw keyboard input (#832)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
3 people authored Apr 28, 2024
1 parent d38b14f commit d386df6
Show file tree
Hide file tree
Showing 15 changed files with 644 additions and 203 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
152 changes: 30 additions & 122 deletions examples/component/print_key_press.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,140 +18,48 @@

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() {
auto screen = ScreenInteractive::TerminalOutput();

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);
Expand Down
38 changes: 36 additions & 2 deletions include/ftxui/component/event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }

Expand All @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions include/ftxui/component/mouse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ struct Mouse {
None = 3,
WheelUp = 4,
WheelDown = 5,
WheelLeft = 6, /// Supported terminal only.
WheelRight = 7, /// Supported terminal only.
};

enum Motion {
Expand Down
12 changes: 12 additions & 0 deletions include/ftxui/component/screen_interactive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions include/ftxui/dom/canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion include/ftxui/screen/screen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down
Loading

0 comments on commit d386df6

Please sign in to comment.