6#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__)
57 constexpr int fallback_width = 140;
58 constexpr int fallback_height = 43;
61 constexpr int fallback_width = 80;
62 constexpr int fallback_height = 24;
64 static Dimensions g_fallback_size{
68 return g_fallback_size;
71bool Contains(std::string_view s, std::string_view key) {
75 const auto it = std::search(
76 s.begin(), s.end(), key.begin(), key.end(), [](
char a,
char b) {
77 return std::tolower(static_cast<unsigned char>(a)) ==
78 std::tolower(static_cast<unsigned char>(b));
83bool ContainsAny(std::string_view s,
84 std::initializer_list<std::string_view> keys) {
85 for (
const std::string_view key : keys) {
86 if (Contains(s, key)) {
94 static const std::vector<int> empty_capabilities;
97 util::GetEnv(
"TERM_PROGRAM"),
"unknown",
"unknown", empty_capabilities);
105 bool block_characters =
true;
106 bool cursor_hiding =
true;
107 bool component_ascii =
false;
111Quirks::Quirks() : impl_(std::make_unique<Impl>()) {}
112Quirks::~Quirks() =
default;
113Quirks::Quirks(
const Quirks& other)
114 : impl_(std::make_unique<Impl>(*other.impl_)) {}
115Quirks& Quirks::operator=(
const Quirks& other) {
116 if (
this != &other) {
117 *impl_ = *other.impl_;
121Quirks::Quirks(Quirks&&) noexcept = default;
122Quirks& Quirks::operator=(Quirks&&) noexcept = default;
124bool Quirks::BlockCharacters()
const {
125 return impl_->block_characters;
127void Quirks::SetBlockCharacters(
bool v) {
128 impl_->block_characters = v;
131bool Quirks::CursorHiding()
const {
132 return impl_->cursor_hiding;
134void Quirks::SetCursorHiding(
bool v) {
135 impl_->cursor_hiding = v;
138bool Quirks::ComponentAscii()
const {
139 return impl_->component_ascii;
141void Quirks::SetComponentAscii(
bool v) {
142 impl_->component_ascii = v;
145Color Quirks::ColorSupport()
const {
146 return impl_->color_support;
148void Quirks::SetColorSupport(Color v) {
149 impl_->color_support = v;
152struct TerminalInfo::Impl {
154 std::string colorterm;
155 std::string term_program;
156 std::string terminal_name;
157 std::string terminal_emulator_name;
158 std::vector<int> capabilities;
161TerminalInfo::TerminalInfo() : impl_(std::make_unique<Impl>()) {}
162TerminalInfo::~TerminalInfo() =
default;
163TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
164TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;
166void TerminalInfo::SetTerm(std::string_view term) {
169void TerminalInfo::SetColorterm(std::string_view colorterm) {
170 impl_->colorterm = colorterm;
172void TerminalInfo::SetTermProgram(std::string_view term_program) {
173 impl_->term_program = term_program;
175void TerminalInfo::SetTerminalName(std::string_view terminal_name) {
176 impl_->terminal_name = terminal_name;
178void TerminalInfo::SetTerminalEmulatorName(
179 std::string_view terminal_emulator_name) {
180 impl_->terminal_emulator_name = terminal_emulator_name;
182void TerminalInfo::SetCapabilities(std::vector<int> capabilities) {
183 impl_->capabilities = std::move(capabilities);
195 std::string_view colorterm,
196 std::string_view term_program,
197 std::string_view terminal_name,
198 std::string_view terminal_emulator_name,
199 const std::vector<int>& capabilities) {
202 info.SetColorterm(colorterm);
203 info.SetTermProgram(term_program);
204 info.SetTerminalName(terminal_name);
205 info.SetTerminalEmulatorName(terminal_emulator_name);
206 info.SetCapabilities(capabilities);
207 return info.ComputeColorSupport();
210Color TerminalInfo::ComputeColorSupport()
const {
217 if (util::GetEnv(
"NO_COLOR")[0] !=
'\0') {
218 return Terminal::Color::Palette1;
222#if defined(__EMSCRIPTEN__)
223 return Terminal::Color::TrueColor;
227 auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
229 if (GetConsoleMode(stdout_handle, &out_mode)) {
230 const int enable_virtual_terminal_processing = 0x0004;
231 const int disable_newline_auto_return = 0x0008;
232 out_mode |= enable_virtual_terminal_processing;
233 out_mode |= disable_newline_auto_return;
234 if (!SetConsoleMode(stdout_handle, out_mode)) {
235 return Terminal::Color::Palette16;
238 return Terminal::Color::TrueColor;
242 if (util::GetEnv(
"WT_SESSION")[0] !=
'\0') {
243 return Terminal::Color::TrueColor;
247 if (ContainsAny(impl_->colorterm, {
"24bit",
"truecolor"})) {
248 return Terminal::Color::TrueColor;
250 if (ContainsAny(impl_->term,
251 {
"direct",
"truecolor",
"kitty",
"alacritty",
"foot"})) {
252 return Terminal::Color::TrueColor;
254 if (ContainsAny(impl_->colorterm, {
"256"}) ||
255 ContainsAny(impl_->term, {
"256",
"xterm",
"screen",
"tmux"})) {
256 return Terminal::Color::Palette256;
260 if (ContainsAny(impl_->term_program, {
267 return Terminal::Color::TrueColor;
271 if (Contains(impl_->term_program,
"apple_terminal")) {
272 return Terminal::Color::Palette256;
278 if (!impl_->terminal_emulator_name.empty() &&
279 impl_->terminal_emulator_name !=
"unknown") {
280 return Terminal::Color::TrueColor;
282 if (impl_->terminal_name ==
"xterm") {
283 return Terminal::Color::TrueColor;
285 for (
const int x : impl_->capabilities) {
291 return Terminal::Color::Palette256;
295 return Terminal::Color::Palette16;
302#if defined(__EMSCRIPTEN__)
306 return FallbackSize();
308 CONSOLE_SCREEN_BUFFER_INFO csbi;
310 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
311 return Dimensions{csbi.srWindow.Right - csbi.srWindow.Left + 1,
312 csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
315 return FallbackSize();
318 const int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
321 if (w.ws_col == 0 || w.ws_row == 0 || status < 0) {
322 return FallbackSize();
324 return Dimensions{w.ws_col, w.ws_row};
330void SetFallbackSize(
const Dimensions& fallbackSize) {
331 FallbackSize() = fallbackSize;
337 if (!ColorSupportDetected()) {
338 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
339 ColorSupportDetected() =
true;
341 return GetQuirksInternal().ColorSupport();
347 GetQuirksInternal().SetColorSupport(color);
348 ColorSupportDetected() =
true;
354 if (!ColorSupportDetected()) {
355 GetQuirksInternal().SetColorSupport(ComputeColorSupportInternal());
356 ColorSupportDetected() =
true;
358 return GetQuirksInternal();
363void SetQuirks(
const Quirks& quirks) {
364 GetQuirksInternal() = quirks;
365 ColorSupportDetected() =
true;
Color
Color 是一個列舉,表示終端機的色彩支援
FTXUI ftxui::Terminal:: 命名空間
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)
根據環境變數與終端機辨識資訊,計算 顏色支援等級。
const char * GetEnv(const char *name)