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