/* vim:set ts=2 sw=2 sts=2 et: */ /** * \author Marcus Holland-Moritz (github@mhxnet.de) * \copyright Copyright (c) Marcus Holland-Moritz * * This file is part of dwarfs. * * dwarfs is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dwarfs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with dwarfs. If not, see . */ #include #include #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include namespace dwarfs { namespace { constexpr size_t default_width{80}; #if defined(_WIN32) void WindowsEmulateVT100Terminal(DWORD std_handle) { static bool done = false; if (done) { return; } done = true; // Enable VT processing on stdout and stdin auto hdl = ::GetStdHandle(std_handle); DWORD out_mode = 0; ::GetConsoleMode(hdl, &out_mode); // https://docs.microsoft.com/en-us/windows/console/setconsolemode static constexpr DWORD enable_virtual_terminal_processing = 0x0004; static constexpr DWORD disable_newline_auto_return = 0x0008; out_mode |= enable_virtual_terminal_processing; ::SetConsoleMode(hdl, out_mode); } size_t width_impl() { CONSOLE_SCREEN_BUFFER_INFO csbi; if (::GetConsoleScreenBufferInfo(::GetStdHandle(STD_ERROR_HANDLE), &csbi)) { return csbi.srWindow.Right - csbi.srWindow.Left + 1; } return default_width; } bool is_tty_impl(std::ostream& os) { if (&os == &std::cout) { return ::_isatty(::_fileno(stdout)); } if (&os == &std::cerr) { return ::_isatty(::_fileno(stderr)); } return false; } bool is_fancy_impl() { return true; } #else size_t width_impl() { struct ::winsize w; auto rv = ::ioctl(STDERR_FILENO, TIOCGWINSZ, &w); return rv == 0 ? w.ws_col : default_width; } bool is_tty_impl(std::ostream& os) { if (&os == &std::cout) { return ::isatty(::fileno(stdout)); } if (&os == &std::cerr) { return ::isatty(::fileno(stderr)); } return false; } bool is_fancy_impl() { // TODO: we might want to use the iolayer here if (auto term = ::getenv("TERM")) { std::string_view term_sv(term); return !term_sv.empty() && term_sv != "dumb"; } return false; } #endif bool setup_impl() { #if defined(_WIN32) WindowsEmulateVT100Terminal(STD_ERROR_HANDLE); ::SetConsoleOutputCP(CP_UTF8); ::SetConsoleCP(CP_UTF8); #endif return true; } } // namespace std::string_view terminal_ansi::color_impl(termcolor color, termstyle style) { static constexpr std::array(termcolor::NUM_COLORS)> // clang-format off colors = {{ "\033[0m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", "\033[37m", "\033[90m", "\033[1;31m", "\033[1;32m", "\033[1;33m", "\033[1;34m", "\033[1;35m", "\033[1;36m", "\033[1;37m", "\033[1;90m", "\033[2;31m", "\033[2;32m", "\033[2;33m", "\033[2;34m", "\033[2;35m", "\033[2;36m", "\033[2;37m", "\033[2;90m", }}; // clang-format on static constexpr size_t const kBoldOffset{ static_cast(termcolor::BOLD_RED) - static_cast(termcolor::RED)}; static constexpr size_t const kDimOffset{ static_cast(termcolor::DIM_RED) - static_cast(termcolor::RED)}; switch (style) { case termstyle::BOLD: case termstyle::DIM: { auto ix = static_cast(color); if (ix < static_cast(termcolor::BOLD_RED)) { color = static_cast( ix + (style == termstyle::BOLD ? kBoldOffset : kDimOffset)); } } break; default: break; } return colors.at(static_cast(color)); } std::string terminal_ansi::colored_impl(std::string_view text, termcolor color, bool enable, termstyle style) { std::string result; if (enable) { auto preamble = color_impl(color, style); auto postamble = color_impl(termcolor::NORMAL); result.reserve(preamble.size() + text.size() + postamble.size()); result.append(preamble); result.append(text); result.append(postamble); } else { result.append(text); } return result; } size_t terminal_ansi::width() const { return width_impl(); } bool terminal_ansi::is_tty(std::ostream& os) const { return is_tty_impl(os); } bool terminal_ansi::is_fancy() const { return is_fancy_impl(); } std::string_view terminal_ansi::color(termcolor color, termstyle style = termstyle::NORMAL) const { return color_impl(color, style); } std::string terminal_ansi::colored(std::string text, termcolor color, bool enable = true, termstyle style = termstyle::NORMAL) const { return colored_impl(std::move(text), color, enable, style); } std::string_view terminal_ansi::carriage_return() const { return "\r"; } std::string_view terminal_ansi::rewind_line() const { return "\x1b[A"; } std::string_view terminal_ansi::clear_line() const { return "\x1b[2K"; } terminal_ansi::terminal_ansi() : terminal_ansi(init_mode::AUTO) {} terminal_ansi::terminal_ansi(init_mode mode) { if (mode == init_mode::AUTO) { static bool initialized [[maybe_unused]] = setup_impl(); } else if (mode == init_mode::FORCE) { setup_impl(); } } } // namespace dwarfs