15#include <initializer_list>
40#define DEFINE_CONSOLEV2_PROPERTIES
41#define WIN32_LEAN_AND_MEAN
47#error Must be compiled in UNICODE mode
50#include <sys/select.h>
56#if defined(__clang__) && defined(__APPLE__)
57#define quick_exit(a) exit(a)
66 screen->RequestAnimationFrame();
77 std::cout <<
'\0' << std::flush;
87 auto parser = TerminalInputParser(
out->Clone());
109 for (
const auto& r :
records) {
110 switch (r.EventType) {
135#elif defined(__EMSCRIPTEN__)
136#include <emscripten.h>
140 auto parser = TerminalInputParser(std::move(
out));
176 auto parser = TerminalInputParser(std::move(
out));
185 std::array<char, buffer_size>
buffer;
187 for (
size_t i = 0; i < l; ++i) {
261const std::string
CSI =
"\x1b[";
264const std::string
DCS =
"\x1bP";
266const std::string
ST =
"\x1b\\";
273enum class DECMode : std::uint16_t {
279 kMouseVt200Highlight = 1001,
281 kMouseBtnEventMouse = 1002,
282 kMouseAnyEvent = 1003,
285 kMouseSgrExtMode = 1006,
286 kMouseUrxvtMode = 1015,
287 kMouseSgrPixelsMode = 1016,
288 kAlternateScreen = 1049,
292enum class DSRMode : std::uint8_t {
310std::string Set(
const std::vector<DECMode>&
parameters) {
321 return CSI + std::to_string(
int(
ps)) +
"n";
324class CapturedMouseImpl :
public CapturedMouseInterface {
326 explicit CapturedMouseImpl(std::function<
void(
void)>
callback)
329 CapturedMouseImpl(
const CapturedMouseImpl&) =
delete;
330 CapturedMouseImpl(CapturedMouseImpl&&) =
delete;
331 CapturedMouseImpl& operator=(
const CapturedMouseImpl&) =
delete;
332 CapturedMouseImpl& operator=(CapturedMouseImpl&&) =
delete;
335 std::function<
void(
void)> callback_;
340 const auto time_delta = std::chrono::milliseconds(15);
342 out->Send(AnimationTask());
349ScreenInteractive::ScreenInteractive(
int dimx,
353 : Screen(dimx, dimy),
387 Dimension::Fullscreen,
400 Dimension::Fullscreen,
410 Dimension::TerminalOutput,
420 Dimension::FitComponent,
455 task_sender_->Send(std::move(
task));
468 if (animation_requested_) {
471 animation_requested_ =
true;
472 auto now = animation::Clock::now();
475 previous_animation_time_ =
now;
484 if (mouse_captured) {
487 mouse_captured =
true;
488 return std::make_unique<CapturedMouseImpl>(
489 [
this] { mouse_captured =
false; });
502bool ScreenInteractive::HasQuitted() {
507void ScreenInteractive::PreMain() {
512 suspended_screen_->ResetCursorPosition();
514 suspended_screen_->
dimx_ = 0;
515 suspended_screen_->
dimy_ = 0;
518 suspended_screen_->Uninstall();
525 previous_animation_time_ = animation::Clock::now();
529void ScreenInteractive::PostMain() {
531 ResetCursorPosition();
536 if (suspended_screen_) {
550 if (!use_alternative_screen_) {
552 std::cout << std::flush;
571 force_handle_ctrl_c_ =
force;
577 force_handle_ctrl_z_ =
force;
585 return selection_->GetParts();
589 selection_on_change_ = std::move(
callback);
599void ScreenInteractive::Install() {
600 frame_valid_ =
false;
619 std::cout <<
"\033[?25h";
620 std::cout <<
"\033[" + std::to_string(cursor_reset_shape_) +
" q";
710 if (use_alternative_screen_) {
712 DECMode::kAlternateScreen,
722 enable({DECMode::kMouseVt200});
723 enable({DECMode::kMouseAnyEvent});
724 enable({DECMode::kMouseUrxvtMode});
725 enable({DECMode::kMouseSgrExtMode});
733 task_sender_ = task_receiver_->MakeSender();
735 std::thread(&
EventListener, &quit_, task_receiver_->MakeSender());
736 animation_listener_ =
741void ScreenInteractive::Uninstall() {
743 event_listener_.join();
744 animation_listener_.join();
753 if (task_receiver_->Receive(&
task)) {
762 while (task_receiver_->ReceiveNonBlocking(&
task)) {
768 if (selection_data_previous_ != selection_data_) {
769 selection_data_previous_ = selection_data_;
770 if (selection_on_change_) {
771 selection_on_change_();
782 using T = std::decay_t<
decltype(
arg)>;
786 if constexpr (std::is_same_v<T, Event>) {
787 if (
arg.is_cursor_position()) {
788 cursor_x_ =
arg.cursor_x();
789 cursor_y_ =
arg.cursor_y();
793 if (
arg.is_cursor_shape()) {
794 cursor_reset_shape_=
arg.cursor_shape();
798 if (
arg.is_mouse()) {
799 arg.mouse().x -= cursor_x_;
800 arg.mouse().y -= cursor_y_;
819 frame_valid_ =
false;
824 if constexpr (std::is_same_v<T, Closure>) {
830 if constexpr (std::is_same_v<T, AnimationTask>) {
831 if (!animation_requested_) {
835 animation_requested_ =
false;
838 previous_animation_time_ =
now;
842 frame_valid_ =
false;
851bool ScreenInteractive::HandleSelection(
bool handled, Event
event) {
853 selection_pending_ =
nullptr;
854 selection_data_.empty =
true;
855 selection_ =
nullptr;
859 if (!
event.is_mouse()) {
863 auto& mouse =
event.mouse();
870 selection_data_.start_x = mouse.x;
871 selection_data_.start_y = mouse.y;
872 selection_data_.end_x = mouse.x;
873 selection_data_.end_y = mouse.y;
877 if (!selection_pending_) {
882 if ((mouse.x != selection_data_.end_x) ||
883 (mouse.y != selection_data_.end_y)) {
884 selection_data_.end_x = mouse.x;
885 selection_data_.end_y = mouse.y;
886 selection_data_.empty =
false;
893 selection_pending_ =
nullptr;
894 selection_data_.end_x = mouse.x;
895 selection_data_.end_y = mouse.y;
896 selection_data_.empty =
false;
914 switch (dimension_) {
915 case Dimension::Fixed:
919 case Dimension::TerminalOutput:
923 case Dimension::Fullscreen:
927 case Dimension::FitComponent:
934 ResetCursorPosition();
939 if ((
dimx <
dimx_) && !use_alternative_screen_) {
940 std::cout <<
"\033[J";
941 std::cout <<
"\033[H";
948 pixels_ = std::vector<std::vector<Pixel>>(
dimy, std::vector<Pixel>(
dimx));
956#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
965 if (!use_alternative_screen_ && (i % 150 == 0)) {
971 if (!use_alternative_screen_ &&
972 (previous_frame_resized_ || i % 40 == 0)) {
976 previous_frame_resized_ =
resized;
978 selection_ = selection_data_.empty
979 ? std::make_unique<Selection>()
981 selection_data_.start_x, selection_data_.start_y,
982 selection_data_.end_x, selection_data_.end_y);
990 set_cursor_position.clear();
991 reset_cursor_position.clear();
994 set_cursor_position +=
"\x1B[" + std::to_string(
dy) +
"A";
995 reset_cursor_position +=
"\x1B[" + std::to_string(
dy) +
"B";
999 set_cursor_position +=
"\x1B[" + std::to_string(
dx) +
"D";
1000 reset_cursor_position +=
"\x1B[" + std::to_string(
dx) +
"C";
1004 set_cursor_position +=
"\033[?25l";
1006 set_cursor_position +=
"\033[?25h";
1007 set_cursor_position +=
1012 std::cout <<
ToString() << set_cursor_position;
1015 frame_valid_ =
true;
1019void ScreenInteractive::ResetCursorPosition() {
1020 std::cout << reset_cursor_position;
1021 reset_cursor_position =
"";
1027 return [
this] {
Exit(); };
1033 Post([
this] { ExitNow(); });
1037void ScreenInteractive::ExitNow() {
1039 task_sender_.reset();
1043void ScreenInteractive::Signal(
int signal) {
1053 ResetCursorPosition();
1059 std::ignore = std::raise(
SIGTSTP);
1072bool ScreenInteractive::SelectionData::operator==(
1073 const ScreenInteractive::SelectionData&
other)
const {
1074 if (empty &&
other.empty) {
1077 if (empty ||
other.empty) {
1080 return start_x ==
other.start_x && start_y ==
other.start_y &&
1081 end_x ==
other.end_x && end_y ==
other.end_y;
1084bool ScreenInteractive::SelectionData::operator!=(
1085 const ScreenInteractive::SelectionData&
other)
const {
1086 return !(*
this ==
other);
std::vector< std::vector< Pixel > > pixels_
bool HasQuitted()
Whether the loop has quitted.
static void Signal(ScreenInteractive &s, int signal)
static ScreenInteractive TerminalOutput()
void Exit()
Exit the main loop.
static ScreenInteractive FixedSize(int dimx, int dimy)
void PostEvent(Event event)
Add an event to the main loop. It will be executed later, after every other scheduled events.
void Post(Task task)
Add a task to the main loop. It will be executed later, after every other scheduled tasks.
static ScreenInteractive FitComponent()
static ScreenInteractive Fullscreen()
static ScreenInteractive FullscreenPrimaryScreen()
static ScreenInteractive * Active()
Return the currently active screen, or null if none.
CapturedMouse CaptureMouse()
Try to get the unique lock about behing able to capture the mouse.
std::string GetSelection()
Returns the content of the current selection.
static ScreenInteractive FullscreenAlternateScreen()
void TrackMouse(bool enable=true)
Set whether mouse is tracked and events reported. called outside of the main loop....
void SelectionChange(std::function< void()> callback)
void RequestAnimationFrame()
Add a task to draw the screen one more time, until all the animations are done.
Closure ExitLoopClosure()
Return a function to exit the main loop.
void ForceHandleCtrlC(bool force)
Force FTXUI to handle or not handle Ctrl-C, even if the component catches the Event::CtrlC.
void ForceHandleCtrlZ(bool force)
Force FTXUI to handle or not handle Ctrl-Z, even if the component catches the Event::CtrlZ.
Closure WithRestoredIO(Closure)
Decorate a function. It executes the same way, but with the currently active screen terminal hooks te...
std::string ToString() const
std::string ResetPosition(bool clear=false) const
Return a string to be printed in order to reset the cursor position to the beginning of the screen.
void Clear()
Clear all the pixel from the screen.
void SetFallbackSize(const Dimensions &fallbackSize)
Override terminal size in case auto-detection fails.
Dimensions Size()
Get the terminal size.
std::chrono::duration< float > Duration
std::chrono::time_point< Clock > TimePoint
void RequestAnimationFrame()
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< T > Make(Args &&... args)
std::shared_ptr< ComponentBase > Component
std::string to_string(const std::wstring &s)
Convert a UTF8 std::string into a std::wstring.
Element select(Element e)
Set the child to be the one focused among its siblings.
std::variant< Event, Closure, AnimationTask > Task
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
std::function< void()> Closure
Represent an event. It can be key press event, a terminal resize, or more ...
static const Event Custom
static Event Special(std::string)
An custom event whose meaning is defined by the user of the library.