FTXUI  0.8.1
C++ functional terminal UI.
screen.cpp
Go to the documentation of this file.
1 #include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
2 #include <memory> // for allocator
3 #include <sstream> // IWYU pragma: keep
4 
6 #include "ftxui/screen/string.hpp" // for string_width
7 #include "ftxui/screen/terminal.hpp" // for Dimensions, Size
8 
9 #if defined(_WIN32)
10 #define WIN32_LEAN_AND_MEAN
11 #ifndef NOMINMAX
12 #define NOMINMAX
13 #endif
14 #include <Windows.h>
15 #endif
16 
17 namespace ftxui {
18 
19 namespace {
20 static const char BOLD_SET[] = "\x1B[1m";
21 static const char BOLD_RESET[] = "\x1B[22m"; // Can't use 21 here.
22 
23 static const char DIM_SET[] = "\x1B[2m";
24 static const char DIM_RESET[] = "\x1B[22m";
25 
26 static const char UNDERLINED_SET[] = "\x1B[4m";
27 static const char UNDERLINED_RESET[] = "\x1B[24m";
28 
29 static const char BLINK_SET[] = "\x1B[5m";
30 static const char BLINK_RESET[] = "\x1B[25m";
31 
32 static const char INVERTED_SET[] = "\x1B[7m";
33 static const char INVERTED_RESET[] = "\x1B[27m";
34 
35 static const char MOVE_LEFT[] = "\r";
36 static const char MOVE_UP[] = "\x1B[1A";
37 static const char CLEAR_LINE[] = "\x1B[2K";
38 
39 Pixel dev_null_pixel;
40 
41 #if defined(_WIN32)
42 void WindowsEmulateVT100Terminal() {
43  static bool done = false;
44  if (done)
45  return;
46  done = true;
47 
48  // Enable VT processing on stdout and stdin
49  auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
50 
51  DWORD out_mode = 0;
52  GetConsoleMode(stdout_handle, &out_mode);
53 
54  // https://docs.microsoft.com/en-us/windows/console/setconsolemode
55  const int enable_virtual_terminal_processing = 0x0004;
56  const int disable_newline_auto_return = 0x0008;
57  out_mode |= enable_virtual_terminal_processing;
58  out_mode |= disable_newline_auto_return;
59 
60  SetConsoleMode(stdout_handle, out_mode);
61 }
62 #endif
63 
64 void UpdatePixelStyle(std::stringstream& ss,
65  Pixel& previous,
66  const Pixel& next) {
67  if (next.bold != previous.bold)
68  ss << (next.bold ? BOLD_SET : BOLD_RESET);
69 
70  if (next.dim != previous.dim)
71  ss << (next.dim ? DIM_SET : DIM_RESET);
72 
73  if (next.underlined != previous.underlined)
74  ss << (next.underlined ? UNDERLINED_SET : UNDERLINED_RESET);
75 
76  if (next.blink != previous.blink)
77  ss << (next.blink ? BLINK_SET : BLINK_RESET);
78 
79  if (next.inverted != previous.inverted)
80  ss << (next.inverted ? INVERTED_SET : INVERTED_RESET);
81 
82  if (next.foreground_color != previous.foreground_color ||
83  next.background_color != previous.background_color) {
84  ss << "\x1B[" + next.foreground_color.Print(false) + "m";
85  ss << "\x1B[" + next.background_color.Print(true) + "m";
86  }
87 
88  previous = next;
89 }
90 
91 } // namespace
92 
93 /// A fixed dimension.
94 /// @see Fit
95 /// @see Full
97  return {v, v};
98 }
99 
100 /// Use the terminal dimensions.
101 /// @see Fixed
102 /// @see Fit
104  return Terminal::Size();
105 }
106 
107 // static
108 /// Create a screen with the given dimension along the x-axis and y-axis.
110  return Screen(width.dimx, height.dimy);
111 }
112 
113 // static
114 /// Create a screen with the given dimension.
116  return Screen(dimension.dimx, dimension.dimy);
117 }
118 
119 Screen::Screen(int dimx, int dimy)
120  : stencil{0, dimx - 1, 0, dimy - 1},
121  dimx_(dimx),
122  dimy_(dimy),
123  pixels_(dimy, std::vector<Pixel>(dimx)) {
124 #if defined(_WIN32)
125  // The placement of this call is a bit weird, however we can assume that
126  // anybody who instantiates a Screen object eventually wants to output
127  // something to the console.
128  // As we require UTF8 for all input/output operations we will just switch to
129  // UTF8 encoding here
130  SetConsoleOutputCP(CP_UTF8);
131  SetConsoleCP(CP_UTF8);
132  WindowsEmulateVT100Terminal();
133 #endif
134 }
135 
136 /// Produce a std::string that can be used to print the Screen on the terminal.
137 /// Don't forget to flush stdout. Alternatively, you can use Screen::Print();
138 std::string Screen::ToString() {
139  std::stringstream ss;
140 
141  Pixel previous_pixel;
142  Pixel final_pixel;
143 
144  for (int y = 0; y < dimy_; ++y) {
145  if (y != 0) {
146  UpdatePixelStyle(ss, previous_pixel, final_pixel);
147  ss << "\r\n";
148  }
149  bool previous_fullwidth = false;
150  for (const auto& pixel : pixels_[y]) {
151  if (!previous_fullwidth) {
152  UpdatePixelStyle(ss, previous_pixel, pixel);
153  ss << pixel.character;
154  }
155  previous_fullwidth = (string_width(pixel.character) == 2);
156  }
157  }
158 
159  UpdatePixelStyle(ss, previous_pixel, final_pixel);
160 
161  return ss.str();
162 }
163 
165  std::cout << ToString() << '\0' << std::flush;
166 }
167 
168 /// @brief Access a character a given position.
169 /// @param x The character position along the x-axis.
170 /// @param y The character position along the y-axis.
171 std::string& Screen::at(int x, int y) {
172  return PixelAt(x, y).character;
173 }
174 
175 /// @brief Access a Pixel at a given position.
176 /// @param x The pixel position along the x-axis.
177 /// @param y The pixel position along the y-axis.
178 Pixel& Screen::PixelAt(int x, int y) {
179  return stencil.Contain(x, y) ? pixels_[y][x] : dev_null_pixel;
180 }
181 
182 /// @brief Return a string to be printed in order to reset the cursor position
183 /// to the beginning of the screen.
184 ///
185 /// ```cpp
186 /// std::string reset_position;
187 /// while(true) {
188 /// auto document = render();
189 /// auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
190 /// Render(screen, document);
191 /// std::cout << reset_position << screen.ToString() << std::flush;
192 /// reset_position = screen.ResetPosition();
193 ///
194 /// using namespace std::chrono_literals;
195 /// std::this_thread::sleep_for(0.01s);
196 /// }
197 /// ```
198 ///
199 /// @return The string to print in order to reset the cursor position to the
200 /// beginning.
201 std::string Screen::ResetPosition(bool clear) {
202  std::stringstream ss;
203  if (clear) {
204  ss << MOVE_LEFT << CLEAR_LINE;
205  for (int y = 1; y < dimy_; ++y) {
206  ss << MOVE_UP << CLEAR_LINE;
207  }
208  } else {
209  ss << MOVE_LEFT;
210  for (int y = 1; y < dimy_; ++y) {
211  ss << MOVE_UP;
212  }
213  }
214  return ss.str();
215 }
216 
217 /// @brief Clear all the pixel from the screen.
219  pixels_ = std::vector<std::vector<Pixel>>(dimy_,
220  std::vector<Pixel>(dimx_, Pixel()));
221  cursor_.x = dimx_ - 1;
222  cursor_.y = dimy_ - 1;
223 }
224 
225 // clang-format off
227  // Merge box characters togethers.
228  for (int y = 1; y < dimy_; ++y) {
229  for (int x = 1; x < dimx_; ++x) {
230  // Box drawing character uses exactly 3 byte.
231  std::string& cur = pixels_[y][x].character;
232  if (cur.size() != 3u)
233  continue;
234 
235  // Left vs current.
236  std::string& left = pixels_[y][x-1].character;
237  if (left.size() == 3u) {
238  if (cur == "│" && left == "─") cur = "┤";
239  if (cur == "├" && left == "─") cur = "┼";
240  if (cur == "─" && left == "│") left = "├";
241  if (cur == "─" && left == "┤") left = "┼";
242  }
243 
244  // Top vs current.
245  std::string& top = pixels_[y-1][x].character;
246  if (top.size() == 3u) {
247  if (cur == "─" && top == "│") cur = "┴";
248  if (cur == "┬" && top == "│") cur = "┼";
249  if (cur == "│" && top == "─") top = "┬";
250  if (cur == "│" && top == "┴") top = "┼";
251  }
252  }
253  }
254 }
255 
256 // clang-format on
257 
258 } // namespace ftxui
259 
260 // Copyright 2020 Arthur Sonzogni. All rights reserved.
261 // Use of this source code is governed by the MIT license that can be found in
262 // the LICENSE file.
ftxui::Screen::cursor_
Cursor cursor_
Definition: screen.hpp:88
ftxui::Screen::ResetPosition
std::string ResetPosition(bool clear=false)
Return a string to be printed in order to reset the cursor position to the beginning of the screen.
Definition: screen.cpp:201
ftxui::Screen::PixelAt
Pixel & PixelAt(int x, int y)
Access a Pixel at a given position.
Definition: screen.cpp:178
ftxui::Pixel::character
std::string character
Definition: screen.hpp:19
ftxui
Definition: captured_mouse.hpp:6
ftxui::Dimensions
Definition: terminal.hpp:5
ftxui::Screen::Cursor::y
int y
Definition: screen.hpp:79
ftxui::Screen::at
std::string & at(int x, int y)
Access a character a given position.
Definition: screen.cpp:171
ftxui::Screen::dimy_
int dimy_
Definition: screen.hpp:86
terminal.hpp
string.hpp
ftxui::Screen::Print
void Print()
Definition: screen.cpp:164
ftxui::Screen::Screen
Screen(int dimx, int dimy)
Definition: screen.cpp:119
ftxui::Screen::dimx_
int dimx_
Definition: screen.hpp:85
ftxui::Screen::dimy
int dimy()
Definition: screen.hpp:66
ftxui::Dimensions::dimx
int dimx
Definition: terminal.hpp:6
ftxui::Dimension::Fixed
Dimensions Fixed(int)
Definition: screen.cpp:96
ftxui::Terminal::Size
Dimensions Size()
Definition: terminal.cpp:21
ftxui::Screen::stencil
Box stencil
Definition: screen.hpp:75
ftxui::Screen::Create
static Screen Create(Dimensions dimension)
Create a screen with the given dimension.
Definition: screen.cpp:115
ftxui::Screen
A rectangular grid of Pixel.
Definition: screen.hpp:49
ftxui::Dimension::Full
Dimensions Full()
Definition: screen.cpp:103
ftxui::Screen::ToString
std::string ToString()
Definition: screen.cpp:138
ftxui::Screen::dimx
int dimx()
Definition: screen.hpp:65
ftxui::Box::Contain
bool Contain(int x, int y)
Definition: box.cpp:20
ftxui::Dimensions::dimy
int dimy
Definition: terminal.hpp:7
ftxui::Screen::pixels_
std::vector< std::vector< Pixel > > pixels_
Definition: screen.hpp:87
ftxui::Pixel
A unicode character and its associated style.
Definition: screen.hpp:16
ftxui::Screen::Cursor::x
int x
Definition: screen.hpp:78
ftxui::string_width
int string_width(const std::string &)
Definition: string.cpp:226
screen.hpp
ftxui::Screen::Clear
void Clear()
Clear all the pixel from the screen.
Definition: screen.cpp:218
ftxui::Screen::ApplyShader
void ApplyShader()
Definition: screen.cpp:226