7#include <initializer_list>
14#define WIN32_LEAN_AND_MEAN
25#if defined(__sun) || defined(__illumos__)
26#include <sys/termios.h>
33std::unique_ptr<Terminal::Quirks> g_quirks;
34bool g_color_support_detected =
false;
36bool& ColorSupportDetected() {
37 return g_color_support_detected;
40Terminal::Quirks& GetQuirksInternal() {
42 g_quirks = std::make_unique<Terminal::Quirks>();
44 g_quirks->SetBlockCharacters(
false);
45 g_quirks->SetCursorHiding(
false);
46 g_quirks->SetComponentAscii(
true);
52Dimensions& FallbackSize() {
53#if defined(__EMSCRIPTEN__)
58 constexpr int fallback_width = 140;
59 constexpr int fallback_height = 43;
64 constexpr int fallback_width = 80;
65 constexpr int fallback_height = 24;
67 static Dimensions g_fallback_size{
71 return g_fallback_size;
74const char* Safe(
const char* c) {
75 return (c !=
nullptr) ? c :
"";
78bool Contains(std::string_view s, std::string_view key) {
82 const auto it = std::search(
83 s.begin(), s.end(), key.begin(), key.end(), [](
char a,
char b) {
84 return std::tolower(static_cast<unsigned char>(a)) ==
85 std::tolower(static_cast<unsigned char>(b));
90bool ContainsAny(std::string_view s,
91 std::initializer_list<std::string_view> keys) {
92 for (
const std::string_view key : keys) {
93 if (Contains(s, key)) {
101 static const std::vector<int> empty_capabilities;
103 Safe(std::getenv(
"TERM")),
104 Safe(std::getenv(
"COLORTERM")),
105 Safe(std::getenv(
"TERM_PROGRAM")),
106 "unknown",
"unknown", empty_capabilities);
114 bool block_characters =
true;
115 bool cursor_hiding =
true;
116 bool component_ascii =
false;
120Quirks::Quirks() : impl_(std::make_unique<Impl>()) {}
121Quirks::~Quirks() =
default;
122Quirks::Quirks(
const Quirks& other)
123 : impl_(std::make_unique<Impl>(*other.impl_)) {}
124Quirks& Quirks::operator=(
const Quirks& other) {
125 if (
this != &other) {
126 *impl_ = *other.impl_;
130Quirks::Quirks(Quirks&&) noexcept = default;
131Quirks& Quirks::operator=(Quirks&&) noexcept = default;
133bool Quirks::BlockCharacters()
const {
134 return impl_->block_characters;
136void Quirks::SetBlockCharacters(
bool v) {
137 impl_->block_characters = v;
140bool Quirks::CursorHiding()
const {
141 return impl_->cursor_hiding;
143void Quirks::SetCursorHiding(
bool v) {
144 impl_->cursor_hiding = v;
147bool Quirks::ComponentAscii()
const {
148 return impl_->component_ascii;
150void Quirks::SetComponentAscii(
bool v) {
151 impl_->component_ascii = v;
154Color Quirks::ColorSupport()
const {
155 return impl_->color_support;
157void Quirks::SetColorSupport(Color v) {
158 impl_->color_support = v;
161struct TerminalInfo::Impl {
163 std::string colorterm;
164 std::string term_program;
165 std::string terminal_name;
166 std::string terminal_emulator_name;
167 std::vector<int> capabilities;
170TerminalInfo::TerminalInfo() : impl_(std::make_unique<Impl>()) {}
171TerminalInfo::~TerminalInfo() =
default;
172TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
173TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;
175void TerminalInfo::SetTerm(std::string_view term) {
178void TerminalInfo::SetColorterm(std::string_view colorterm) {
179 impl_->colorterm = colorterm;
181void TerminalInfo::SetTermProgram(std::string_view term_program) {
182 impl_->term_program = term_program;
184void TerminalInfo::SetTerminalName(std::string_view terminal_name) {
185 impl_->terminal_name = terminal_name;
187void TerminalInfo::SetTerminalEmulatorName(
188 std::string_view terminal_emulator_name) {
189 impl_->terminal_emulator_name = terminal_emulator_name;
191void TerminalInfo::SetCapabilities(std::vector<int> capabilities) {
192 impl_->capabilities = std::move(capabilities);
204 std::string_view colorterm,
205 std::string_view term_program,
206 std::string_view terminal_name,
207 std::string_view terminal_emulator_name,
208 const std::vector<int>& capabilities) {
211 info.SetColorterm(colorterm);
212 info.SetTermProgram(term_program);
213 info.SetTerminalName(terminal_name);
214 info.SetTerminalEmulatorName(terminal_emulator_name);
215 info.SetCapabilities(capabilities);
216 return info.ComputeColorSupport();
219Color TerminalInfo::ComputeColorSupport()
const {
226 const char* no_color = std::getenv(
"NO_COLOR");
227 if (no_color !=
nullptr && no_color[0] !=
'\0') {
228 return Terminal::Color::Palette1;
232#if defined(__EMSCRIPTEN__)
233 return Terminal::Color::TrueColor;
237 auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
239 if (GetConsoleMode(stdout_handle, &out_mode)) {
240 const int enable_virtual_terminal_processing = 0x0004;
241 const int disable_newline_auto_return = 0x0008;
242 out_mode |= enable_virtual_terminal_processing;
243 out_mode |= disable_newline_auto_return;
244 if (!SetConsoleMode(stdout_handle, out_mode)) {
245 return Terminal::Color::Palette16;
248 return Terminal::Color::TrueColor;
252 const char* wt_session = std::getenv(
"WT_SESSION");
253 if (wt_session !=
nullptr && wt_session[0] !=
'\0') {
254 return Terminal::Color::TrueColor;
258 if (ContainsAny(impl_->colorterm, {
"24bit",
"truecolor"})) {
259 return Terminal::Color::TrueColor;
261 if (ContainsAny(impl_->term,
262 {
"direct",
"truecolor",
"kitty",
"alacritty",
"foot"})) {
263 return Terminal::Color::TrueColor;
265 if (ContainsAny(impl_->colorterm, {
"256"}) ||
266 ContainsAny(impl_->term, {
"256",
"xterm",
"screen",
"tmux"})) {
267 return Terminal::Color::Palette256;
271 if (ContainsAny(impl_->term_program, {
278 return Terminal::Color::TrueColor;
282 if (Contains(impl_->term_program,
"apple_terminal")) {
283 return Terminal::Color::Palette256;
289 if (!impl_->terminal_emulator_name.empty() &&
290 impl_->terminal_emulator_name !=
"unknown") {
291 return Terminal::Color::TrueColor;
293 if (impl_->terminal_name ==
"xterm") {
294 return Terminal::Color::TrueColor;
296 for (
const int x : impl_->capabilities) {
302 return Terminal::Color::Palette256;
306 return Terminal::Color::Palette16;
313#if defined(__EMSCRIPTEN__)
318 return FallbackSize();
320 CONSOLE_SCREEN_BUFFER_INFO csbi;
322 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
323 return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
324 csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
327 return FallbackSize();
330 const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
333 if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
334 return FallbackSize();
336 return Dimensions{w.ws_col, w.ws_row};
342void SetFallbackSize(
const Dimensions& fallbackSize) {
343 FallbackSize() = fallbackSize;
349 if (!ColorSupportDetected()) {
350 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
351 ColorSupportDetected() =
true;
353 return GetQuirksInternal().ColorSupport();
359 GetQuirksInternal().SetColorSupport(color);
360 ColorSupportDetected() =
true;
366 if (!ColorSupportDetected()) {
367 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
368 ColorSupportDetected() =
true;
370 return GetQuirksInternal();
375void SetQuirks(
const Quirks& quirks) {
376 GetQuirksInternal() = quirks;
377 ColorSupportDetected() =
true;
Color
Color is an enumeration that represents the color support of the terminal.
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.
The FTXUI ftxui:: namespace.