diff options
| author | 2025-12-22 21:45:47 +0100 | |
|---|---|---|
| committer | 2025-12-22 21:46:59 +0100 | |
| commit | 241ab15be837b681f3cdfc555ec2e8d2638f759f (patch) | |
| tree | 3399a11acb3f50b4ed05a551551c2c6feeb4b4e5 /src/bin | |
| parent | 84c82d205fea95c5cdea1a3ba084f9fa74417649 (diff) | |
| download | OneRoll-241ab15be837b681f3cdfc555ec2e8d2638f759f.tar.gz OneRoll-241ab15be837b681f3cdfc555ec2e8d2638f759f.zip | |
[various fixes]Cli improvement of managing color.
Diffstat (limited to 'src/bin')
| -rw-r--r-- | src/bin/cli/displaytoolbox.cpp | 72 | ||||
| -rw-r--r-- | src/bin/cli/displaytoolbox.h | 3 | ||||
| -rw-r--r-- | src/bin/cli/main.cpp | 34 | ||||
| -rw-r--r-- | src/bin/cli/rang.hpp | 503 |
4 files changed, 542 insertions, 70 deletions
diff --git a/src/bin/cli/displaytoolbox.cpp b/src/bin/cli/displaytoolbox.cpp index 1709d18..ec629a8 100644 --- a/src/bin/cli/displaytoolbox.cpp +++ b/src/bin/cli/displaytoolbox.cpp @@ -2,6 +2,7 @@ #include <QBuffer> #include <QJsonArray> #include <QJsonObject> +#include <sstream> #ifdef PAINTER_OP #include <QFont> @@ -34,50 +35,25 @@ QString DisplayToolBox::makeImage(QByteArray svgCode) } #endif -QString DisplayToolBox::colorToIntCode(QString str) +rang::fg DisplayToolBox::colorToIntCode(QString str) { - if(str.isEmpty() || str == QStringLiteral("black")) - { - return QStringLiteral("0;31"); - } - else if(str == QStringLiteral("white")) - { - return QStringLiteral("97"); - } - else if(str == QStringLiteral("blue")) - { - return QStringLiteral("34"); - } - else if(str == QStringLiteral("red")) - { - return QStringLiteral("31"); - } - else if(str == QStringLiteral("black")) - { - return QStringLiteral("30"); - } - else if(str == QStringLiteral("green")) - { - return QStringLiteral("32"); - } - else if(str == QStringLiteral("yellow")) - { - return QStringLiteral("33"); - } - else if(str == QStringLiteral("cyan")) - { - return QStringLiteral("36"); - } - else if(str == QStringLiteral("reset")) - { - return QStringLiteral("0"); - } - return {}; + QHash<QString, rang::fg> data{ + {"black", rang::fg::black}, {"red", rang::fg::red}, {"green", rang::fg::green}, + {"yellow", rang::fg::yellow}, {"blue", rang::fg::blue}, {"magenta", rang::fg::magenta}, + {"cyan", rang::fg::cyan}, {"gray", rang::fg::gray}, {"reset", rang::fg::reset}}; + + if(data.contains(str)) + return data.value(str); + + return rang::fg::black; } QString DisplayToolBox::colorToTermCode(QString str) { - return QStringLiteral("\e[").append(DisplayToolBox::colorToIntCode(str)).append("m"); + rang::setControlMode(rang::control::Force); + std::stringstream s; + s << DisplayToolBox::colorToIntCode(str); + return QString::fromStdString(s.str()); } QString DisplayToolBox::diceToSvg(QJsonArray array, bool withColor, bool allSameColor, bool allSameFaceCount) @@ -91,7 +67,7 @@ QString DisplayToolBox::diceToSvg(QJsonArray array, bool withColor, bool allSame QStringList subResult; auto obj= item.toObject(); auto values= obj["values"].toArray(); - for(auto valRef : values) + for(auto valRef : std::as_const(values)) { subResult.append(diceResultToString(valRef.toObject(), Output::Svg, withColor)); } @@ -108,7 +84,7 @@ QString DisplayToolBox::diceToSvg(QJsonArray array, bool withColor, bool allSame auto obj= item.toObject(); auto values= obj["values"].toArray(); - for(auto valRef : values) + for(auto valRef : std::as_const(values)) { subResult.append(diceResultToString(valRef.toObject(), Output::Svg, withColor)); } @@ -139,7 +115,7 @@ QString DisplayToolBox::diceResultToString(QJsonObject val, Output type, bool ha auto subvalues= val["subvalues"].toArray(); QStringList subStr; - for(auto subval : subvalues) + for(auto subval : std::as_const(subvalues)) { subStr << QString::number(subval.toDouble()); } @@ -151,17 +127,13 @@ QString DisplayToolBox::diceResultToString(QJsonObject val, Output type, bool ha { if(type == Output::Terminal) { - total= QStringLiteral("%1%2%3") - .arg(DisplayToolBox::colorToTermCode(color)) - .arg(total) - .arg(DisplayToolBox::colorToTermCode(QStringLiteral("reset"))); + total= QStringLiteral("%1%2%3").arg(DisplayToolBox::colorToTermCode(color), total, + DisplayToolBox::colorToTermCode(QStringLiteral("reset"))); } else if(type == Output::Svg) { - total= QStringLiteral("%1%2%3") - .arg(QStringLiteral("<tspan fill=\"%1\">").arg(color)) - .arg(total) - .arg(QStringLiteral("</tspan>")); + total= QStringLiteral("%1%2%3").arg(QStringLiteral("<tspan fill=\"%1\">").arg(color), total, + QStringLiteral("</tspan>")); } } return total; diff --git a/src/bin/cli/displaytoolbox.h b/src/bin/cli/displaytoolbox.h index 5a22b0f..2b0b556 100644 --- a/src/bin/cli/displaytoolbox.h +++ b/src/bin/cli/displaytoolbox.h @@ -5,6 +5,7 @@ #include <QString> #include "diceparser/diceparser.h" +#include "rang.hpp" class DisplayToolBox { @@ -20,7 +21,7 @@ public: #ifdef PAINTER_OP static QString makeImage(QByteArray svgCode); #endif - static QString colorToIntCode(QString str); + static rang::fg colorToIntCode(QString str); static QString colorToTermCode(QString str); static QString diceToSvg(QJsonArray array, bool withColor, bool allSameColor, bool allSameFaceCount); static QString diceResultToString(QJsonObject val, Output type, bool hasColor); diff --git a/src/bin/cli/main.cpp b/src/bin/cli/main.cpp index a63f0ab..d82375f 100644 --- a/src/bin/cli/main.cpp +++ b/src/bin/cli/main.cpp @@ -41,10 +41,8 @@ #include "diceparser/dicealias.h" #include "diceparser/diceparser.h" -#include "diceparser/highlightdice.h" #include "diceparser/parsingtoolbox.h" #include "displaytoolbox.h" - /** * @page Dice * The cli for DiceParser the new dice system from rolisteam. @@ -99,18 +97,18 @@ void displayMarkdown(QString json) auto comment= obj["comment"].toString(); auto arrayInst= obj["instructions"].toArray(); QStringList diceResults; - for(auto inst : arrayInst) + for(auto inst : std::as_const(arrayInst)) { auto obj= inst.toObject(); auto diceVals= obj["diceval"].toArray(); - for(auto diceval : diceVals) + for(auto diceval : std::as_const(diceVals)) { auto objval= diceval.toObject(); auto resultStr= QString::number(objval["value"].toDouble()); auto subvalues= objval["subvalues"].toArray(); QStringList subValueStr; - for(auto sub : subvalues) + for(auto sub : std::as_const(subvalues)) { subValueStr << QString::number(sub.toDouble()); } @@ -145,7 +143,7 @@ void displayMarkdown(QString json) } else { - str.append(QObject::tr("# %1\nDetails:[%3 (%2)]\n").arg(scalarText).arg(diceList).arg(cmd)); + str.append(QObject::tr("# %1\nDetails:[%3 (%2)]\n").arg(scalarText, diceList, cmd)); } } str.append(QStringLiteral("```")); @@ -160,11 +158,11 @@ QString displaySVG(QString json, bool withColor) auto comment= obj["warning"].toString(); auto arrayInst= obj["instructions"].toArray(); QStringList diceResults; - for(auto inst : arrayInst) + for(auto inst : std::as_const(arrayInst)) { auto obj= inst.toObject(); auto diceVals= obj["diceval"].toArray(); - for(auto diceval : diceVals) + for(auto diceval : std::as_const(diceVals)) { auto objval= diceval.toObject(); auto resultStr= objval["string"].toString(); @@ -213,17 +211,13 @@ QString displaySVG(QString json, bool withColor) str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%4\"><tspan " "fill=\"red\">%1</tspan>\n" "<tspan x=\"0\" y=\"%5\">details:</tspan>[%3 (%2)]</text>") - .arg(scalarText) - .arg(diceList) - .arg(cmd) + .arg(scalarText, diceList, cmd) .arg(y) .arg(y * 2)); else str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%4\"><tspan>%1</tspan>\n" "<tspan x=\"0\" y=\"%5\">details:</tspan>[%3 (%2)]</text>") - .arg(scalarText) - .arg(diceList) - .arg(cmd) + .arg(scalarText, diceList, cmd) .arg(y) .arg(y * 2)); } @@ -242,6 +236,7 @@ void displayImage(QString json, bool withColor) void displayCommandResult(QString json, bool withColor, QString color) { + using namespace Qt::Literals::StringLiterals; QJsonDocument doc= QJsonDocument::fromJson(json.toUtf8()); auto obj= doc.object(); auto error= obj["error"].toString(); @@ -277,8 +272,9 @@ void displayCommandResult(QString json, bool withColor, QString color) QString str; if(withColor) - str= QObject::tr("Result: \e[0;%4;1m%1\e[0m - details:[%3 (%2)]") - .arg(scalarText, diceList, cmd, DisplayToolBox::colorToIntCode(color)); + str= QObject::tr("Result: %4%1%5 - details:[%3 (%2)]") + .arg(scalarText, diceList, cmd, DisplayToolBox::colorToTermCode(color), + DisplayToolBox::colorToTermCode("reset")); else str= QObject::tr("Result: %1 - details:[%3 (%2)]").arg(scalarText, diceList, cmd); @@ -291,7 +287,7 @@ void displayCommandResult(QString json, bool withColor, QString color) if(!comment.isEmpty()) { if(withColor) - out << "\033[1m" << comment << "\033[0m\n"; + out << DisplayToolBox::colorToTermCode("yellow") << comment << DisplayToolBox::colorToTermCode("reset"); else out << comment << " "; } @@ -368,7 +364,7 @@ int startDiceParsing(QStringList& cmds, bool withColor, QString baseColor, EXPOR style+= QStringLiteral("font-weight=\"bold\" "); } if(!style.isEmpty()) - result= QString("<tspan %2>%1</tspan>").arg(value).arg(style); + result= QString("<tspan %2>%1</tspan>").arg(value, style); return result; }); } @@ -391,7 +387,7 @@ int startDiceParsing(QStringList& cmds, bool withColor, QString baseColor, EXPOR auto front= DisplayToolBox::colorToTermCode(trueColor); auto end= front.isEmpty() ? "" : DisplayToolBox::colorToTermCode("reset"); - return hightlight ? QString("%1%2%3").arg(front).arg(result).arg(end) : result; + return hightlight ? QString("%1%2%3").arg(front, result, end) : result; }); } else diff --git a/src/bin/cli/rang.hpp b/src/bin/cli/rang.hpp new file mode 100644 index 0000000..59e7b42 --- /dev/null +++ b/src/bin/cli/rang.hpp @@ -0,0 +1,503 @@ +#ifndef RANG_DOT_HPP +#define RANG_DOT_HPP + +#if defined(__unix__) || defined(__unix) || defined(__linux__) +#define OS_LINUX +#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) +#define OS_WIN +#elif defined(__APPLE__) || defined(__MACH__) +#define OS_MAC +#else +#error Unknown Platform +#endif + +#if defined(OS_LINUX) || defined(OS_MAC) +#include <unistd.h> + +#elif defined(OS_WIN) + +#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) +#error \ + "Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA" +#elif !defined(_WIN32_WINNT) +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + +#include <windows.h> +#include <io.h> +#include <memory> + +// Only defined in windows 10 onwards, redefining in lower windows since it +// doesn't gets used in lower versions +// https://docs.microsoft.com/en-us/windows/console/getconsolemode +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +#endif + +#include <algorithm> +#include <atomic> +#include <cstdlib> +#include <cstring> +#include <iostream> + +namespace rang { + +/* For better compability with most of terminals do not use any style settings + * except of reset, bold and reversed. + * Note that on Windows terminals bold style is same as fgB color. + */ +enum class style { + reset = 0, + bold = 1, + dim = 2, + italic = 3, + underline = 4, + blink = 5, + rblink = 6, + reversed = 7, + conceal = 8, + crossed = 9 +}; + +enum class fg { + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + gray = 37, + reset = 39 +}; + +enum class bg { + black = 40, + red = 41, + green = 42, + yellow = 43, + blue = 44, + magenta = 45, + cyan = 46, + gray = 47, + reset = 49 +}; + +enum class fgB { + black = 90, + red = 91, + green = 92, + yellow = 93, + blue = 94, + magenta = 95, + cyan = 96, + gray = 97 +}; + +enum class bgB { + black = 100, + red = 101, + green = 102, + yellow = 103, + blue = 104, + magenta = 105, + cyan = 106, + gray = 107 +}; + +enum class control { // Behaviour of rang function calls + Off = 0, // toggle off rang style/color calls + Auto = 1, // (Default) autodect terminal and colorize if needed + Force = 2 // force ansi color output to non terminal streams +}; +// Use rang::setControlMode to set rang control mode + +enum class winTerm { // Windows Terminal Mode + Auto = 0, // (Default) automatically detects wheter Ansi or Native API + Ansi = 1, // Force use Ansi API + Native = 2 // Force use Native API +}; +// Use rang::setWinTermMode to explicitly set terminal API for Windows +// Calling rang::setWinTermMode have no effect on other OS + +namespace rang_implementation { + + inline std::atomic<control> &controlMode() noexcept + { + static std::atomic<control> value(control::Auto); + return value; + } + + inline std::atomic<winTerm> &winTermMode() noexcept + { + static std::atomic<winTerm> termMode(winTerm::Auto); + return termMode; + } + + inline bool supportsColor() noexcept + { +#if defined(OS_LINUX) || defined(OS_MAC) + + static const bool result = [] { + const char *Terms[] + = { "ansi", "color", "console", "cygwin", "gnome", + "konsole", "kterm", "linux", "msys", "putty", + "rxvt", "screen", "vt100", "xterm" }; + + const char *env_p = std::getenv("TERM"); + std::cout << "Rang: " <<env_p <<std::endl; + if (env_p == nullptr) { + return false; + } + return std::any_of(std::begin(Terms), std::end(Terms), + [&](const char *term) { + return std::strstr(env_p, term) != nullptr; + }); + }(); + +#elif defined(OS_WIN) + // All windows versions support colors through native console methods + static constexpr bool result = true; +#endif + return result; + } + +#ifdef OS_WIN + + + inline bool isMsysPty(int fd) noexcept + { + // Dynamic load for binary compability with old Windows + const auto ptrGetFileInformationByHandleEx + = reinterpret_cast<decltype(&GetFileInformationByHandleEx)>( + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "GetFileInformationByHandleEx")); + if (!ptrGetFileInformationByHandleEx) { + return false; + } + + HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) { + return false; + } + + // Check that it's a pipe: + if (GetFileType(h) != FILE_TYPE_PIPE) { + return false; + } + + // POD type is binary compatible with FILE_NAME_INFO from WinBase.h + // It have the same alignment and used to avoid UB in caller code + struct MY_FILE_NAME_INFO { + DWORD FileNameLength; + WCHAR FileName[MAX_PATH]; + }; + + auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>( + new (std::nothrow) MY_FILE_NAME_INFO()); + if (!pNameInfo) { + return false; + } + + // Check pipe name is template of + // {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX + if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(), + sizeof(MY_FILE_NAME_INFO))) { + return false; + } + std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR)); + if ((name.find(L"msys-") == std::wstring::npos + && name.find(L"cygwin-") == std::wstring::npos) + || name.find(L"-pty") == std::wstring::npos) { + return false; + } + + return true; + } + +#endif + + inline bool isTerminal(const std::streambuf *osbuf) noexcept + { + using std::cerr; + using std::clog; + using std::cout; +#if defined(OS_LINUX) || defined(OS_MAC) + if (osbuf == cout.rdbuf()) { + static const bool cout_term = isatty(fileno(stdout)) != 0; + return cout_term; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_term = isatty(fileno(stderr)) != 0; + return cerr_term; + } +#elif defined(OS_WIN) + if (osbuf == cout.rdbuf()) { + static const bool cout_term + = (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout))); + return cout_term; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_term + = (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr))); + return cerr_term; + } +#endif + return false; + } + + template <typename T> + using enableStd = typename std::enable_if< + std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value + || std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value + || std::is_same<T, rang::bgB>::value, + std::ostream &>::type; + + +#ifdef OS_WIN + + struct SGR { // Select Graphic Rendition parameters for Windows console + BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit + BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit + BYTE bold; // emulated as FOREGROUND_INTENSITY bit + BYTE underline; // emulated as BACKGROUND_INTENSITY bit + BOOLEAN inverse; // swap foreground/bold & background/underline + BOOLEAN conceal; // set foreground/bold to background/underline + }; + + enum class AttrColor : BYTE { // Color attributes for console screen buffer + black = 0, + red = 4, + green = 2, + yellow = 6, + blue = 1, + magenta = 5, + cyan = 3, + gray = 7 + }; + + inline HANDLE getConsoleHandle(const std::streambuf *osbuf) noexcept + { + if (osbuf == std::cout.rdbuf()) { + static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + return hStdout; + } else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) { + static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); + return hStderr; + } + return INVALID_HANDLE_VALUE; + } + + inline bool setWinTermAnsiColors(const std::streambuf *osbuf) noexcept + { + HANDLE h = getConsoleHandle(osbuf); + if (h == INVALID_HANDLE_VALUE) { + return false; + } + DWORD dwMode = 0; + if (!GetConsoleMode(h, &dwMode)) { + return false; + } + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(h, dwMode)) { + return false; + } + return true; + } + + inline bool supportsAnsi(const std::streambuf *osbuf) noexcept + { + using std::cerr; + using std::clog; + using std::cout; + if (osbuf == cout.rdbuf()) { + static const bool cout_ansi + = (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf)); + return cout_ansi; + } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) { + static const bool cerr_ansi + = (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf)); + return cerr_ansi; + } + return false; + } + + inline const SGR &defaultState() noexcept + { + static const SGR defaultSgr = []() -> SGR { + CONSOLE_SCREEN_BUFFER_INFO info; + WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), + &info) + || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), + &info)) { + attrib = info.wAttributes; + } + SGR sgr = { 0, 0, 0, 0, FALSE, FALSE }; + sgr.fgColor = attrib & 0x0F; + sgr.bgColor = (attrib & 0xF0) >> 4; + return sgr; + }(); + return defaultSgr; + } + + inline BYTE ansi2attr(BYTE rgb) noexcept + { + static const AttrColor rev[8] + = { AttrColor::black, AttrColor::red, AttrColor::green, + AttrColor::yellow, AttrColor::blue, AttrColor::magenta, + AttrColor::cyan, AttrColor::gray }; + return static_cast<BYTE>(rev[rgb]); + } + + inline void setWinSGR(rang::bg col, SGR &state) noexcept + { + if (col != rang::bg::reset) { + state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40); + } else { + state.bgColor = defaultState().bgColor; + } + } + + inline void setWinSGR(rang::fg col, SGR &state) noexcept + { + if (col != rang::fg::reset) { + state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30); + } else { + state.fgColor = defaultState().fgColor; + } + } + + inline void setWinSGR(rang::bgB col, SGR &state) noexcept + { + state.bgColor = (BACKGROUND_INTENSITY >> 4) + | ansi2attr(static_cast<BYTE>(col) - 100); + } + + inline void setWinSGR(rang::fgB col, SGR &state) noexcept + { + state.fgColor + = FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90); + } + + inline void setWinSGR(rang::style style, SGR &state) noexcept + { + switch (style) { + case rang::style::reset: state = defaultState(); break; + case rang::style::bold: state.bold = FOREGROUND_INTENSITY; break; + case rang::style::underline: + case rang::style::blink: + state.underline = BACKGROUND_INTENSITY; + break; + case rang::style::reversed: state.inverse = TRUE; break; + case rang::style::conceal: state.conceal = TRUE; break; + default: break; + } + } + + inline SGR ¤t_state() noexcept + { + static SGR state = defaultState(); + return state; + } + + inline WORD SGR2Attr(const SGR &state) noexcept + { + WORD attrib = 0; + if (state.conceal) { + if (state.inverse) { + attrib = (state.fgColor << 4) | state.fgColor; + if (state.bold) + attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } else { + attrib = (state.bgColor << 4) | state.bgColor; + if (state.underline) + attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + } else if (state.inverse) { + attrib = (state.fgColor << 4) | state.bgColor; + if (state.bold) attrib |= BACKGROUND_INTENSITY; + if (state.underline) attrib |= FOREGROUND_INTENSITY; + } else { + attrib = state.fgColor | (state.bgColor << 4) | state.bold + | state.underline; + } + return attrib; + } + + template <typename T> + inline void setWinColorAnsi(std::ostream &os, T const value) + { + os << "\033[" << static_cast<int>(value) << "m"; + } + + template <typename T> + inline void setWinColorNative(std::ostream &os, T const value) + { + const HANDLE h = getConsoleHandle(os.rdbuf()); + if (h != INVALID_HANDLE_VALUE) { + setWinSGR(value, current_state()); + // Out all buffered text to console with previous settings: + os.flush(); + SetConsoleTextAttribute(h, SGR2Attr(current_state())); + } + } + + template <typename T> + inline enableStd<T> setColor(std::ostream &os, T const value) + { + if (winTermMode() == winTerm::Auto) { + if (supportsAnsi(os.rdbuf())) { + setWinColorAnsi(os, value); + } else { + setWinColorNative(os, value); + } + } else if (winTermMode() == winTerm::Ansi) { + setWinColorAnsi(os, value); + } else { + setWinColorNative(os, value); + } + return os; + } +#else + template <typename T> + inline enableStd<T> setColor(std::ostream &os, T const value) + { + return os << "\033[" << static_cast<int>(value) << "m"; + } +#endif +} // namespace rang_implementation + +template <typename T> +inline rang_implementation::enableStd<T> operator<<(std::ostream &os, + const T value) +{ + const control option = rang_implementation::controlMode(); + switch (option) { + case control::Auto: + return rang_implementation::supportsColor() + && rang_implementation::isTerminal(os.rdbuf()) + ? rang_implementation::setColor(os, value) + : os; + case control::Force: return rang_implementation::setColor(os, value); + default: return os; + } +} + +inline void setWinTermMode(const rang::winTerm value) noexcept +{ + rang_implementation::winTermMode() = value; +} + +inline void setControlMode(const control value) noexcept +{ + rang_implementation::controlMode() = value; +} + +} // namespace rang + +#undef OS_LINUX +#undef OS_WIN +#undef OS_MAC + +#endif /* ifndef RANG_DOT_HPP */ |