FTXUI/src/ftxui/component/screen_interactive.cpp

657 lines
19 KiB
C++
Raw Normal View History

2021-05-02 02:40:35 +08:00
#include <stdio.h> // for fileno, stdin
#include <algorithm> // for copy, max, min
2022-03-14 01:51:46 +08:00
#include <chrono> // for operator-, duration, operator>=, milliseconds, time_point, common_type<>::type
#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
2021-05-10 02:32:27 +08:00
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <stack> // for stack
2022-03-14 01:51:46 +08:00
#include <thread> // for thread, sleep_for
#include <type_traits> // for decay_t
#include <utility> // for swap, move
#include <variant> // for visit
#include <vector> // for vector
2021-05-10 02:32:27 +08:00
2022-03-14 01:51:46 +08:00
#include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
2021-05-10 02:32:27 +08:00
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event
2022-03-14 01:51:46 +08:00
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
2021-05-10 02:32:27 +08:00
#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
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
#if defined(_WIN32)
#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
#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
#endif
// Quick exit is missing in standard CLang headers
#if defined(__clang__) && defined(__APPLE__)
#define quick_exit(a) exit(a)
#endif
namespace ftxui {
2022-03-14 01:51:46 +08:00
namespace animation {
void RequestAnimationFrame() {
auto* screen = ScreenInteractive::Active();
if (screen)
screen->RequestAnimationFrame();
}
} // namespace animation
2020-08-09 20:53:56 +08:00
namespace {
2020-03-25 08:15:46 +08:00
ScreenInteractive* g_active_screen = nullptr;
2021-03-22 05:54:39 +08:00
void Flush() {
// Emscripten doesn't implement flush. We interpret zero as flush.
std::cout << '\0' << std::flush;
2021-03-22 05:54:39 +08:00
}
constexpr int timeout_milliseconds = 20;
constexpr int timeout_microseconds = timeout_milliseconds * 1000;
#if defined(_WIN32)
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
auto console = GetStdHandle(STD_INPUT_HANDLE);
auto parser = TerminalInputParser(out->Clone());
while (!*quit) {
// Throttle ReadConsoleInput by waiting 250ms, this wait function will
// return if there is input in the console.
auto wait_result = WaitForSingleObject(console, timeout_milliseconds);
if (wait_result == WAIT_TIMEOUT) {
parser.Timeout(timeout_milliseconds);
continue;
}
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(),
&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;
parser.Add((char)key_event.uChar.UnicodeChar);
} break;
case WINDOW_BUFFER_SIZE_EVENT:
out->Send(Event::Special({0}));
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<Task> out) {
2021-03-22 05:54:39 +08:00
(void)timeout_microseconds;
auto parser = TerminalInputParser(std::move(out));
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.
void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
2020-05-02 08:02:04 +08:00
const int buffer_size = 100;
auto parser = TerminalInputParser(std::move(out));
while (!*quit) {
if (!CheckStdinReady(timeout_microseconds)) {
parser.Timeout(timeout_milliseconds);
continue;
}
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)
parser.Add(buff[i]);
}
2020-03-25 08:15:46 +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,
};
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;
}
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-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 21:22:38 +08:00
// Device Status Report (DSR)
const std::string DeviceStatusReport(DSRMode ps) {
return CSI + std::to_string(int(ps)) + "n";
}
2020-03-23 16:23:57 +08:00
using SignalHandler = void(int);
std::stack<Closure> on_exit_functions;
void OnExit(int signal) {
(void)signal;
while (!on_exit_functions.empty()) {
on_exit_functions.top()();
on_exit_functions.pop();
}
}
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-23 16:23:57 +08:00
};
Closure on_resize = [] {};
2019-07-01 05:59:27 +08:00
void OnResize(int /* signal */) {
on_resize();
}
void OnSigStop(int /*signal*/) {
ScreenInteractive::Private::SigStop(*g_active_screen);
}
class CapturedMouseImpl : public CapturedMouseInterface {
public:
2021-05-02 02:40:35 +08:00
CapturedMouseImpl(std::function<void(void)> callback) : callback_(callback) {}
~CapturedMouseImpl() override { callback_(); }
private:
std::function<void(void)> callback_;
};
2022-03-14 01:51:46 +08:00
void AnimationListener(std::atomic<bool>* quit, Sender<Task> out) {
while (!*quit) {
out->Send(AnimationTask());
std::this_thread::sleep_for(std::chrono::milliseconds(15));
}
}
2020-08-09 20:53:56 +08:00
} // namespace
ScreenInteractive::ScreenInteractive(int dimx,
int dimy,
Dimension dimension,
bool use_alternative_screen)
: Screen(dimx, dimy),
dimension_(dimension),
use_alternative_screen_(use_alternative_screen) {
task_receiver_ = MakeReceiver<Task>();
}
// static
2019-01-27 04:52:55 +08:00
ScreenInteractive ScreenInteractive::FixedSize(int dimx, int dimy) {
return ScreenInteractive(dimx, dimy, Dimension::Fixed, false);
}
// static
ScreenInteractive ScreenInteractive::Fullscreen() {
return ScreenInteractive(0, 0, Dimension::Fullscreen, true);
}
// static
ScreenInteractive ScreenInteractive::TerminalOutput() {
return ScreenInteractive(0, 0, Dimension::TerminalOutput, false);
}
2019-01-19 07:20:29 +08:00
// static
ScreenInteractive ScreenInteractive::FitComponent() {
return ScreenInteractive(0, 0, Dimension::FitComponent, false);
2019-01-19 07:20:29 +08:00
}
void ScreenInteractive::Post(Task task) {
2020-03-25 08:15:46 +08:00
if (!quit_)
task_sender_->Send(task);
}
void ScreenInteractive::PostEvent(Event event) {
Post(event);
2019-01-27 09:33:06 +08:00
}
2022-03-14 01:51:46 +08:00
void ScreenInteractive::RequestAnimationFrame() {
if (animation_requested_)
return;
animation_requested_ = true;
auto now = animation::Clock::now();
if (now - previous_animation_time >= std::chrono::milliseconds(33))
previous_animation_time = now;
}
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) {
2021-09-01 23:47:48 +08:00
// Suspend previously active screen:
if (g_active_screen) {
std::swap(suspended_screen_, g_active_screen);
std::cout << suspended_screen_->reset_cursor_position
<< suspended_screen_->ResetPosition(/*clear=*/true);
suspended_screen_->dimx_ = 0;
suspended_screen_->dimy_ = 0;
suspended_screen_->Uninstall();
}
// This screen is now active:
g_active_screen = this;
g_active_screen->Install();
g_active_screen->Main(component);
g_active_screen->Uninstall();
g_active_screen = nullptr;
// Put cursor position at the end of the drawing.
std::cout << reset_cursor_position;
// Restore suspended screen.
if (suspended_screen_) {
std::cout << ResetPosition(/*clear=*/true);
dimx_ = 0;
dimy_ = 0;
std::swap(g_active_screen, suspended_screen_);
g_active_screen->Install();
} else {
// On final exit, keep the current drawing and reset cursor position one
// line after it.
std::cout << std::endl;
}
}
/// @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.
Closure ScreenInteractive::WithRestoredIO(Closure fn) {
return [this, fn] {
Uninstall();
fn();
Install();
};
}
2022-03-14 01:51:46 +08:00
// static
ScreenInteractive* ScreenInteractive::Active() {
return g_active_screen;
}
2021-09-01 23:47:48 +08:00
void ScreenInteractive::Install() {
// After uninstalling the new configuration, flush it to the terminal to
// ensure it is fully applied:
on_exit_functions.push([] { Flush(); });
on_exit_functions.push([this] { ExitLoopClosure()(); });
// Install signal handlers to restore the terminal state on exit. The default
// signal handlers are restored on exit.
for (int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE})
install_signal_handler(signal, OnExit);
// Save the old terminal configuration and restore it on exit.
#if defined(_WIN32)
// 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;
SetConsoleMode(stdin_handle, in_mode);
SetConsoleMode(stdout_handle, out_mode);
#else
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
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
// Handle resize.
on_resize = [&] { task_sender_->Send(Event::Special({0})); };
install_signal_handler(SIGWINCH, OnResize);
// Handle SIGTSTP/SIGCONT.
install_signal_handler(SIGTSTP, OnSigStop);
#endif
2021-04-25 21:22:38 +08:00
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); });
};
if (use_alternative_screen_) {
2021-04-25 21:22:38 +08:00
enable({
DECMode::kAlternateScreen,
});
}
2021-04-25 21:22:38 +08:00
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,
});
// After installing the new configuration, flush it to the terminal to ensure
// it is fully applied:
Flush();
2021-09-01 23:47:48 +08:00
quit_ = false;
task_sender_ = task_receiver_->MakeSender();
2021-09-01 23:47:48 +08:00
event_listener_ =
std::thread(&EventListener, &quit_, task_receiver_->MakeSender());
2022-03-14 01:51:46 +08:00
animation_listener_ =
std::thread(&AnimationListener, &quit_, task_receiver_->MakeSender());
2021-09-01 23:47:48 +08:00
}
void ScreenInteractive::Uninstall() {
ExitLoopClosure()();
event_listener_.join();
2022-03-14 01:51:46 +08:00
animation_listener_.join();
2019-01-27 09:33:06 +08:00
2021-09-01 23:47:48 +08:00
OnExit(0);
}
void ScreenInteractive::Main(Component component) {
2022-03-14 01:51:46 +08:00
previous_animation_time = animation::Clock::now();
2022-03-14 01:51:46 +08:00
auto draw = [&] {
Draw(component);
std::cout << ToString() << set_cursor_position;
Flush();
Clear();
};
2022-03-14 01:51:46 +08:00
draw();
2022-03-14 01:51:46 +08:00
while (!quit_) {
if (!task_receiver_->HasPending())
draw();
bool continue_event_loop = true;
while (continue_event_loop) {
continue_event_loop = false;
Task task;
if (!task_receiver_->Receive(&task))
break;
std::visit(
[&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
// 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 (arg.is_mouse()) {
arg.mouse().x -= cursor_x_;
arg.mouse().y -= cursor_y_;
}
arg.screen_ = this;
component->OnEvent(arg);
}
2022-03-14 01:51:46 +08:00
// Handle callback
if constexpr (std::is_same_v<T, Closure>) {
arg();
return;
}
2022-03-14 01:51:46 +08:00
// Handle Animation
if constexpr (std::is_same_v<T, AnimationTask>) {
if (!animation_requested_) {
continue_event_loop = true;
return;
}
animation_requested_ = false;
animation::TimePoint now = animation::Clock::now();
animation::Duration delta = now - previous_animation_time;
previous_animation_time = now;
animation::Params params(delta);
component->OnAnimation(params);
}
},
task);
}
}
}
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();
int dimx = 0;
int dimy = 0;
switch (dimension_) {
case Dimension::Fixed:
2019-01-13 01:24:46 +08:00
dimx = dimx_;
dimy = dimy_;
break;
case Dimension::TerminalOutput:
document->ComputeRequirement();
dimx = Terminal::Size().dimx;
2020-06-01 22:13:29 +08:00
dimy = document->requirement().min_y;
break;
case Dimension::Fullscreen:
dimx = Terminal::Size().dimx;
dimy = Terminal::Size().dimy;
break;
2019-01-19 07:20:29 +08:00
case Dimension::FitComponent:
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;
}
2021-05-17 06:44:37 +08:00
bool resized = (dimx != dimx_) || (dimy != dimy_);
std::cout << reset_cursor_position << ResetPosition(/*clear=*/resized);
// Resize the screen if needed.
2021-05-17 06:44:37 +08:00
if (resized) {
dimx_ = dimx;
dimy_ = dimy;
pixels_ = std::vector<std::vector<Pixel>>(dimy, std::vector<Pixel>(dimx));
cursor_.x = dimx_ - 1;
cursor_.y = dimy_ - 1;
}
Implement Fallback for microsoft's terminals. (#138) I finally got access to a computer using the Microsoft's Windows OS. That's the opportunity to find and mitigate all the problems encountered. This patch: 1. Introduce an option and a C++ definition to enable fallback for Microsoft's terminal emulators. This allows me to see/test the Microsoft output from Linux. This also allows Windows users to remove the fallback and target non Microsoft terminals on Windows if needed. 2. Microsoft's terminal suffer from a race condition bug when reporting the cursor position: https://github.com/microsoft/terminal/pull/7583. The mitigation is not to ask for the cursor position in fullscreen mode where it isn't really needed and request it less often. This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/136 3. Microsoft's terminal do not handle properly hidding the cursor. Instead the character under the cursor is hidden, which is a big problem. As a result, we don't enable setting the cursor to the best position for [input method editors](https://en.wikipedia.org/wiki/Input_method), It will be displayed at the bottom right corner. See: - https://github.com/microsoft/terminal/issues/1203 - https://github.com/microsoft/terminal/issues/3093 4. Microsoft's terminals do not provide a way to query if they support colors. As a fallback, assume true colors is supported. See issue: - https://github.com/microsoft/terminal/issues/1040 This mitigates: - https://github.com/ArthurSonzogni/FTXUI/issues/135 5. The "cmd" on Windows do not properly report its dimension. Powershell works correctly. As a fallback, use a 80x80 size instead of 0x0. 6. There are several dom elements and component displayed incorrectly, because the font used is missing several unicode glyph. Use alternatives or less detailled one as a fallback.
2021-07-04 23:38:31 +08:00
// Periodically request the terminal emulator the frame position relative to
// the screen. This is useful for converting mouse position reported in
// screen's coordinates to frame's coordinates.
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft's terminal suffers from a [bug]. When reporting the cursor
// position, several output sequences are mixed together into garbage.
// This causes FTXUI user to see some "1;1;R" sequences into the Input
// component. See [issue]. Solution is to request cursor position less
// often. [bug]: https://github.com/microsoft/terminal/pull/7583 [issue]:
// https://github.com/ArthurSonzogni/FTXUI/issues/136
static int i = -3;
++i;
if (!use_alternative_screen_ && (i % 150 == 0))
std::cout << DeviceStatusReport(DSRMode::kCursor);
Implement Fallback for microsoft's terminals. (#138) I finally got access to a computer using the Microsoft's Windows OS. That's the opportunity to find and mitigate all the problems encountered. This patch: 1. Introduce an option and a C++ definition to enable fallback for Microsoft's terminal emulators. This allows me to see/test the Microsoft output from Linux. This also allows Windows users to remove the fallback and target non Microsoft terminals on Windows if needed. 2. Microsoft's terminal suffer from a race condition bug when reporting the cursor position: https://github.com/microsoft/terminal/pull/7583. The mitigation is not to ask for the cursor position in fullscreen mode where it isn't really needed and request it less often. This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/136 3. Microsoft's terminal do not handle properly hidding the cursor. Instead the character under the cursor is hidden, which is a big problem. As a result, we don't enable setting the cursor to the best position for [input method editors](https://en.wikipedia.org/wiki/Input_method), It will be displayed at the bottom right corner. See: - https://github.com/microsoft/terminal/issues/1203 - https://github.com/microsoft/terminal/issues/3093 4. Microsoft's terminals do not provide a way to query if they support colors. As a fallback, assume true colors is supported. See issue: - https://github.com/microsoft/terminal/issues/1040 This mitigates: - https://github.com/ArthurSonzogni/FTXUI/issues/135 5. The "cmd" on Windows do not properly report its dimension. Powershell works correctly. As a fallback, use a 80x80 size instead of 0x0. 6. There are several dom elements and component displayed incorrectly, because the font used is missing several unicode glyph. Use alternatives or less detailled one as a fallback.
2021-07-04 23:38:31 +08:00
#else
static int i = -3;
2021-05-17 06:44:37 +08:00
++i;
if (!use_alternative_screen_ && (previous_frame_resized_ || i % 40 == 0))
Implement Fallback for microsoft's terminals. (#138) I finally got access to a computer using the Microsoft's Windows OS. That's the opportunity to find and mitigate all the problems encountered. This patch: 1. Introduce an option and a C++ definition to enable fallback for Microsoft's terminal emulators. This allows me to see/test the Microsoft output from Linux. This also allows Windows users to remove the fallback and target non Microsoft terminals on Windows if needed. 2. Microsoft's terminal suffer from a race condition bug when reporting the cursor position: https://github.com/microsoft/terminal/pull/7583. The mitigation is not to ask for the cursor position in fullscreen mode where it isn't really needed and request it less often. This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/136 3. Microsoft's terminal do not handle properly hidding the cursor. Instead the character under the cursor is hidden, which is a big problem. As a result, we don't enable setting the cursor to the best position for [input method editors](https://en.wikipedia.org/wiki/Input_method), It will be displayed at the bottom right corner. See: - https://github.com/microsoft/terminal/issues/1203 - https://github.com/microsoft/terminal/issues/3093 4. Microsoft's terminals do not provide a way to query if they support colors. As a fallback, assume true colors is supported. See issue: - https://github.com/microsoft/terminal/issues/1040 This mitigates: - https://github.com/ArthurSonzogni/FTXUI/issues/135 5. The "cmd" on Windows do not properly report its dimension. Powershell works correctly. As a fallback, use a 80x80 size instead of 0x0. 6. There are several dom elements and component displayed incorrectly, because the font used is missing several unicode glyph. Use alternatives or less detailled one as a fallback.
2021-07-04 23:38:31 +08:00
std::cout << DeviceStatusReport(DSRMode::kCursor);
#endif
previous_frame_resized_ = resized;
2021-05-17 06:44:37 +08:00
Render(*this, document);
// 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";
}
if (dy != 0) {
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";
}
}
Closure ScreenInteractive::ExitLoopClosure() {
return [this] {
2020-03-25 08:15:46 +08:00
quit_ = true;
task_sender_.reset();
2020-03-25 08:15:46 +08:00
};
}
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.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.