2021-05-02 02:40:35 +08:00
|
|
|
#include <stdio.h> // for fileno, stdin
|
|
|
|
#include <algorithm> // for copy, max, min
|
2021-05-10 02:32:27 +08:00
|
|
|
#include <csignal> // for signal, SIGINT, SIGWINCH
|
2021-05-02 02:40:35 +08:00
|
|
|
#include <cstdlib> // for exit, NULL
|
2021-05-10 02:32:27 +08:00
|
|
|
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
|
|
|
|
#include <stack> // for stack
|
|
|
|
#include <thread> // for thread
|
|
|
|
#include <utility> // for move
|
|
|
|
#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, SenderImpl, MakeReceiver
|
|
|
|
#include "ftxui/component/screen_interactive.hpp"
|
|
|
|
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
|
2021-05-02 02:40:35 +08:00
|
|
|
#include "ftxui/dom/node.hpp" // for Node, Render
|
|
|
|
#include "ftxui/dom/requirement.hpp" // for Requirement
|
2021-05-10 02:32:27 +08:00
|
|
|
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimensions, Terminal
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2020-03-25 15:54:03 +08:00
|
|
|
#if defined(_WIN32)
|
2020-08-16 06:24:18 +08:00
|
|
|
#define DEFINE_CONSOLEV2_PROPERTIES
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#ifndef NOMINMAX
|
|
|
|
#define NOMINMAX
|
|
|
|
#endif
|
|
|
|
#include <Windows.h>
|
|
|
|
#ifndef UNICODE
|
|
|
|
#error Must be compiled in UNICODE mode
|
|
|
|
#endif
|
2020-03-21 23:21:32 +08:00
|
|
|
#else
|
2021-05-10 02:32:27 +08:00
|
|
|
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
|
|
|
|
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
|
|
|
|
#include <unistd.h> // for STDIN_FILENO, read
|
2020-03-21 23:21:32 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Quick exit is missing in standard CLang headers
|
2020-03-24 04:26:00 +08:00
|
|
|
#if defined(__clang__) && defined(__APPLE__)
|
2020-08-16 06:24:18 +08:00
|
|
|
#define quick_exit(a) exit(a)
|
2020-02-03 04:27:46 +08:00
|
|
|
#endif
|
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
namespace ftxui {
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2020-08-09 20:53:56 +08:00
|
|
|
namespace {
|
2020-03-25 08:15:46 +08:00
|
|
|
|
2021-03-22 05:54:39 +08:00
|
|
|
void Flush() {
|
|
|
|
// Emscripten doesn't implement flush. We interpret zero as flush.
|
2021-04-25 22:58:16 +08:00
|
|
|
std::cout << '\0' << std::flush;
|
2021-03-22 05:54:39 +08:00
|
|
|
}
|
|
|
|
|
2020-10-25 07:57:56 +08:00
|
|
|
constexpr int timeout_milliseconds = 20;
|
|
|
|
constexpr int timeout_microseconds = timeout_milliseconds * 1000;
|
2020-03-25 15:54:03 +08:00
|
|
|
#if defined(_WIN32)
|
2020-03-25 08:38:50 +08:00
|
|
|
|
2021-05-02 02:40:35 +08:00
|
|
|
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
2020-03-25 08:38:50 +08:00
|
|
|
auto console = GetStdHandle(STD_INPUT_HANDLE);
|
2020-10-25 07:57:56 +08:00
|
|
|
auto parser = TerminalInputParser(out->Clone());
|
2020-03-25 08:38:50 +08:00
|
|
|
while (!*quit) {
|
|
|
|
// Throttle ReadConsoleInput by waiting 250ms, this wait function will
|
|
|
|
// return if there is input in the console.
|
2020-10-25 07:57:56 +08:00
|
|
|
auto wait_result = WaitForSingleObject(console, timeout_milliseconds);
|
|
|
|
if (wait_result == WAIT_TIMEOUT) {
|
|
|
|
parser.Timeout(timeout_milliseconds);
|
2020-03-25 08:38:50 +08:00
|
|
|
continue;
|
2020-10-25 07:57:56 +08:00
|
|
|
}
|
2020-03-25 08:38:50 +08:00
|
|
|
|
|
|
|
DWORD number_of_events = 0;
|
|
|
|
if (!GetNumberOfConsoleInputEvents(console, &number_of_events))
|
|
|
|
continue;
|
|
|
|
if (number_of_events <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::vector<INPUT_RECORD> records{number_of_events};
|
|
|
|
DWORD number_of_events_read = 0;
|
2021-05-02 02:40:35 +08:00
|
|
|
ReadConsoleInput(console, records.data(), (DWORD)records.size(),
|
2020-03-25 08:38:50 +08:00
|
|
|
&number_of_events_read);
|
|
|
|
records.resize(number_of_events_read);
|
|
|
|
|
|
|
|
for (const auto& r : records) {
|
|
|
|
switch (r.EventType) {
|
|
|
|
case KEY_EVENT: {
|
|
|
|
auto key_event = r.Event.KeyEvent;
|
|
|
|
// ignore UP key events
|
|
|
|
if (key_event.bKeyDown == FALSE)
|
|
|
|
continue;
|
2020-10-25 07:57:56 +08:00
|
|
|
parser.Add((char)key_event.uChar.UnicodeChar);
|
2020-03-25 08:38:50 +08:00
|
|
|
} break;
|
|
|
|
case WINDOW_BUFFER_SIZE_EVENT:
|
2020-10-25 07:57:56 +08:00
|
|
|
out->Send(Event::Special({0}));
|
2020-03-25 08:38:50 +08:00
|
|
|
break;
|
|
|
|
case MENU_EVENT:
|
|
|
|
case FOCUS_EVENT:
|
|
|
|
case MOUSE_EVENT:
|
|
|
|
// TODO(mauve): Implement later.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-22 05:54:39 +08:00
|
|
|
#elif defined(__EMSCRIPTEN__)
|
|
|
|
#include <emscripten.h>
|
|
|
|
|
|
|
|
// Read char from the terminal.
|
|
|
|
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
|
|
|
(void)timeout_microseconds;
|
|
|
|
auto parser = TerminalInputParser(std::move(out));
|
2020-03-25 08:38:50 +08:00
|
|
|
|
2021-03-22 05:54:39 +08:00
|
|
|
char c;
|
|
|
|
while (!*quit) {
|
2021-05-10 02:32:27 +08:00
|
|
|
while (read(STDIN_FILENO, &c, 1), c)
|
2021-03-22 05:54:39 +08:00
|
|
|
parser.Add(c);
|
|
|
|
|
|
|
|
emscripten_sleep(1);
|
|
|
|
parser.Timeout(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2021-05-02 02:40:35 +08:00
|
|
|
#include <sys/time.h> // for timeval
|
2021-03-21 05:45:21 +08:00
|
|
|
|
2020-05-02 08:02:04 +08:00
|
|
|
int CheckStdinReady(int usec_timeout) {
|
|
|
|
timeval tv = {0, usec_timeout};
|
|
|
|
fd_set fds;
|
|
|
|
FD_ZERO(&fds);
|
|
|
|
FD_SET(STDIN_FILENO, &fds);
|
|
|
|
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
|
|
|
|
return FD_ISSET(STDIN_FILENO, &fds);
|
|
|
|
}
|
|
|
|
|
2020-03-25 08:15:46 +08:00
|
|
|
// Read char from the terminal.
|
2020-10-25 07:57:56 +08:00
|
|
|
void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
2020-05-02 08:02:04 +08:00
|
|
|
const int buffer_size = 100;
|
2020-10-25 07:57:56 +08:00
|
|
|
|
|
|
|
auto parser = TerminalInputParser(std::move(out));
|
2020-05-02 05:33:21 +08:00
|
|
|
|
|
|
|
while (!*quit) {
|
2020-10-25 07:57:56 +08:00
|
|
|
if (!CheckStdinReady(timeout_microseconds)) {
|
|
|
|
parser.Timeout(timeout_milliseconds);
|
2020-05-02 05:33:21 +08:00
|
|
|
continue;
|
2020-10-25 07:57:56 +08:00
|
|
|
}
|
|
|
|
|
2020-05-02 08:02:04 +08:00
|
|
|
char buff[buffer_size];
|
|
|
|
int l = read(fileno(stdin), buff, buffer_size);
|
|
|
|
for (int i = 0; i < l; ++i)
|
2020-10-25 07:57:56 +08:00
|
|
|
parser.Add(buff[i]);
|
2020-05-02 05:33:21 +08:00
|
|
|
}
|
2020-03-25 08:15:46 +08:00
|
|
|
}
|
|
|
|
|
2020-03-25 08:38:50 +08:00
|
|
|
#endif
|
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
const std::string CSI = "\x1b[";
|
|
|
|
|
|
|
|
// DEC: Digital Equipment Corporation
|
|
|
|
enum class DECMode {
|
|
|
|
kLineWrap = 7,
|
|
|
|
kMouseX10 = 9,
|
|
|
|
kCursor = 25,
|
|
|
|
kMouseVt200 = 1000,
|
|
|
|
kMouseAnyEvent = 1003,
|
|
|
|
kMouseUtf8 = 1005,
|
|
|
|
kMouseSgrExtMode = 1006,
|
|
|
|
kMouseUrxvtMode = 1015,
|
|
|
|
kMouseSgrPixelsMode = 1016,
|
|
|
|
kAlternateScreen = 1049,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Device Status Report (DSR) {
|
|
|
|
enum class DSRMode {
|
|
|
|
kCursor = 6,
|
|
|
|
};
|
2019-06-30 00:52:58 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
const std::string Serialize(std::vector<DECMode> parameters) {
|
|
|
|
bool first = true;
|
|
|
|
std::string out;
|
|
|
|
for (DECMode parameter : parameters) {
|
|
|
|
if (!first)
|
|
|
|
out += ";";
|
|
|
|
out += std::to_string(int(parameter));
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
2019-07-01 05:53:56 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
// DEC Private Mode Set (DECSET)
|
|
|
|
const std::string Set(std::vector<DECMode> parameters) {
|
|
|
|
return CSI + "?" + Serialize(parameters) + "h";
|
|
|
|
}
|
2021-04-06 04:03:37 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
// DEC Private Mode Reset (DECRST)
|
|
|
|
const std::string Reset(std::vector<DECMode> parameters) {
|
|
|
|
return CSI + "?" + Serialize(parameters) + "l";
|
|
|
|
}
|
2021-04-25 00:16:13 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
// Device Status Report (DSR)
|
|
|
|
const std::string DeviceStatusReport(DSRMode ps) {
|
|
|
|
return CSI + std::to_string(int(ps)) + "n";
|
|
|
|
}
|
2020-05-03 02:39:56 +08:00
|
|
|
|
2020-03-23 16:23:57 +08:00
|
|
|
using SignalHandler = void(int);
|
2019-06-30 00:52:58 +08:00
|
|
|
std::stack<std::function<void()>> on_exit_functions;
|
|
|
|
void OnExit(int signal) {
|
|
|
|
while (!on_exit_functions.empty()) {
|
|
|
|
on_exit_functions.top()();
|
|
|
|
on_exit_functions.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signal == SIGINT)
|
2020-07-17 02:58:58 +08:00
|
|
|
std::exit(SIGINT);
|
2019-06-30 00:52:58 +08:00
|
|
|
}
|
|
|
|
|
2020-03-23 16:23:57 +08:00
|
|
|
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); });
|
|
|
|
};
|
|
|
|
|
2020-03-24 04:26:00 +08:00
|
|
|
std::function<void()> on_resize = [] {};
|
2019-07-01 05:59:27 +08:00
|
|
|
void OnResize(int /* signal */) {
|
2019-07-01 05:53:56 +08:00
|
|
|
on_resize();
|
|
|
|
}
|
|
|
|
|
2021-05-02 00:13:56 +08:00
|
|
|
class CapturedMouseImpl : public CapturedMouseInterface {
|
|
|
|
public:
|
2021-05-02 02:40:35 +08:00
|
|
|
CapturedMouseImpl(std::function<void(void)> callback) : callback_(callback) {}
|
2021-05-02 00:13:56 +08:00
|
|
|
~CapturedMouseImpl() override { callback_(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::function<void(void)> callback_;
|
|
|
|
};
|
|
|
|
|
2020-08-09 20:53:56 +08:00
|
|
|
} // namespace
|
|
|
|
|
2020-05-03 02:39:56 +08:00
|
|
|
ScreenInteractive::ScreenInteractive(int dimx,
|
|
|
|
int dimy,
|
|
|
|
Dimension dimension,
|
|
|
|
bool use_alternative_screen)
|
|
|
|
: Screen(dimx, dimy),
|
|
|
|
dimension_(dimension),
|
|
|
|
use_alternative_screen_(use_alternative_screen) {
|
2020-03-25 07:07:41 +08:00
|
|
|
event_receiver_ = MakeReceiver<Event>();
|
|
|
|
event_sender_ = event_receiver_->MakeSender();
|
2020-03-25 06:26:55 +08:00
|
|
|
}
|
|
|
|
|
2018-10-10 01:06:03 +08:00
|
|
|
ScreenInteractive::~ScreenInteractive() {}
|
|
|
|
|
2019-01-05 09:03:49 +08:00
|
|
|
// static
|
2019-01-27 04:52:55 +08:00
|
|
|
ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
|
2020-05-03 02:39:56 +08:00
|
|
|
return ScreenInteractive(dimx, dimy, Dimension::Fixed, false);
|
2019-01-05 09:03:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
ScreenInteractive ScreenInteractive::Fullscreen() {
|
2020-05-03 02:39:56 +08:00
|
|
|
return ScreenInteractive(0, 0, Dimension::Fullscreen, true);
|
2019-01-05 09:03:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
ScreenInteractive ScreenInteractive::TerminalOutput() {
|
2020-05-03 02:39:56 +08:00
|
|
|
return ScreenInteractive(0, 0, Dimension::TerminalOutput, false);
|
2019-01-05 09:03:49 +08:00
|
|
|
}
|
|
|
|
|
2019-01-19 07:20:29 +08:00
|
|
|
// static
|
|
|
|
ScreenInteractive ScreenInteractive::FitComponent() {
|
2020-05-03 02:39:56 +08:00
|
|
|
return ScreenInteractive(0, 0, Dimension::FitComponent, false);
|
2019-01-19 07:20:29 +08:00
|
|
|
}
|
|
|
|
|
2019-02-02 23:28:44 +08:00
|
|
|
void ScreenInteractive::PostEvent(Event event) {
|
2020-03-25 08:15:46 +08:00
|
|
|
if (!quit_)
|
|
|
|
event_sender_->Send(event);
|
2019-01-27 09:33:06 +08:00
|
|
|
}
|
|
|
|
|
2021-05-02 00:13:56 +08:00
|
|
|
CapturedMouse ScreenInteractive::CaptureMouse() {
|
|
|
|
if (mouse_captured)
|
|
|
|
return nullptr;
|
|
|
|
mouse_captured = true;
|
|
|
|
return std::make_unique<CapturedMouseImpl>(
|
|
|
|
[this] { mouse_captured = false; });
|
|
|
|
}
|
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
void ScreenInteractive::Loop(Component component) {
|
2020-05-02 05:48:22 +08:00
|
|
|
// Install a SIGINT handler and restore the old handler on exit.
|
|
|
|
auto old_sigint_handler = std::signal(SIGINT, OnExit);
|
|
|
|
on_exit_functions.push(
|
|
|
|
[old_sigint_handler]() { std::signal(SIGINT, old_sigint_handler); });
|
|
|
|
|
2019-06-30 00:52:58 +08:00
|
|
|
// Save the old terminal configuration and restore it on exit.
|
2020-03-25 15:54:03 +08:00
|
|
|
#if defined(_WIN32)
|
2020-03-22 18:29:33 +08:00
|
|
|
// Enable VT processing on stdout and stdin
|
|
|
|
auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
|
|
|
|
DWORD out_mode = 0;
|
|
|
|
DWORD in_mode = 0;
|
|
|
|
GetConsoleMode(stdout_handle, &out_mode);
|
|
|
|
GetConsoleMode(stdin_handle, &in_mode);
|
|
|
|
on_exit_functions.push([=] { SetConsoleMode(stdout_handle, out_mode); });
|
|
|
|
on_exit_functions.push([=] { SetConsoleMode(stdin_handle, in_mode); });
|
|
|
|
|
2020-07-17 02:58:58 +08:00
|
|
|
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
|
|
|
|
const int enable_virtual_terminal_processing = 0x0004;
|
|
|
|
const int disable_newline_auto_return = 0x0008;
|
|
|
|
out_mode |= enable_virtual_terminal_processing;
|
|
|
|
out_mode |= disable_newline_auto_return;
|
|
|
|
|
|
|
|
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
|
|
|
|
const int enable_line_input = 0x0002;
|
|
|
|
const int enable_echo_input = 0x0004;
|
|
|
|
const int enable_virtual_terminal_input = 0x0200;
|
|
|
|
const int enable_window_input = 0x0008;
|
|
|
|
in_mode &= ~enable_echo_input;
|
|
|
|
in_mode &= ~enable_line_input;
|
|
|
|
in_mode |= enable_virtual_terminal_input;
|
|
|
|
in_mode |= enable_window_input;
|
2020-03-22 18:29:33 +08:00
|
|
|
|
|
|
|
SetConsoleMode(stdin_handle, in_mode);
|
|
|
|
SetConsoleMode(stdout_handle, out_mode);
|
2020-03-21 23:21:32 +08:00
|
|
|
#else
|
2020-03-22 18:29:33 +08:00
|
|
|
struct termios terminal;
|
|
|
|
tcgetattr(STDIN_FILENO, &terminal);
|
|
|
|
on_exit_functions.push([=] { tcsetattr(STDIN_FILENO, TCSANOW, &terminal); });
|
|
|
|
|
|
|
|
terminal.c_lflag &= ~ICANON; // Non canonique terminal.
|
|
|
|
terminal.c_lflag &= ~ECHO; // Do not print after a key press.
|
2020-05-02 08:02:04 +08:00
|
|
|
terminal.c_cc[VMIN] = 0;
|
|
|
|
terminal.c_cc[VTIME] = 0;
|
2020-05-21 02:51:07 +08:00
|
|
|
// 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); });
|
2020-05-02 08:02:04 +08:00
|
|
|
|
2020-03-22 18:29:33 +08:00
|
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
|
2020-03-24 08:26:06 +08:00
|
|
|
|
|
|
|
// Handle resize.
|
2020-03-25 08:38:50 +08:00
|
|
|
on_resize = [&] { event_sender_->Send(Event::Special({0})); };
|
2020-03-24 08:26:06 +08:00
|
|
|
install_signal_handler(SIGWINCH, OnResize);
|
2020-03-21 23:21:32 +08:00
|
|
|
#endif
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
// Commit state:
|
|
|
|
auto flush = [&] {
|
|
|
|
Flush();
|
|
|
|
on_exit_functions.push([] { Flush(); });
|
|
|
|
};
|
|
|
|
|
|
|
|
auto enable = [&](std::vector<DECMode> parameters) {
|
|
|
|
std::cout << Set(parameters);
|
|
|
|
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
|
|
|
};
|
|
|
|
|
|
|
|
auto disable = [&](std::vector<DECMode> parameters) {
|
|
|
|
std::cout << Reset(parameters);
|
|
|
|
on_exit_functions.push([=] { std::cout << Set(parameters); });
|
|
|
|
};
|
|
|
|
|
|
|
|
flush();
|
|
|
|
|
2021-03-16 16:46:02 +08:00
|
|
|
if (use_alternative_screen_) {
|
2021-04-25 21:22:38 +08:00
|
|
|
enable({
|
|
|
|
DECMode::kAlternateScreen,
|
|
|
|
});
|
2021-03-16 16:46:02 +08:00
|
|
|
}
|
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
// On exit, reset cursor one line after the current drawing.
|
|
|
|
on_exit_functions.push(
|
|
|
|
[=] { std::cout << reset_cursor_position << std::endl; });
|
|
|
|
|
|
|
|
disable({
|
|
|
|
DECMode::kCursor,
|
|
|
|
DECMode::kLineWrap,
|
|
|
|
});
|
|
|
|
|
|
|
|
enable({
|
2021-05-02 02:40:35 +08:00
|
|
|
// DECMode::kMouseVt200,
|
2021-04-25 21:22:38 +08:00
|
|
|
DECMode::kMouseAnyEvent,
|
|
|
|
DECMode::kMouseUtf8,
|
|
|
|
DECMode::kMouseSgrExtMode,
|
2019-06-30 00:52:58 +08:00
|
|
|
});
|
|
|
|
|
2021-04-25 22:58:16 +08:00
|
|
|
flush();
|
|
|
|
|
2020-03-25 08:38:50 +08:00
|
|
|
auto event_listener =
|
2020-10-25 07:57:56 +08:00
|
|
|
std::thread(&EventListener, &quit_, event_receiver_->MakeSender());
|
2019-01-27 09:33:06 +08:00
|
|
|
|
2020-03-23 16:23:57 +08:00
|
|
|
// The main loop.
|
2019-01-07 00:10:35 +08:00
|
|
|
while (!quit_) {
|
2020-06-01 21:57:18 +08:00
|
|
|
if (!event_receiver_->HasPending()) {
|
|
|
|
std::cout << reset_cursor_position << ResetPosition();
|
2021-04-25 00:16:13 +08:00
|
|
|
static int i = -2;
|
2021-04-25 21:22:38 +08:00
|
|
|
if (i % 10 == 0)
|
|
|
|
std::cout << DeviceStatusReport(DSRMode::kCursor);
|
2021-04-25 00:16:13 +08:00
|
|
|
++i;
|
2020-06-01 21:57:18 +08:00
|
|
|
Draw(component);
|
2021-03-22 05:54:39 +08:00
|
|
|
std::cout << ToString() << set_cursor_position;
|
|
|
|
Flush();
|
2020-06-01 21:57:18 +08:00
|
|
|
Clear();
|
|
|
|
}
|
2021-04-25 00:16:13 +08:00
|
|
|
|
2020-03-25 06:26:55 +08:00
|
|
|
Event event;
|
2021-04-25 00:16:13 +08:00
|
|
|
if (!event_receiver_->Receive(&event))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (event.is_cursor_reporting()) {
|
2021-04-25 21:22:38 +08:00
|
|
|
cursor_x_ = event.cursor_x();
|
|
|
|
cursor_y_ = event.cursor_y();
|
2021-04-25 00:16:13 +08:00
|
|
|
continue;
|
2021-04-19 04:33:41 +08:00
|
|
|
}
|
2021-04-25 00:16:13 +08:00
|
|
|
|
2021-04-25 21:22:38 +08:00
|
|
|
if (event.is_mouse()) {
|
|
|
|
event.mouse().x -= cursor_x_;
|
|
|
|
event.mouse().y -= cursor_y_;
|
|
|
|
}
|
2021-04-25 00:16:13 +08:00
|
|
|
|
2021-05-02 02:40:35 +08:00
|
|
|
event.screen_ = this;
|
2021-04-25 00:16:13 +08:00
|
|
|
component->OnEvent(event);
|
2019-01-07 00:10:35 +08:00
|
|
|
}
|
2020-03-25 08:15:46 +08:00
|
|
|
|
2020-03-25 08:38:50 +08:00
|
|
|
event_listener.join();
|
2019-06-30 00:52:58 +08:00
|
|
|
OnExit(0);
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
void ScreenInteractive::Draw(Component component) {
|
2019-01-13 01:24:46 +08:00
|
|
|
auto document = component->Render();
|
2020-02-12 05:34:01 +08:00
|
|
|
int dimx = 0;
|
|
|
|
int dimy = 0;
|
2019-01-07 00:10:35 +08:00
|
|
|
switch (dimension_) {
|
2019-01-05 09:03:49 +08:00
|
|
|
case Dimension::Fixed:
|
2019-01-13 01:24:46 +08:00
|
|
|
dimx = dimx_;
|
|
|
|
dimy = dimy_;
|
2019-01-05 09:03:49 +08:00
|
|
|
break;
|
|
|
|
case Dimension::TerminalOutput:
|
|
|
|
document->ComputeRequirement();
|
|
|
|
dimx = Terminal::Size().dimx;
|
2020-06-01 22:13:29 +08:00
|
|
|
dimy = document->requirement().min_y;
|
2019-01-05 09:03:49 +08:00
|
|
|
break;
|
|
|
|
case Dimension::Fullscreen:
|
|
|
|
dimx = Terminal::Size().dimx;
|
|
|
|
dimy = Terminal::Size().dimy;
|
|
|
|
break;
|
2019-01-19 07:20:29 +08:00
|
|
|
case Dimension::FitComponent:
|
2019-01-21 06:04:10 +08:00
|
|
|
auto terminal = Terminal::Size();
|
2019-01-19 07:20:29 +08:00
|
|
|
document->ComputeRequirement();
|
2020-06-01 22:13:29 +08:00
|
|
|
dimx = std::min(document->requirement().min_x, terminal.dimx);
|
|
|
|
dimy = std::min(document->requirement().min_y, terminal.dimy);
|
2019-01-19 07:20:29 +08:00
|
|
|
break;
|
2019-01-05 09:03:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-30 00:52:58 +08:00
|
|
|
// Resize the screen if needed.
|
2019-01-05 09:03:49 +08:00
|
|
|
if (dimx != dimx_ || dimy != dimy_) {
|
|
|
|
dimx_ = dimx;
|
|
|
|
dimy_ = dimy;
|
2020-03-24 04:26:00 +08:00
|
|
|
pixels_ = std::vector<std::vector<Pixel>>(dimy, std::vector<Pixel>(dimx));
|
2019-06-30 00:52:58 +08:00
|
|
|
cursor_.x = dimx_ - 1;
|
|
|
|
cursor_.y = dimy_ - 1;
|
2019-01-05 09:03:49 +08:00
|
|
|
}
|
|
|
|
|
2020-05-21 03:23:59 +08:00
|
|
|
Render(*this, document);
|
2019-06-30 00:52:58 +08:00
|
|
|
|
|
|
|
// Set cursor position for user using tools to insert CJK characters.
|
|
|
|
set_cursor_position = "";
|
|
|
|
reset_cursor_position = "";
|
|
|
|
|
|
|
|
int dx = dimx_ - 1 - cursor_.x;
|
|
|
|
int dy = dimy_ - 1 - cursor_.y;
|
|
|
|
|
|
|
|
if (dx != 0) {
|
2020-02-12 04:44:55 +08:00
|
|
|
set_cursor_position += "\x1B[" + std::to_string(dx) + "D";
|
|
|
|
reset_cursor_position += "\x1B[" + std::to_string(dx) + "C";
|
2019-06-30 00:52:58 +08:00
|
|
|
}
|
|
|
|
if (dy != 0) {
|
2020-03-24 04:26:00 +08:00
|
|
|
set_cursor_position += "\x1B[" + std::to_string(dy) + "A";
|
2020-02-12 04:44:55 +08:00
|
|
|
reset_cursor_position += "\x1B[" + std::to_string(dy) + "B";
|
2019-06-30 00:52:58 +08:00
|
|
|
}
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::function<void()> ScreenInteractive::ExitLoopClosure() {
|
2020-03-25 08:15:46 +08:00
|
|
|
return [this]() {
|
|
|
|
quit_ = true;
|
|
|
|
event_sender_.reset();
|
|
|
|
};
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
} // namespace ftxui.
|
2020-08-16 06:24:18 +08:00
|
|
|
|
|
|
|
// 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.
|