mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 18:59:59 +08:00
Support SIGTSTP and task posting. (#331)
- Add support for SIGTSTP: https://github.com/ArthurSonzogni/FTXUI/issues/330 This - Add support for task posting. This allows folks to defer function execution, and execute it directly below the main loop. The task are executed in a FIFO order.
This commit is contained in:
parent
62747a49b6
commit
9c4218c2a8
@ -18,6 +18,10 @@ Element gaugeDown(float ratio);
|
||||
Element gaugeDirection(float ratio, GaugeDirection);
|
||||
```
|
||||
|
||||
#### Component
|
||||
- Support SIGTSTP. (ctrl+z).
|
||||
- Support task posting. `ScreenInteractive::Post(Task)`.
|
||||
|
||||
2.0.0
|
||||
-----
|
||||
|
||||
|
@ -90,6 +90,7 @@ add_library(component
|
||||
include/ftxui/component/mouse.hpp
|
||||
include/ftxui/component/receiver.hpp
|
||||
include/ftxui/component/screen_interactive.hpp
|
||||
include/ftxui/component/task.hpp
|
||||
src/ftxui/component/button.cpp
|
||||
src/ftxui/component/catch_event.cpp
|
||||
src/ftxui/component/checkbox.cpp
|
||||
|
@ -32,8 +32,8 @@ Element make_grid() {
|
||||
};
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
float focus_x = 0.0f;
|
||||
float focus_y = 0.0f;
|
||||
float focus_x = 0.5f;
|
||||
float focus_y = 0.5f;
|
||||
|
||||
auto slider_x = Slider("x", &focus_x, 0.f, 1.f, 0.01f);
|
||||
auto slider_y = Slider("y", &focus_y, 0.f, 1.f, 0.01f);
|
||||
|
@ -18,14 +18,12 @@ int main() {
|
||||
// temporarily uninstall the terminal hook and execute the provided callback
|
||||
// function. This allow running the application in a non-interactive mode.
|
||||
auto btn_run = Button("Execute with restored IO", screen.WithRestoredIO([] {
|
||||
std::system("bash");
|
||||
std::cout << "This is a child program using stdin/stdout." << std::endl;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
std::cout << "Please enter 10 strings (" << i << "/10)" << std::flush;
|
||||
std::string input;
|
||||
std::getline(std::cin, input);
|
||||
}
|
||||
std::system("bash");
|
||||
}));
|
||||
|
||||
auto btn_quit = Button("Quit", screen.ExitLoopClosure());
|
||||
|
@ -34,7 +34,7 @@ std::vector<std::vector<ColorInfo>> ColorInfoSorted2D() {
|
||||
for (int i = 0; i < int(column.size()) - 1; ++i) {
|
||||
int best_index = i + 1;
|
||||
int best_distance = 255 * 255 * 3;
|
||||
for (size_t j = i + 1; j < column.size(); ++j) {
|
||||
for (int j = i + 1; j < column.size(); ++j) {
|
||||
int dx = column[i].red - column[j].red;
|
||||
int dy = column[i].green - column[j].green;
|
||||
int dz = column[i].blue - column[j].blue;
|
||||
|
@ -2,7 +2,8 @@
|
||||
#define FTXUI_COMPONENT_EVENT_HPP
|
||||
|
||||
#include <ftxui/component/mouse.hpp> // for Mouse
|
||||
#include <string> // for string, operator==
|
||||
#include <functional>
|
||||
#include <string> // for string, operator==
|
||||
#include <vector>
|
||||
|
||||
namespace ftxui {
|
||||
|
@ -7,9 +7,11 @@
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <thread> // for thread
|
||||
#include <variant> // for variant
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/task.hpp" // for Closure, Task
|
||||
#include "ftxui/screen/screen.hpp" // for Screen
|
||||
|
||||
namespace ftxui {
|
||||
@ -17,37 +19,38 @@ class ComponentBase;
|
||||
struct Event;
|
||||
|
||||
using Component = std::shared_ptr<ComponentBase>;
|
||||
class ScreenInteractivePrivate;
|
||||
|
||||
class ScreenInteractive : public Screen {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
static ScreenInteractive FixedSize(int dimx, int dimy);
|
||||
static ScreenInteractive Fullscreen();
|
||||
static ScreenInteractive FitComponent();
|
||||
static ScreenInteractive TerminalOutput();
|
||||
|
||||
void Loop(Component);
|
||||
Callback ExitLoopClosure();
|
||||
Closure ExitLoopClosure();
|
||||
|
||||
void Post(Task task);
|
||||
void PostEvent(Event event);
|
||||
|
||||
CapturedMouse CaptureMouse();
|
||||
|
||||
// Decorate a function. The outputted one will execute similarly to the
|
||||
// inputted one, but with the currently active screen terminal hooks
|
||||
// temporarily uninstalled.
|
||||
Callback WithRestoredIO(Callback);
|
||||
Closure WithRestoredIO(Closure);
|
||||
|
||||
private:
|
||||
void Install();
|
||||
void Uninstall();
|
||||
|
||||
void Main(Component component);
|
||||
ScreenInteractive* suspended_screen_ = nullptr;
|
||||
|
||||
void Draw(Component component);
|
||||
void EventLoop(Component component);
|
||||
void SigStop();
|
||||
|
||||
ScreenInteractive* suspended_screen_ = nullptr;
|
||||
enum class Dimension {
|
||||
FitComponent,
|
||||
Fixed,
|
||||
@ -61,8 +64,8 @@ class ScreenInteractive : public Screen {
|
||||
Dimension dimension,
|
||||
bool use_alternative_screen);
|
||||
|
||||
Sender<Event> event_sender_;
|
||||
Receiver<Event> event_receiver_;
|
||||
Sender<Task> task_sender_;
|
||||
Receiver<Task> task_receiver_;
|
||||
|
||||
std::string set_cursor_position;
|
||||
std::string reset_cursor_position;
|
||||
@ -75,6 +78,13 @@ class ScreenInteractive : public Screen {
|
||||
|
||||
bool mouse_captured = false;
|
||||
bool previous_frame_resized_ = false;
|
||||
|
||||
public:
|
||||
class Private {
|
||||
public:
|
||||
static void SigStop(ScreenInteractive& s) { return s.SigStop(); }
|
||||
};
|
||||
friend Private;
|
||||
};
|
||||
|
||||
} // namespace ftxui
|
||||
|
12
include/ftxui/component/task.hpp
Normal file
12
include/ftxui/component/task.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
#include "ftxui/component/event.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
using Closure = std::function<void()>;
|
||||
using Task = std::variant<Event, Closure>;
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2022 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
@ -151,16 +151,16 @@ extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
|
||||
auto screen =
|
||||
Screen::Create(Dimension::Fixed(width), Dimension::Fixed(height));
|
||||
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
parser.Add(data[i]);
|
||||
}
|
||||
|
||||
Event event;
|
||||
Task event;
|
||||
while (event_receiver->Receive(&event)) {
|
||||
component->OnEvent(event);
|
||||
component->OnEvent(std::get<Event>(event));
|
||||
auto document = component->Render();
|
||||
Render(screen, document);
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
#include <stdio.h> // for fileno, stdin
|
||||
#include <algorithm> // for copy, max, min
|
||||
#include <csignal> // for signal, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH
|
||||
#include <cstdlib> // for NULL
|
||||
#include <csignal> // for signal, raise, SIGTSTP, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH
|
||||
#include <cstdlib> // for NULL
|
||||
#include <functional> // for function
|
||||
#include <initializer_list> // for initializer_list
|
||||
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
|
||||
#include <stack> // for stack
|
||||
#include <thread> // for thread
|
||||
#include <utility> // for swap, move
|
||||
#include <vector> // for vector
|
||||
#include <type_traits> // for decay_t
|
||||
#include <utility> // for swap, move
|
||||
#include <variant> // for visit
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, MakeReceiver, Sender, SenderImpl, Receiver
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
|
||||
@ -32,8 +34,8 @@
|
||||
#endif
|
||||
#else
|
||||
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
|
||||
#include <termios.h> // for tcsetattr, tcgetattr, cc_t
|
||||
#include <unistd.h> // for STDIN_FILENO, read
|
||||
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
|
||||
#include <unistd.h> // for STDIN_FILENO, read
|
||||
#endif
|
||||
|
||||
// Quick exit is missing in standard CLang headers
|
||||
@ -45,6 +47,8 @@ namespace ftxui {
|
||||
|
||||
namespace {
|
||||
|
||||
ScreenInteractive* g_active_screen = nullptr;
|
||||
|
||||
void Flush() {
|
||||
// Emscripten doesn't implement flush. We interpret zero as flush.
|
||||
std::cout << '\0' << std::flush;
|
||||
@ -54,7 +58,7 @@ constexpr int timeout_milliseconds = 20;
|
||||
constexpr int timeout_microseconds = timeout_milliseconds * 1000;
|
||||
#if defined(_WIN32)
|
||||
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
auto console = GetStdHandle(STD_INPUT_HANDLE);
|
||||
auto parser = TerminalInputParser(out->Clone());
|
||||
while (!*quit) {
|
||||
@ -104,7 +108,7 @@ void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
||||
#include <emscripten.h>
|
||||
|
||||
// Read char from the terminal.
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
(void)timeout_microseconds;
|
||||
auto parser = TerminalInputParser(std::move(out));
|
||||
|
||||
@ -131,7 +135,7 @@ int CheckStdinReady(int usec_timeout) {
|
||||
}
|
||||
|
||||
// Read char from the terminal.
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
||||
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
|
||||
const int buffer_size = 100;
|
||||
|
||||
auto parser = TerminalInputParser(std::move(out));
|
||||
@ -200,7 +204,7 @@ const std::string DeviceStatusReport(DSRMode ps) {
|
||||
}
|
||||
|
||||
using SignalHandler = void(int);
|
||||
std::stack<ScreenInteractive::Callback> on_exit_functions;
|
||||
std::stack<Closure> on_exit_functions;
|
||||
void OnExit(int signal) {
|
||||
(void)signal;
|
||||
while (!on_exit_functions.empty()) {
|
||||
@ -211,14 +215,18 @@ void OnExit(int signal) {
|
||||
|
||||
auto install_signal_handler = [](int sig, SignalHandler handler) {
|
||||
auto old_signal_handler = std::signal(sig, handler);
|
||||
on_exit_functions.push([&] { std::signal(sig, old_signal_handler); });
|
||||
on_exit_functions.push([=] { std::signal(sig, old_signal_handler); });
|
||||
};
|
||||
|
||||
ScreenInteractive::Callback on_resize = [] {};
|
||||
Closure on_resize = [] {};
|
||||
void OnResize(int /* signal */) {
|
||||
on_resize();
|
||||
}
|
||||
|
||||
void OnSigStop(int /*signal*/) {
|
||||
ScreenInteractive::Private::SigStop(*g_active_screen);
|
||||
}
|
||||
|
||||
class CapturedMouseImpl : public CapturedMouseInterface {
|
||||
public:
|
||||
CapturedMouseImpl(std::function<void(void)> callback) : callback_(callback) {}
|
||||
@ -230,8 +238,6 @@ class CapturedMouseImpl : public CapturedMouseInterface {
|
||||
|
||||
} // namespace
|
||||
|
||||
ScreenInteractive* g_active_screen = nullptr;
|
||||
|
||||
ScreenInteractive::ScreenInteractive(int dimx,
|
||||
int dimy,
|
||||
Dimension dimension,
|
||||
@ -239,8 +245,7 @@ ScreenInteractive::ScreenInteractive(int dimx,
|
||||
: Screen(dimx, dimy),
|
||||
dimension_(dimension),
|
||||
use_alternative_screen_(use_alternative_screen) {
|
||||
event_receiver_ = MakeReceiver<Event>();
|
||||
event_sender_ = event_receiver_->MakeSender();
|
||||
task_receiver_ = MakeReceiver<Task>();
|
||||
}
|
||||
|
||||
// static
|
||||
@ -263,9 +268,12 @@ ScreenInteractive ScreenInteractive::FitComponent() {
|
||||
return ScreenInteractive(0, 0, Dimension::FitComponent, false);
|
||||
}
|
||||
|
||||
void ScreenInteractive::PostEvent(Event event) {
|
||||
void ScreenInteractive::Post(Task task) {
|
||||
if (!quit_)
|
||||
event_sender_->Send(event);
|
||||
task_sender_->Send(task);
|
||||
}
|
||||
void ScreenInteractive::PostEvent(Event event) {
|
||||
Post(event);
|
||||
}
|
||||
|
||||
CapturedMouse ScreenInteractive::CaptureMouse() {
|
||||
@ -314,7 +322,7 @@ void ScreenInteractive::Loop(Component component) {
|
||||
/// @brief Decorate a function. It executes the same way, but with the currently
|
||||
/// active screen terminal hooks temporarilly uninstalled during its execution.
|
||||
/// @param fn The function to decorate.
|
||||
ScreenInteractive::Callback ScreenInteractive::WithRestoredIO(Callback fn) {
|
||||
Closure ScreenInteractive::WithRestoredIO(Closure fn) {
|
||||
return [this, fn] {
|
||||
Uninstall();
|
||||
fn();
|
||||
@ -381,8 +389,11 @@ void ScreenInteractive::Install() {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
|
||||
|
||||
// Handle resize.
|
||||
on_resize = [&] { event_sender_->Send(Event::Special({0})); };
|
||||
on_resize = [&] { task_sender_->Send(Event::Special({0})); };
|
||||
install_signal_handler(SIGWINCH, OnResize);
|
||||
|
||||
// Handle SIGTSTP/SIGCONT.
|
||||
install_signal_handler(SIGTSTP, OnSigStop);
|
||||
#endif
|
||||
|
||||
auto enable = [&](std::vector<DECMode> parameters) {
|
||||
@ -418,8 +429,9 @@ void ScreenInteractive::Install() {
|
||||
Flush();
|
||||
|
||||
quit_ = false;
|
||||
task_sender_ = task_receiver_->MakeSender();
|
||||
event_listener_ =
|
||||
std::thread(&EventListener, &quit_, event_receiver_->MakeSender());
|
||||
std::thread(&EventListener, &quit_, task_receiver_->MakeSender());
|
||||
}
|
||||
|
||||
void ScreenInteractive::Uninstall() {
|
||||
@ -431,30 +443,43 @@ void ScreenInteractive::Uninstall() {
|
||||
|
||||
void ScreenInteractive::Main(Component component) {
|
||||
while (!quit_) {
|
||||
if (!event_receiver_->HasPending()) {
|
||||
if (!task_receiver_->HasPending()) {
|
||||
Draw(component);
|
||||
std::cout << ToString() << set_cursor_position;
|
||||
Flush();
|
||||
Clear();
|
||||
}
|
||||
|
||||
Event event;
|
||||
if (!event_receiver_->Receive(&event))
|
||||
Task task;
|
||||
if (!task_receiver_->Receive(&task))
|
||||
break;
|
||||
|
||||
if (event.is_cursor_reporting()) {
|
||||
cursor_x_ = event.cursor_x();
|
||||
cursor_y_ = event.cursor_y();
|
||||
continue;
|
||||
}
|
||||
std::visit(
|
||||
[&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if (event.is_mouse()) {
|
||||
event.mouse().x -= cursor_x_;
|
||||
event.mouse().y -= cursor_y_;
|
||||
}
|
||||
// Handle Event.
|
||||
if constexpr (std::is_same_v<T, Event>) {
|
||||
if (arg.is_cursor_reporting()) {
|
||||
cursor_x_ = arg.cursor_x();
|
||||
cursor_y_ = arg.cursor_y();
|
||||
return;
|
||||
}
|
||||
|
||||
event.screen_ = this;
|
||||
component->OnEvent(event);
|
||||
if (arg.is_mouse()) {
|
||||
arg.mouse().x -= cursor_x_;
|
||||
arg.mouse().y -= cursor_y_;
|
||||
}
|
||||
|
||||
arg.screen_ = this;
|
||||
component->OnEvent(arg);
|
||||
}
|
||||
|
||||
// Handle callback
|
||||
if constexpr (std::is_same_v<T, Closure>)
|
||||
arg();
|
||||
},
|
||||
task);
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,13 +562,31 @@ void ScreenInteractive::Draw(Component component) {
|
||||
}
|
||||
}
|
||||
|
||||
ScreenInteractive::Callback ScreenInteractive::ExitLoopClosure() {
|
||||
Closure ScreenInteractive::ExitLoopClosure() {
|
||||
return [this] {
|
||||
quit_ = true;
|
||||
event_sender_.reset();
|
||||
task_sender_.reset();
|
||||
};
|
||||
}
|
||||
|
||||
void ScreenInteractive::SigStop() {
|
||||
#if defined(_WIN32)
|
||||
// Windows do no support SIGTSTP.
|
||||
#else
|
||||
Post([&] {
|
||||
Uninstall();
|
||||
std::cout << reset_cursor_position;
|
||||
reset_cursor_position = "";
|
||||
std::cout << ResetPosition(/*clear=*/true);
|
||||
dimx_ = 0;
|
||||
dimy_ = 0;
|
||||
Flush();
|
||||
std::raise(SIGTSTP);
|
||||
Install();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ftxui.
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@ -1,15 +1,15 @@
|
||||
#include "ftxui/component/terminal_input_parser.hpp"
|
||||
|
||||
#include <algorithm> // for max
|
||||
#include <cstdint>
|
||||
#include <cstdint> // for uint32_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/task.hpp" // for Task
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
TerminalInputParser::TerminalInputParser(Sender<Event> out)
|
||||
TerminalInputParser::TerminalInputParser(Sender<Task> out)
|
||||
: out_(std::move(out)) {}
|
||||
|
||||
void TerminalInputParser::Timeout(int time) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "ftxui/component/event.hpp" // for Event (ptr only)
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/component/receiver.hpp" // for Sender
|
||||
#include "ftxui/component/task.hpp" // for Task
|
||||
|
||||
namespace ftxui {
|
||||
struct Event;
|
||||
@ -15,7 +16,7 @@ struct Event;
|
||||
// Parse a sequence of |char| accross |time|. Produces |Event|.
|
||||
class TerminalInputParser {
|
||||
public:
|
||||
TerminalInputParser(Sender<Event> out);
|
||||
TerminalInputParser(Sender<Task> out);
|
||||
void Timeout(int time);
|
||||
void Add(char c);
|
||||
|
||||
@ -57,7 +58,7 @@ class TerminalInputParser {
|
||||
Output ParseMouse(bool altered, bool pressed, std::vector<int> arguments);
|
||||
Output ParseCursorReporting(std::vector<int> arguments);
|
||||
|
||||
Sender<Event> out_;
|
||||
Sender<Task> out_;
|
||||
int position_ = -1;
|
||||
int timeout_ = 0;
|
||||
std::string pending_;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
|
||||
#include <algorithm> // for max
|
||||
#include <memory> // for unique_ptr, allocator
|
||||
#include <variant> // for get
|
||||
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::Escape
|
||||
#include "ftxui/component/receiver.hpp" // for MakeReceiver, ReceiverImpl
|
||||
@ -18,61 +19,61 @@ TEST(Event, Character) {
|
||||
for (char c = 'A'; c <= 'Z'; ++c)
|
||||
basic_char.push_back(c);
|
||||
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
for (char c : basic_char)
|
||||
parser.Add(c);
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
for (char c : basic_char) {
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_character());
|
||||
EXPECT_EQ(c, received.character()[0]);
|
||||
EXPECT_TRUE(std::get<Event>(received).is_character());
|
||||
EXPECT_EQ(c, std::get<Event>(received).character()[0]);
|
||||
}
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, EscapeKeyWithoutWaiting) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, EscapeKeyNotEnoughWait) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
parser.Timeout(49);
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, EscapeKeyEnoughWait) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
parser.Timeout(50);
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_EQ(received, Event::Escape);
|
||||
EXPECT_EQ(std::get<Event>(received), Event::Escape);
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, MouseLeftClick) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
@ -88,17 +89,17 @@ TEST(Event, MouseLeftClick) {
|
||||
parser.Add('M');
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_mouse());
|
||||
EXPECT_EQ(Mouse::Left, received.mouse().button);
|
||||
EXPECT_EQ(12, received.mouse().x);
|
||||
EXPECT_EQ(42, received.mouse().y);
|
||||
EXPECT_TRUE(std::get<Event>(received).is_mouse());
|
||||
EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button);
|
||||
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
|
||||
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, MouseMiddleClick) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
@ -114,17 +115,17 @@ TEST(Event, MouseMiddleClick) {
|
||||
parser.Add('M');
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_mouse());
|
||||
EXPECT_EQ(Mouse::Middle, received.mouse().button);
|
||||
EXPECT_EQ(12, received.mouse().x);
|
||||
EXPECT_EQ(42, received.mouse().y);
|
||||
EXPECT_TRUE(std::get<Event>(received).is_mouse());
|
||||
EXPECT_EQ(Mouse::Middle, std::get<Event>(received).mouse().button);
|
||||
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
|
||||
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, MouseRightClick) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
@ -140,12 +141,12 @@ TEST(Event, MouseRightClick) {
|
||||
parser.Add('M');
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_mouse());
|
||||
EXPECT_EQ(Mouse::Right, received.mouse().button);
|
||||
EXPECT_EQ(12, received.mouse().x);
|
||||
EXPECT_EQ(42, received.mouse().y);
|
||||
EXPECT_TRUE(std::get<Event>(received).is_mouse());
|
||||
EXPECT_EQ(Mouse::Right, std::get<Event>(received).mouse().button);
|
||||
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
|
||||
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
@ -216,16 +217,16 @@ TEST(Event, UTF8) {
|
||||
|
||||
};
|
||||
for (auto test : kTestCase) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
for (auto input : test.input)
|
||||
parser.Add(input);
|
||||
}
|
||||
Event received;
|
||||
Task received;
|
||||
if (test.valid) {
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_character());
|
||||
EXPECT_TRUE(std::get<Event>(received).is_character());
|
||||
}
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
@ -5,14 +5,14 @@
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
|
||||
using namespace ftxui;
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
parser.Add(data[i]);
|
||||
}
|
||||
|
||||
Event received;
|
||||
Task received;
|
||||
while (event_receiver->Receive(&received))
|
||||
;
|
||||
return 0; // Non-zero return values are reserved for future use.
|
||||
|
Loading…
Reference in New Issue
Block a user