FTXUI 6.1.9
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 <cstdlib> // for getenv
7#include <initializer_list>
8#include <string>
9#include <string_view> // for string_view
10
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
26namespace ftxui {
27
28namespace {
29
30bool g_color_support_detected = false; // NOLINT
31Terminal::Quirks g_quirks = [] { // NOLINT
32 Terminal::Quirks quirks;
33#if defined(_WIN32)
34 quirks.SetBlockCharacters(false);
35 quirks.SetCursorHiding(false);
36 quirks.SetComponentAscii(true);
37#endif
38 return quirks;
39}();
40
41Dimensions& FallbackSize() {
42#if defined(__EMSCRIPTEN__)
43 // This dimension was chosen arbitrarily to be able to display:
44 // https://arthursonzogni.com/FTXUI/examples
45 // This will have to be improved when someone has time to implement and need
46 // it.
47 constexpr int fallback_width = 140;
48 constexpr int fallback_height = 43;
49#else
50 // The terminal size in VT100 was 80x24. It is still used nowadays by
51 // default in many terminal emulator. That's a good choice for a fallback
52 // value.
53 constexpr int fallback_width = 80;
54 constexpr int fallback_height = 24;
55#endif
56 static Dimensions g_fallback_size{
57 fallback_width,
58 fallback_height,
59 };
60 return g_fallback_size;
61}
62
63const char* Safe(const char* c) {
64 return (c != nullptr) ? c : "";
65}
66
67bool Contains(std::string_view s, std::string_view key) {
68 if (key.empty()) {
69 return true;
70 }
71 const auto it = std::search( // NOLINT
72 s.begin(), s.end(), key.begin(), key.end(), [](char a, char b) {
73 return std::tolower(static_cast<unsigned char>(a)) ==
74 std::tolower(static_cast<unsigned char>(b));
75 });
76 return it != s.end();
77}
78
79bool ContainsAny(std::string_view s,
80 std::initializer_list<std::string_view> keys) {
81 for (const std::string_view key : keys) {
82 if (Contains(s, key)) {
83 return true;
84 }
85 }
86 return false;
87}
88
89Terminal::Color ComputeColorSupportInternal() {
90 static const std::vector<int> empty_capabilities;
92 Safe(std::getenv("TERM")), // NOLINT
93 Safe(std::getenv("COLORTERM")), // NOLINT
94 Safe(std::getenv("TERM_PROGRAM")), // NOLINT
95 "unknown", "unknown", empty_capabilities);
96}
97
98} // namespace
99
100namespace Terminal {
101
102struct Quirks::Impl {
103 bool block_characters = true;
104 bool cursor_hiding = true;
105 bool component_ascii = false;
106 Color color_support = Palette256;
107};
108
109Quirks::Quirks() : impl_(std::make_unique<Impl>()) {}
110Quirks::~Quirks() = default;
111Quirks::Quirks(const Quirks& other) : impl_(std::make_unique<Impl>(*other.impl_)) {}
112Quirks& Quirks::operator=(const Quirks& other) {
113 if (this != &other) {
114 *impl_ = *other.impl_;
115 }
116 return *this;
117}
118Quirks::Quirks(Quirks&&) noexcept = default;
119Quirks& Quirks::operator=(Quirks&&) noexcept = default;
120
121bool Quirks::BlockCharacters() const {
122 return impl_->block_characters;
123}
124void Quirks::SetBlockCharacters(bool v) {
125 impl_->block_characters = v;
126}
127
128bool Quirks::CursorHiding() const {
129 return impl_->cursor_hiding;
130}
131void Quirks::SetCursorHiding(bool v) {
132 impl_->cursor_hiding = v;
133}
134
135bool Quirks::ComponentAscii() const {
136 return impl_->component_ascii;
137}
138void Quirks::SetComponentAscii(bool v) {
139 impl_->component_ascii = v;
140}
141
142Color Quirks::ColorSupport() const {
143 return impl_->color_support;
144}
145void Quirks::SetColorSupport(Color v) {
146 impl_->color_support = v;
147}
148
149struct TerminalInfo::Impl {
150 std::string term;
151 std::string colorterm;
152 std::string term_program;
153 std::string terminal_name;
154 std::string terminal_emulator_name;
155 std::vector<int> capabilities;
156};
157
158TerminalInfo::TerminalInfo() : impl_(std::make_unique<Impl>()) {}
159TerminalInfo::~TerminalInfo() = default;
160TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
161TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;
162
163void TerminalInfo::SetTerm(std::string_view term) {
164 impl_->term = term;
165}
166void TerminalInfo::SetColorterm(std::string_view colorterm) {
167 impl_->colorterm = colorterm;
168}
169void TerminalInfo::SetTermProgram(std::string_view term_program) {
170 impl_->term_program = term_program;
171}
172void TerminalInfo::SetTerminalName(std::string_view terminal_name) {
173 impl_->terminal_name = terminal_name;
174}
175void TerminalInfo::SetTerminalEmulatorName(
176 std::string_view terminal_emulator_name) {
177 impl_->terminal_emulator_name = terminal_emulator_name;
178}
179void TerminalInfo::SetCapabilities(std::vector<int> capabilities) {
180 impl_->capabilities = std::move(capabilities);
181}
182
183/// @brief Compute the color support based on environment variables and terminal
184/// identification.
185/// @param term The TERM environment variable.
186/// @param colorterm The COLORTERM environment variable.
187/// @param term_program The TERM_PROGRAM environment variable.
188/// @param terminal_name The terminal name (from DA2).
189/// @param terminal_emulator_name The terminal emulator name (from XTVERSION).
190/// @param capabilities The terminal capabilities (from DA1).
191Color ComputeColorSupport(std::string_view term,
192 std::string_view colorterm,
193 std::string_view term_program,
194 std::string_view terminal_name,
195 std::string_view terminal_emulator_name,
196 const std::vector<int>& capabilities) {
197 TerminalInfo info;
198 info.SetTerm(term);
199 info.SetColorterm(colorterm);
200 info.SetTermProgram(term_program);
201 info.SetTerminalName(terminal_name);
202 info.SetTerminalEmulatorName(terminal_emulator_name);
203 info.SetCapabilities(capabilities);
204 return info.ComputeColorSupport();
205}
206
207Color TerminalInfo::ComputeColorSupport() const {
208 // 0. Platform specific overrides.
209#if defined(__EMSCRIPTEN__)
210 return Terminal::Color::TrueColor;
211#endif
212
213 // 1. term / colorterm environment variables.
214 if (ContainsAny(impl_->colorterm, {"24bit", "truecolor"})) {
215 return Terminal::Color::TrueColor;
216 }
217 if (ContainsAny(impl_->term,
218 {"direct", "truecolor", "kitty", "alacritty", "foot"})) {
219 return Terminal::Color::TrueColor;
220 }
221 if (ContainsAny(impl_->colorterm, {"256"}) ||
222 ContainsAny(impl_->term, {"256", "xterm", "screen", "tmux"})) {
223 return Terminal::Color::Palette256;
224 }
225
226 // 2. term_program
227 if (ContainsAny(impl_->term_program, {
228 "iterm",
229 "apple_terminal",
230 "vscode",
231 "warp",
232 "ghostty",
233 "wezterm",
234 })) {
235 return Terminal::Color::TrueColor;
236 }
237 if (Contains(impl_->term_program, "iterm")) {
238 return Terminal::Color::Palette256;
239 }
240
241 // 3. terminal identification.
242 if (impl_->terminal_emulator_name != "unknown") {
243 return Terminal::Color::TrueColor;
244 }
245 if (impl_->terminal_name == "xterm") {
246 return Terminal::Color::TrueColor;
247 }
248 for (const int x : impl_->capabilities) {
249 // The value 22 is the SGR capability for 256 colors. If the terminal
250 // supports it, it is a strong indication that the terminal supports 256
251 // colors. This is not a perfect detection method, but it is a reasonable
252 // heuristic in the absence of more specific information.
253 if (x == 22) {
254 return Terminal::Color::Palette256;
255 }
256 }
257
258 return Terminal::Color::Palette16;
259}
260
261/// @brief Get the terminal size.
262/// @return The terminal size.
263/// @ingroup screen
264Dimensions Size() {
265#if defined(__EMSCRIPTEN__)
266 // This dimension was chosen arbitrarily to be able to display:
267 // https://arthursonzogni.com/FTXUI/examples
268 // This will have to be improved when someone has time to implement and need
269 // it.
270 return FallbackSize();
271#elif defined(_WIN32)
272 CONSOLE_SCREEN_BUFFER_INFO csbi;
273
274 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
275 return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
276 csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
277 }
278
279 return FallbackSize();
280#else
281 winsize w{};
282 const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // NOLINT
283 // The ioctl return value result should be checked. Some operating systems
284 // don't support TIOCGWINSZ.
285 if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
286 return FallbackSize();
287 }
288 return Dimensions{w.ws_col, w.ws_row};
289#endif
290}
291
292/// @brief Override terminal size in case auto-detection fails
293/// @param fallbackSize Terminal dimensions to fallback to
294void SetFallbackSize(const Dimensions& fallbackSize) {
295 FallbackSize() = fallbackSize;
296}
297
298/// @brief Get the color support of the terminal.
299/// @ingroup screen
300Color ColorSupport() {
301 if (!g_color_support_detected) {
302 g_quirks.SetColorSupport(ComputeColorSupportInternal());
303 g_color_support_detected = true;
304 }
305 return g_quirks.ColorSupport();
306}
307
308/// @brief Override terminal color support in case auto-detection fails
309/// @ingroup dom
310void SetColorSupport(Color color) {
311 g_quirks.SetColorSupport(color);
312 g_color_support_detected = true;
313}
314
315/// @brief Get the terminal quirks.
316/// @ingroup screen
317Quirks GetQuirks() {
318 if (!g_color_support_detected) {
319 g_quirks.SetColorSupport(ComputeColorSupportInternal());
320 g_color_support_detected = true;
321 }
322 return g_quirks;
323}
324
325/// @brief Override terminal quirks.
326/// @ingroup screen
327void SetQuirks(const Quirks& quirks) {
328 g_quirks = quirks;
329 g_color_support_detected = true;
330}
331
332} // namespace Terminal
333} // namespace ftxui
Color
Color is an enumeration that represents the color support of the terminal.
Definition terminal.hpp:29
The FTXUI ftxui::Terminal:: namespace.
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)
Compute the color support based on environment variables and terminal identification.
Definition terminal.cpp:191
The FTXUI ftxui:: namespace.
Definition animation.hpp:11