19#define WIN32_LEAN_AND_MEAN
27#if !defined(FTXUI_UNLIKELY)
28#if defined(COMPILER_GCC) || defined(__clang__)
29#define FTXUI_UNLIKELY(x) __builtin_expect(!!(x), 0)
31#define FTXUI_UNLIKELY(x) (x)
35#if !defined(FTXUI_LIKELY)
36#if defined(COMPILER_GCC) || defined(__clang__)
37#define FTXUI_LIKELY(x) __builtin_expect(!!(x), 1)
39#define FTXUI_LIKELY(x) (x)
48void WindowsEmulateVT100Terminal() {
49 static bool done =
false;
56 auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
59 GetConsoleMode(stdout_handle, &out_mode);
62 const int enable_virtual_terminal_processing = 0x0004;
63 const int disable_newline_auto_return = 0x0008;
64 out_mode |= enable_virtual_terminal_processing;
65 out_mode |= disable_newline_auto_return;
67 SetConsoleMode(stdout_handle, out_mode);
72void UpdatePixelStyle(
const Screen*
screen,
73 std::stringstream& ss,
78 ss <<
"\x1B]8;;" <<
screen->Hyperlink(next.hyperlink) <<
"\x1B\\";
82 if (
FTXUI_UNLIKELY((next.bold ^ prev.bold) | (next.dim ^ prev.dim))) {
84 ss << ((prev.bold && !next.bold) || (prev.dim && !next.dim) ?
"\x1B[22m"
86 ss << (next.bold ?
"\x1B[1m" :
"");
87 ss << (next.dim ?
"\x1B[2m" :
"");
92 next.underlined_double != prev.underlined_double)) {
93 ss << (next.underlined ?
"\x1B[4m"
94 : next.underlined_double ?
"\x1B[21m"
100 ss << (next.blink ?
"\x1B[5m"
106 ss << (next.inverted ?
"\x1B[7m"
112 ss << (next.italic ?
"\x1B[3m"
118 ss << (next.strikethrough ?
"\x1B[9m"
122 if (
FTXUI_UNLIKELY(next.foreground_color != prev.foreground_color ||
123 next.background_color != prev.background_color)) {
124 ss <<
"\x1B[" + next.foreground_color.Print(
false) +
"m";
125 ss <<
"\x1B[" + next.background_color.Print(
true) +
"m";
137 bool operator<(
const TileEncoding& other)
const {
138 if (
left < other.left) {
return true; }
139 if (
left > other.left) {
return false; }
140 if (
top < other.top) {
return true; }
141 if (
top > other.top) {
return false; }
142 if (
right < other.right) {
return true; }
143 if (
right > other.right) {
return false; }
144 if (
down < other.down) {
return true; }
145 if (
down > other.down) {
return false; }
146 if (
round < other.round) {
return true; }
147 if (
round > other.round) {
return false; }
154const std::map<std::string, TileEncoding> tile_encoding = {
155 {
"─", {1, 0, 1, 0, 0}},
156 {
"━", {2, 0, 2, 0, 0}},
157 {
"╍", {2, 0, 2, 0, 0}},
159 {
"│", {0, 1, 0, 1, 0}},
160 {
"┃", {0, 2, 0, 2, 0}},
161 {
"╏", {0, 2, 0, 2, 0}},
163 {
"┌", {0, 0, 1, 1, 0}},
164 {
"┍", {0, 0, 2, 1, 0}},
165 {
"┎", {0, 0, 1, 2, 0}},
166 {
"┏", {0, 0, 2, 2, 0}},
168 {
"┐", {1, 0, 0, 1, 0}},
169 {
"┑", {2, 0, 0, 1, 0}},
170 {
"┒", {1, 0, 0, 2, 0}},
171 {
"┓", {2, 0, 0, 2, 0}},
173 {
"└", {0, 1, 1, 0, 0}},
174 {
"┕", {0, 1, 2, 0, 0}},
175 {
"┖", {0, 2, 1, 0, 0}},
176 {
"┗", {0, 2, 2, 0, 0}},
178 {
"┘", {1, 1, 0, 0, 0}},
179 {
"┙", {2, 1, 0, 0, 0}},
180 {
"┚", {1, 2, 0, 0, 0}},
181 {
"┛", {2, 2, 0, 0, 0}},
183 {
"├", {0, 1, 1, 1, 0}},
184 {
"┝", {0, 1, 2, 1, 0}},
185 {
"┞", {0, 2, 1, 1, 0}},
186 {
"┟", {0, 1, 1, 2, 0}},
187 {
"┠", {0, 2, 1, 2, 0}},
188 {
"┡", {0, 2, 2, 1, 0}},
189 {
"┢", {0, 1, 2, 2, 0}},
190 {
"┣", {0, 2, 2, 2, 0}},
192 {
"┤", {1, 1, 0, 1, 0}},
193 {
"┥", {2, 1, 0, 1, 0}},
194 {
"┦", {1, 2, 0, 1, 0}},
195 {
"┧", {1, 1, 0, 2, 0}},
196 {
"┨", {1, 2, 0, 2, 0}},
197 {
"┩", {2, 2, 0, 1, 0}},
198 {
"┪", {2, 1, 0, 2, 0}},
199 {
"┫", {2, 2, 0, 2, 0}},
201 {
"┬", {1, 0, 1, 1, 0}},
202 {
"┭", {2, 0, 1, 1, 0}},
203 {
"┮", {1, 0, 2, 1, 0}},
204 {
"┯", {2, 0, 2, 1, 0}},
205 {
"┰", {1, 0, 1, 2, 0}},
206 {
"┱", {2, 0, 1, 2, 0}},
207 {
"┲", {1, 0, 2, 2, 0}},
208 {
"┳", {2, 0, 2, 2, 0}},
210 {
"┴", {1, 1, 1, 0, 0}},
211 {
"┵", {2, 1, 1, 0, 0}},
212 {
"┶", {1, 1, 2, 0, 0}},
213 {
"┷", {2, 1, 2, 0, 0}},
214 {
"┸", {1, 2, 1, 0, 0}},
215 {
"┹", {2, 2, 1, 0, 0}},
216 {
"┺", {1, 2, 2, 0, 0}},
217 {
"┻", {2, 2, 2, 0, 0}},
219 {
"┼", {1, 1, 1, 1, 0}},
220 {
"┽", {2, 1, 1, 1, 0}},
221 {
"┾", {1, 1, 2, 1, 0}},
222 {
"┿", {2, 1, 2, 1, 0}},
223 {
"╀", {1, 2, 1, 1, 0}},
224 {
"╁", {1, 1, 1, 2, 0}},
225 {
"╂", {1, 2, 1, 2, 0}},
226 {
"╃", {2, 2, 1, 1, 0}},
227 {
"╄", {1, 2, 2, 1, 0}},
228 {
"╅", {2, 1, 1, 2, 0}},
229 {
"╆", {1, 1, 2, 2, 0}},
230 {
"╇", {2, 2, 2, 1, 0}},
231 {
"╈", {2, 1, 2, 2, 0}},
232 {
"╉", {2, 2, 1, 2, 0}},
233 {
"╊", {1, 2, 2, 2, 0}},
234 {
"╋", {2, 2, 2, 2, 0}},
236 {
"═", {3, 0, 3, 0, 0}},
237 {
"║", {0, 3, 0, 3, 0}},
239 {
"╒", {0, 0, 3, 1, 0}},
240 {
"╓", {0, 0, 1, 3, 0}},
241 {
"╔", {0, 0, 3, 3, 0}},
243 {
"╕", {3, 0, 0, 1, 0}},
244 {
"╖", {1, 0, 0, 3, 0}},
245 {
"╗", {3, 0, 0, 3, 0}},
247 {
"╘", {0, 1, 3, 0, 0}},
248 {
"╙", {0, 3, 1, 0, 0}},
249 {
"╚", {0, 3, 3, 0, 0}},
251 {
"╛", {3, 1, 0, 0, 0}},
252 {
"╜", {1, 3, 0, 0, 0}},
253 {
"╝", {3, 3, 0, 0, 0}},
255 {
"╞", {0, 1, 3, 1, 0}},
256 {
"╟", {0, 3, 1, 3, 0}},
257 {
"╠", {0, 3, 3, 3, 0}},
259 {
"╡", {3, 1, 0, 1, 0}},
260 {
"╢", {1, 3, 0, 3, 0}},
261 {
"╣", {3, 3, 0, 3, 0}},
263 {
"╤", {3, 0, 3, 1, 0}},
264 {
"╥", {1, 0, 1, 3, 0}},
265 {
"╦", {3, 0, 3, 3, 0}},
267 {
"╧", {3, 1, 3, 0, 0}},
268 {
"╨", {1, 3, 1, 0, 0}},
269 {
"╩", {3, 3, 3, 0, 0}},
271 {
"╪", {3, 1, 3, 1, 0}},
272 {
"╫", {1, 3, 1, 3, 0}},
273 {
"╬", {3, 3, 3, 3, 0}},
275 {
"╭", {0, 0, 1, 1, 1}},
276 {
"╮", {1, 0, 0, 1, 1}},
277 {
"╯", {1, 1, 0, 0, 1}},
278 {
"╰", {0, 1, 1, 0, 1}},
280 {
"╴", {1, 0, 0, 0, 0}},
281 {
"╵", {0, 1, 0, 0, 0}},
282 {
"╶", {0, 0, 1, 0, 0}},
283 {
"╷", {0, 0, 0, 1, 0}},
285 {
"╸", {2, 0, 0, 0, 0}},
286 {
"╹", {0, 2, 0, 0, 0}},
287 {
"╺", {0, 0, 2, 0, 0}},
288 {
"╻", {0, 0, 0, 2, 0}},
290 {
"╼", {1, 0, 2, 0, 0}},
291 {
"╽", {0, 1, 0, 2, 0}},
292 {
"╾", {2, 0, 1, 0, 0}},
293 {
"╿", {0, 2, 0, 1, 0}},
297template <
class A,
class B>
298std::map<B, A> InvertMap(
const std::map<A, B> input) {
299 std::map<B, A> output;
300 for (
const auto& it : input) {
301 output[it.second] = it.first;
306const std::map<TileEncoding, std::string> tile_encoding_inverse =
307 InvertMap(tile_encoding);
309void UpgradeLeftRight(std::string&
left, std::string&
right) {
310 const auto it_left = tile_encoding.find(
left);
311 if (it_left == tile_encoding.end()) {
314 const auto it_right = tile_encoding.find(
right);
315 if (it_right == tile_encoding.end()) {
319 if (it_left->second.right == 0 && it_right->second.left != 0) {
320 TileEncoding encoding_left = it_left->second;
321 encoding_left.right = it_right->second.left;
322 const auto it_left_upgrade = tile_encoding_inverse.find(encoding_left);
323 if (it_left_upgrade != tile_encoding_inverse.end()) {
324 left = it_left_upgrade->second;
328 if (it_right->second.left == 0 && it_left->second.right != 0) {
329 TileEncoding encoding_right = it_right->second;
330 encoding_right.left = it_left->second.right;
331 const auto it_right_upgrade = tile_encoding_inverse.find(encoding_right);
332 if (it_right_upgrade != tile_encoding_inverse.end()) {
333 right = it_right_upgrade->second;
338void UpgradeTopDown(std::string&
top, std::string&
down) {
339 const auto it_top = tile_encoding.find(
top);
340 if (it_top == tile_encoding.end()) {
343 const auto it_down = tile_encoding.find(
down);
344 if (it_down == tile_encoding.end()) {
348 if (it_top->second.down == 0 && it_down->second.top != 0) {
349 TileEncoding encoding_top = it_top->second;
350 encoding_top.down = it_down->second.top;
351 const auto it_top_down = tile_encoding_inverse.find(encoding_top);
352 if (it_top_down != tile_encoding_inverse.end()) {
353 top = it_top_down->second;
357 if (it_down->second.top == 0 && it_top->second.down != 0) {
358 TileEncoding encoding_down = it_down->second;
359 encoding_down.top = it_top->second.down;
360 const auto it_down_top = tile_encoding_inverse.find(encoding_down);
361 if (it_down_top != tile_encoding_inverse.end()) {
362 down = it_down_top->second;
367bool ShouldAttemptAutoMerge(Pixel& pixel) {
368 return pixel.automerge && pixel.character.size() == 3;
396 return {dimension.
dimx, dimension.
dimy};
406 SetConsoleOutputCP(CP_UTF8);
407 SetConsoleCP(CP_UTF8);
408 WindowsEmulateVT100Terminal();
417 std::stringstream ss;
419 const Pixel default_pixel;
420 const Pixel* previous_pixel_ref = &default_pixel;
422 for (
int y = 0; y <
dimy_; ++y) {
425 UpdatePixelStyle(
this, ss, *previous_pixel_ref, default_pixel);
426 previous_pixel_ref = &default_pixel;
431 bool previous_fullwidth =
false;
432 for (
const auto& pixel :
pixels_[y]) {
433 if (!previous_fullwidth) {
434 UpdatePixelStyle(
this, ss, *previous_pixel_ref, pixel);
435 previous_pixel_ref = &pixel;
436 if (pixel.character.empty()) {
439 ss << pixel.character;
442 previous_fullwidth = (
string_width(pixel.character) == 2);
447 UpdatePixelStyle(
this, ss, *previous_pixel_ref, default_pixel);
454 std::cout <<
ToString() <<
'\0' << std::flush;
477 std::stringstream ss;
481 for (
int y = 1; y <
dimy_; ++y) {
487 for (
int y = 1; y <
dimy_; ++y) {
509 for (
int y = 0; y <
dimy_; ++y) {
510 for (
int x = 0; x <
dimx_; ++x) {
513 if (!ShouldAttemptAutoMerge(cur)) {
519 if (ShouldAttemptAutoMerge(
left)) {
525 if (ShouldAttemptAutoMerge(
top)) {
535 for (std::size_t i = 0; i <
hyperlinks_.size(); ++i) {
540 if (
hyperlinks_.size() == std::numeric_limits<std::uint8_t>::max()) {
std::function< void(Pixel &)> SelectionStyle
const SelectionStyle & GetSelectionStyle() const
Devuelve el estilo de selección actual.
const std::string & Hyperlink(uint8_t id) const
std::string ToString() const
static Screen Create(Dimensions dimension)
Crea una pantalla con la dimensión dada.
uint8_t RegisterHyperlink(const std::string &link)
Screen(int dimx, int dimy)
std::string ResetPosition(bool clear=false) const
Devuelve una cadena que se puede imprimir para restablecer la posición del cursor al principio de la ...
void Clear()
Borra todos los píxeles de la pantalla.
SelectionStyle selection_style_
void SetSelectionStyle(SelectionStyle decorator)
Establece el estilo de selección actual.
std::vector< std::string > hyperlinks_
std::vector< std::vector< Pixel > > pixels_
Una cuadrícula rectangular de píxeles.
Una cuadrícula rectangular de píxeles.
Dimensions Size()
Obtiene el tamaño del terminal.
Dimensions es una estructura que representa el tamaño de la terminal.
Un carácter Unicode y su estilo asociado.
El espacio de nombres ftxui:: de FTXUI.
int string_width(const std::string &)
#define FTXUI_UNLIKELY(x)