FTXUI 7.0.3
C++ functional terminal UI.
Loading...
Searching...
No Matches
terminal.cpp
Go to the documentation of this file.
1// Copyright 2020 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4#include <algorithm> // for std::search
5#include <cctype> // for std::tolower
6#include <initializer_list>
7#include <string>
8#include <string_view> // for string_view
9
11#include "ftxui/screen/util.hpp" // for util::GetEnv
12
13#if defined(_WIN32)
14#define WIN32_LEAN_AND_MEAN
15
16#ifndef NOMINMAX
17#define NOMINMAX
18#endif
19
20#include <windows.h>
21#else
22#include <sys/ioctl.h> // for winsize, ioctl, TIOCGWINSZ
23#include <unistd.h> // for STDOUT_FILENO
24#endif
25#if defined(__sun) || defined(__illumos__)
26#include <sys/termios.h> // for winsize on illumos
27#endif
28
29namespace ftxui {
30
31namespace {
32
33std::unique_ptr<Terminal::Quirks> g_quirks;
34bool g_color_support_detected = false;
35
36bool& ColorSupportDetected() {
37 return g_color_support_detected;
38}
39
40Terminal::Quirks& GetQuirksInternal() {
41 if (!g_quirks) {
42 g_quirks = std::make_unique<Terminal::Quirks>();
43#if defined(_WIN32)
44 g_quirks->SetBlockCharacters(false);
45 g_quirks->SetCursorHiding(false);
46 g_quirks->SetComponentAscii(true);
47#endif
48 }
49 return *g_quirks;
50}
51
52Dimensions& FallbackSize() {
53#if defined(__EMSCRIPTEN__)
54 // Esta dimensión fue elegida arbitrariamente para poder mostrar:
55 // https://arthursonzogni.com/FTXUI/examples
56 // Esto deberá mejorarse cuando alguien tenga tiempo de implementarlo y lo necesite.
57 constexpr int fallback_width = 140;
58 constexpr int fallback_height = 43;
59#else
60 // El tamaño del terminal en VT100 era 80x24. Todavía se utiliza hoy en día por
61 // defecto en muchos emuladores de terminal. Esa es una buena opción como valor de
62 // reserva.
63 constexpr int fallback_width = 80;
64 constexpr int fallback_height = 24;
65#endif
66 static Dimensions g_fallback_size{
67 fallback_width,
68 fallback_height,
69 };
70 return g_fallback_size;
71}
72
73bool Contains(std::string_view s, std::string_view key) {
74 if (key.empty()) {
75 return true;
76 }
77 const auto it = std::search( // NOLINT
78 s.begin(), s.end(), key.begin(), key.end(), [](char a, char b) {
79 return std::tolower(static_cast<unsigned char>(a)) ==
80 std::tolower(static_cast<unsigned char>(b));
81 });
82 return it != s.end();
83}
84
85bool ContainsAny(std::string_view s,
86 std::initializer_list<std::string_view> keys) {
87 for (const std::string_view key : keys) {
88 if (Contains(s, key)) {
89 return true;
90 }
91 }
92 return false;
93}
94
95Terminal::Color ComputeColorSupportInternal() {
96 static const std::vector<int> empty_capabilities;
98 util::GetEnv("TERM"), util::GetEnv("COLORTERM"),
99 util::GetEnv("TERM_PROGRAM"), "unknown", "unknown", empty_capabilities);
100}
101
102} // namespace
103
104namespace Terminal {
105
106struct Quirks::Impl {
107 bool block_characters = true;
108 bool cursor_hiding = true;
109 bool component_ascii = false;
110 Color color_support = Palette256;
111};
112
113Quirks::Quirks() : impl_(std::make_unique<Impl>()) {}
114Quirks::~Quirks() = default;
115Quirks::Quirks(const Quirks& other)
116 : impl_(std::make_unique<Impl>(*other.impl_)) {}
117Quirks& Quirks::operator=(const Quirks& other) {
118 if (this != &other) {
119 *impl_ = *other.impl_;
120 }
121 return *this;
122}
123Quirks::Quirks(Quirks&&) noexcept = default;
124Quirks& Quirks::operator=(Quirks&&) noexcept = default;
125
126bool Quirks::BlockCharacters() const {
127 return impl_->block_characters;
128}
129void Quirks::SetBlockCharacters(bool v) {
130 impl_->block_characters = v;
131}
132
133bool Quirks::CursorHiding() const {
134 return impl_->cursor_hiding;
135}
136void Quirks::SetCursorHiding(bool v) {
137 impl_->cursor_hiding = v;
138}
139
140bool Quirks::ComponentAscii() const {
141 return impl_->component_ascii;
142}
143void Quirks::SetComponentAscii(bool v) {
144 impl_->component_ascii = v;
145}
146
147Color Quirks::ColorSupport() const {
148 return impl_->color_support;
149}
150void Quirks::SetColorSupport(Color v) {
151 impl_->color_support = v;
152}
153
154struct TerminalInfo::Impl {
155 std::string term;
156 std::string colorterm;
157 std::string term_program;
158 std::string terminal_name;
159 std::string terminal_emulator_name;
160 std::vector<int> capabilities;
161};
162
163TerminalInfo::TerminalInfo() : impl_(std::make_unique<Impl>()) {}
164TerminalInfo::~TerminalInfo() = default;
165TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
166TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;
167
168void TerminalInfo::SetTerm(std::string_view term) {
169 impl_->term = term;
170}
171void TerminalInfo::SetColorterm(std::string_view colorterm) {
172 impl_->colorterm = colorterm;
173}
174void TerminalInfo::SetTermProgram(std::string_view term_program) {
175 impl_->term_program = term_program;
176}
177void TerminalInfo::SetTerminalName(std::string_view terminal_name) {
178 impl_->terminal_name = terminal_name;
179}
180void TerminalInfo::SetTerminalEmulatorName(
181 std::string_view terminal_emulator_name) {
182 impl_->terminal_emulator_name = terminal_emulator_name;
183}
184void TerminalInfo::SetCapabilities(std::vector<int> capabilities) {
185 impl_->capabilities = std::move(capabilities);
186}
187
188/// @brief Calcula el soporte de color según variables de entorno e
189/// identificación de la terminal.
190/// @param term La variable de entorno TERM.
191/// @param colorterm La variable de entorno COLORTERM.
192/// @param term_program La variable de entorno TERM_PROGRAM.
193/// @param terminal_name El nombre de la terminal (de DA2).
194/// @param terminal_emulator_name El nombre del emulador de terminal (de XTVERSION).
195/// @param capabilities Las capacidades de la terminal (de DA1).
196Color ComputeColorSupport(std::string_view term,
197 std::string_view colorterm,
198 std::string_view term_program,
199 std::string_view terminal_name,
200 std::string_view terminal_emulator_name,
201 const std::vector<int>& capabilities) {
202 TerminalInfo info;
203 info.SetTerm(term);
204 info.SetColorterm(colorterm);
205 info.SetTermProgram(term_program);
206 info.SetTerminalName(terminal_name);
207 info.SetTerminalEmulatorName(terminal_emulator_name);
208 info.SetCapabilities(capabilities);
209 return info.ComputeColorSupport();
210}
211
212Color TerminalInfo::ComputeColorSupport() const {
213 // TODO(v8): Leer NO_COLOR y WT_SESSION desde ComputeColorSupportInternal()
214 // y pasarlos como parámetros, para que esta función siga siendo una función
215 // pura de sus entradas. Esto requiere ampliar la firma pública de
216 // Terminal::ComputeColorSupport(), es decir, un cambio que rompe la API.
217
218 // 0. Preferencia del usuario. Ver https://no-color.org.
219 if (util::GetEnv("NO_COLOR")[0] != '\0') {
220 return Terminal::Color::Palette1;
221 }
222
223 // 1. Anulaciones específicas de la plataforma.
224#if defined(__EMSCRIPTEN__)
225 return Terminal::Color::TrueColor;
226#endif
227#if defined(_WIN32)
228 // Comprobar si estamos ejecutando en una consola, y si esa consola admite el procesamiento VT.
229 auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
230 DWORD out_mode = 0;
231 if (GetConsoleMode(stdout_handle, &out_mode)) {
232 const int enable_virtual_terminal_processing = 0x0004;
233 const int disable_newline_auto_return = 0x0008;
234 out_mode |= enable_virtual_terminal_processing;
235 out_mode |= disable_newline_auto_return;
236 if (!SetConsoleMode(stdout_handle, out_mode)) {
237 return Terminal::Color::Palette16;
238 }
239 }
240 return Terminal::Color::TrueColor;
241#endif
242
243 // Comprobar WT_SESSION para Windows Terminal (por ejemplo, al ejecutar bajo WSL).
244 if (util::GetEnv("WT_SESSION")[0] != '\0') {
245 return Terminal::Color::TrueColor;
246 }
247
248 // 2. Variables de entorno term / colorterm.
249 if (ContainsAny(impl_->colorterm, {"24bit", "truecolor"})) {
250 return Terminal::Color::TrueColor;
251 }
252 if (ContainsAny(impl_->term,
253 {"direct", "truecolor", "kitty", "alacritty", "foot"})) {
254 return Terminal::Color::TrueColor;
255 }
256 if (ContainsAny(impl_->colorterm, {"256"}) ||
257 ContainsAny(impl_->term, {"256", "xterm", "screen", "tmux"})) {
258 return Terminal::Color::Palette256;
259 }
260
261 // 3. term_program
262 if (ContainsAny(impl_->term_program, {
263 "iterm",
264 "vscode",
265 "warp",
266 "ghostty",
267 "wezterm",
268 })) {
269 return Terminal::Color::TrueColor;
270 }
271 // Terminal.app de Apple (TERM_PROGRAM=Apple_Terminal) admite 256 colores,
272 // pero no de 24 bits.
273 if (Contains(impl_->term_program, "apple_terminal")) {
274 return Terminal::Color::Palette256;
275 }
276
277 // 4. identificación de la terminal.
278 // Un nombre vacío significa que la terminal no fue identificada, lo mismo que
279 // "unknown".
280 if (!impl_->terminal_emulator_name.empty() &&
281 impl_->terminal_emulator_name != "unknown") {
282 return Terminal::Color::TrueColor;
283 }
284 if (impl_->terminal_name == "xterm") {
285 return Terminal::Color::TrueColor;
286 }
287 for (const int x : impl_->capabilities) {
288 // El valor 22 es la capacidad SGR para 256 colores. Si la terminal
289 // lo admite, es una fuerte indicación de que la terminal admite 256
290 // colores. Este no es un método de detección perfecto, pero es una heurística
291 // razonable en ausencia de información más específica.
292 if (x == 22) {
293 return Terminal::Color::Palette256;
294 }
295 }
296
297 return Terminal::Color::Palette16;
298}
299
300/// @brief Obtiene el tamaño del terminal.
301/// @return El tamaño del terminal.
302/// @ingroup screen
303Dimensions Size() {
304#if defined(__EMSCRIPTEN__)
305 // Esta dimensión fue elegida arbitrariamente para poder mostrar:
306 // https://arthursonzogni.com/FTXUI/examples
307 // Esto deberá mejorarse cuando alguien tenga tiempo de implementarlo y lo necesite.
308 return FallbackSize();
309#elif defined(_WIN32)
310 CONSOLE_SCREEN_BUFFER_INFO csbi;
311
312 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
313 return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
314 csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
315 }
316
317 return FallbackSize();
318#else
319 winsize w{};
320 const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // NOLINT
321 // The ioctl return value result should be checked. Some operating systems
322 // don't support TIOCGWINSZ.
323 if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
324 return FallbackSize();
325 }
326 return Dimensions{w.ws_col, w.ws_row};
327#endif
328}
329
330/// @brief Anula el tamaño del terminal en caso de que la autodetección falle
331/// @param fallbackSize Dimensiones del terminal a las que recurrir
332void SetFallbackSize(const Dimensions& fallbackSize) {
333 FallbackSize() = fallbackSize;
334}
335
336/// @brief Obtiene el soporte de color del terminal.
337/// @ingroup screen
338Color ColorSupport() {
339 if (!ColorSupportDetected()) {
340 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
341 ColorSupportDetected() = true;
342 }
343 return GetQuirksInternal().ColorSupport();
344}
345
346/// @brief Anula el soporte de color del terminal en caso de que la autodetección falle
347/// @ingroup dom
348void SetColorSupport(Color color) {
349 GetQuirksInternal().SetColorSupport(color);
350 ColorSupportDetected() = true;
351}
352
353/// @brief Obtiene las peculiaridades de la terminal.
354/// @ingroup screen
355Quirks GetQuirks() {
356 if (!ColorSupportDetected()) {
357 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
358 ColorSupportDetected() = true;
359 }
360 return GetQuirksInternal();
361}
362
363/// @brief Anula las peculiaridades de la terminal.
364/// @ingroup screen
365void SetQuirks(const Quirks& quirks) {
366 GetQuirksInternal() = quirks;
367 ColorSupportDetected() = true;
368}
369
370} // namespace Terminal
371} // namespace ftxui
Color
Color es una enumeración que representa el soporte de color de la terminal.
Definition terminal.hpp:31
El espacio de nombres ftxui::Terminal:: de FTXUI.
Color ComputeColorSupport(std::string_view term, std::string_view colorterm, std::string_view term_program, std::string_view terminal_name, std::string_view terminal_emulator_name, const std::vector< int > &capabilities)
Calcula el soporte de color según variables de entorno e identificación de la terminal.
Definition terminal.cpp:196
const char * GetEnv(const char *name)
Definition util.hpp:18
El espacio de nombres ftxui:: de FTXUI.
Definition animation.hpp:11