NeKernel dev
Loading...
Searching...
No Matches
rang.h
Go to the documentation of this file.
1#ifndef RANG_DOT_HPP
2#define RANG_DOT_HPP
3
4#if defined(__unix__) || defined(__unix) || defined(__linux__)
5#define OS_LINUX
6#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
7#define OS_WIN
8#elif defined(__APPLE__) || defined(__MACH__)
9#define OS_MAC
10#else
11#error Unknown Platform
12#endif
13
14#if defined(OS_LINUX) || defined(OS_MAC)
15#include <unistd.h>
16
17#elif defined(OS_WIN)
18
19#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
20#error \
21 "Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA"
22#elif !defined(_WIN32_WINNT)
23#define _WIN32_WINNT _WIN32_WINNT_VISTA
24#endif
25
26#include <io.h>
27#include <windows.h>
28#include <memory>
29
30// Only defined in windows 10 onwards, redefining in lower windows since it
31// doesn't gets used in lower versions
32// https://docs.microsoft.com/en-us/windows/console/getconsolemode
33#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
34#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
35#endif
36
37#endif
38
39#include <algorithm>
40#include <atomic>
41#include <cstdlib>
42#include <cstring>
43#include <iostream>
44
45namespace rang {
46
47/* For better compability with most of terminals do not use any style settings
48 * except of reset, bold and reversed.
49 * Note that on Windows terminals bold style is same as fgB color.
50 */
51enum class style {
52 reset = 0,
53 bold = 1,
54 dim = 2,
55 italic = 3,
57 blink = 5,
58 rblink = 6,
62};
63
64enum class fg {
65 black = 30,
66 red = 31,
67 green = 32,
68 yellow = 33,
69 blue = 34,
70 magenta = 35,
71 cyan = 36,
72 gray = 37,
73 reset = 39
74};
75
76enum class bg {
77 black = 40,
78 red = 41,
79 green = 42,
80 yellow = 43,
81 blue = 44,
82 magenta = 45,
83 cyan = 46,
84 gray = 47,
85 reset = 49
86};
87
88enum class fgB {
89 black = 90,
90 red = 91,
91 green = 92,
92 yellow = 93,
93 blue = 94,
94 magenta = 95,
95 cyan = 96,
96 gray = 97
97};
98
99enum class bgB {
100 black = 100,
101 red = 101,
102 green = 102,
103 yellow = 103,
104 blue = 104,
105 magenta = 105,
106 cyan = 106,
107 gray = 107
108};
109
110enum class control { // Behaviour of rang function calls
111 Off = 0, // toggle off rang style/color calls
112 Auto = 1, // (Default) autodect terminal and colorize if needed
113 Force = 2 // force ansi color output to non terminal streams
114};
115// Use rang::setControlMode to set rang control mode
116
117enum class winTerm { // Windows Terminal Mode
118 Auto = 0, // (Default) automatically detects wheter Ansi or Native API
119 Ansi = 1, // Force use Ansi API
120 Native = 2 // Force use Native API
121};
122// Use rang::setWinTermMode to explicitly set terminal API for Windows
123// Calling rang::setWinTermMode have no effect on other OS
124
126
127 inline std::atomic<control>& controlMode() noexcept {
128 static std::atomic<control> value(control::Auto);
129 return value;
130 }
131
132 inline std::atomic<winTerm>& winTermMode() noexcept {
133 static std::atomic<winTerm> termMode(winTerm::Auto);
134 return termMode;
135 }
136
137 inline bool supportsColor() noexcept {
138#if defined(OS_LINUX) || defined(OS_MAC)
139
140 static const bool result = [] {
141 const char* Terms[] = {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
142 "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
143
144 const char* env_p = std::getenv("TERM");
145 if (env_p == nullptr) {
146 return false;
147 }
148 return std::any_of(std::begin(Terms), std::end(Terms),
149 [&](const char* term) { return std::strstr(env_p, term) != nullptr; });
150 }();
151
152#elif defined(OS_WIN)
153 // All windows versions support colors through native console methods
154 static constexpr bool result = true;
155#endif
156 return result;
157 }
158
159#ifdef OS_WIN
160
161 inline bool isMsysPty(int fd) noexcept {
162 // Dynamic load for binary compability with old Windows
163 const auto ptrGetFileInformationByHandleEx =
164 reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(
165 GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetFileInformationByHandleEx"));
166 if (!ptrGetFileInformationByHandleEx) {
167 return false;
168 }
169
170 HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
171 if (h == INVALID_HANDLE_VALUE) {
172 return false;
173 }
174
175 // Check that it's a pipe:
176 if (GetFileType(h) != FILE_TYPE_PIPE) {
177 return false;
178 }
179
180 // POD type is binary compatible with FILE_NAME_INFO from WinBase.h
181 // It have the same alignment and used to avoid UB in caller code
182 struct MY_FILE_NAME_INFO {
183 DWORD FileNameLength;
184 WCHAR FileName[MAX_PATH];
185 };
186
187 auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(new (std::nothrow) MY_FILE_NAME_INFO());
188 if (!pNameInfo) {
189 return false;
190 }
191
192 // Check pipe name is template of
193 // {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
194 if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
195 sizeof(MY_FILE_NAME_INFO))) {
196 return false;
197 }
198 std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR));
199 if ((name.find(L"msys-") == std::wstring::npos &&
200 name.find(L"cygwin-") == std::wstring::npos) ||
201 name.find(L"-pty") == std::wstring::npos) {
202 return false;
203 }
204
205 return true;
206 }
207
208#endif
209
210 inline bool isTerminal(const std::streambuf* osbuf) noexcept {
211 using std::cerr;
212 using std::clog;
213 using std::cout;
214#if defined(OS_LINUX) || defined(OS_MAC)
215 if (osbuf == cout.rdbuf()) {
216 static const bool cout_term = isatty(fileno(stdout)) != 0;
217 return cout_term;
218 } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
219 static const bool cerr_term = isatty(fileno(stderr)) != 0;
220 return cerr_term;
221 }
222#elif defined(OS_WIN)
223 if (osbuf == cout.rdbuf()) {
224 static const bool cout_term = (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout)));
225 return cout_term;
226 } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
227 static const bool cerr_term = (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr)));
228 return cerr_term;
229 }
230#endif
231 return false;
232 }
233
234 template <typename T>
235 using enableStd = typename std::enable_if<
236 std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value ||
237 std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value ||
238 std::is_same<T, rang::bgB>::value,
239 std::ostream&>::type;
240
241#ifdef OS_WIN
242
243 struct SGR { // Select Graphic Rendition parameters for Windows console
244 BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit
245 BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit
246 BYTE bold; // emulated as FOREGROUND_INTENSITY bit
247 BYTE underline; // emulated as BACKGROUND_INTENSITY bit
248 BOOLEAN inverse; // swap foreground/bold & background/underline
249 BOOLEAN conceal; // set foreground/bold to background/underline
250 };
251
252 enum class AttrColor : BYTE { // Color attributes for console screen buffer
253 black = 0,
254 red = 4,
255 green = 2,
256 yellow = 6,
257 blue = 1,
258 magenta = 5,
259 cyan = 3,
260 gray = 7
261 };
262
263 inline HANDLE getConsoleHandle(const std::streambuf* osbuf) noexcept {
264 if (osbuf == std::cout.rdbuf()) {
265 static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
266 return hStdout;
267 } else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {
268 static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
269 return hStderr;
270 }
271 return INVALID_HANDLE_VALUE;
272 }
273
274 inline bool setWinTermAnsiColors(const std::streambuf* osbuf) noexcept {
275 HANDLE h = getConsoleHandle(osbuf);
276 if (h == INVALID_HANDLE_VALUE) {
277 return false;
278 }
279 DWORD dwMode = 0;
280 if (!GetConsoleMode(h, &dwMode)) {
281 return false;
282 }
283 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
284 if (!SetConsoleMode(h, dwMode)) {
285 return false;
286 }
287 return true;
288 }
289
290 inline bool supportsAnsi(const std::streambuf* osbuf) noexcept {
291 using std::cerr;
292 using std::clog;
293 using std::cout;
294 if (osbuf == cout.rdbuf()) {
295 static const bool cout_ansi = (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf));
296 return cout_ansi;
297 } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
298 static const bool cerr_ansi = (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf));
299 return cerr_ansi;
300 }
301 return false;
302 }
303
304 inline const SGR& defaultState() noexcept {
305 static const SGR defaultSgr = []() -> SGR {
306 CONSOLE_SCREEN_BUFFER_INFO info;
307 WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
308 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) ||
309 GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info)) {
310 attrib = info.wAttributes;
311 }
312 SGR sgr = {0, 0, 0, 0, FALSE, FALSE};
313 sgr.fgColor = attrib & 0x0F;
314 sgr.bgColor = (attrib & 0xF0) >> 4;
315 return sgr;
316 }();
317 return defaultSgr;
318 }
319
320 inline BYTE ansi2attr(BYTE rgb) noexcept {
321 static const AttrColor rev[8] = {AttrColor::black, AttrColor::red, AttrColor::green,
322 AttrColor::yellow, AttrColor::blue, AttrColor::magenta,
323 AttrColor::cyan, AttrColor::gray};
324 return static_cast<BYTE>(rev[rgb]);
325 }
326
327 inline void setWinSGR(rang::bg col, SGR& state) noexcept {
328 if (col != rang::bg::reset) {
329 state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40);
330 } else {
331 state.bgColor = defaultState().bgColor;
332 }
333 }
334
335 inline void setWinSGR(rang::fg col, SGR& state) noexcept {
336 if (col != rang::fg::reset) {
337 state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30);
338 } else {
339 state.fgColor = defaultState().fgColor;
340 }
341 }
342
343 inline void setWinSGR(rang::bgB col, SGR& state) noexcept {
344 state.bgColor = (BACKGROUND_INTENSITY >> 4) | ansi2attr(static_cast<BYTE>(col) - 100);
345 }
346
347 inline void setWinSGR(rang::fgB col, SGR& state) noexcept {
348 state.fgColor = FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90);
349 }
350
351 inline void setWinSGR(rang::style style, SGR& state) noexcept {
352 switch (style) {
354 state = defaultState();
355 break;
357 state.bold = FOREGROUND_INTENSITY;
358 break;
361 state.underline = BACKGROUND_INTENSITY;
362 break;
364 state.inverse = TRUE;
365 break;
367 state.conceal = TRUE;
368 break;
369 default:
370 break;
371 }
372 }
373
374 inline SGR& current_state() noexcept {
375 static SGR state = defaultState();
376 return state;
377 }
378
379 inline WORD SGR2Attr(const SGR& state) noexcept {
380 WORD attrib = 0;
381 if (state.conceal) {
382 if (state.inverse) {
383 attrib = (state.fgColor << 4) | state.fgColor;
384 if (state.bold) attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
385 } else {
386 attrib = (state.bgColor << 4) | state.bgColor;
387 if (state.underline) attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
388 }
389 } else if (state.inverse) {
390 attrib = (state.fgColor << 4) | state.bgColor;
391 if (state.bold) attrib |= BACKGROUND_INTENSITY;
392 if (state.underline) attrib |= FOREGROUND_INTENSITY;
393 } else {
394 attrib = state.fgColor | (state.bgColor << 4) | state.bold | state.underline;
395 }
396 return attrib;
397 }
398
399 template <typename T>
400 inline void setWinColorAnsi(std::ostream& os, T const value) {
401 os << "\033[" << static_cast<int>(value) << "m";
402 }
403
404 template <typename T>
405 inline void setWinColorNative(std::ostream& os, T const value) {
406 const HANDLE h = getConsoleHandle(os.rdbuf());
407 if (h != INVALID_HANDLE_VALUE) {
408 setWinSGR(value, current_state());
409 // Out all buffered text to console with previous settings:
410 os.flush();
411 SetConsoleTextAttribute(h, SGR2Attr(current_state()));
412 }
413 }
414
415 template <typename T>
416 inline enableStd<T> setColor(std::ostream& os, T const value) {
417 if (winTermMode() == winTerm::Auto) {
418 if (supportsAnsi(os.rdbuf())) {
419 setWinColorAnsi(os, value);
420 } else {
421 setWinColorNative(os, value);
422 }
423 } else if (winTermMode() == winTerm::Ansi) {
424 setWinColorAnsi(os, value);
425 } else {
426 setWinColorNative(os, value);
427 }
428 return os;
429 }
430#else
431 template <typename T>
432 inline enableStd<T> setColor(std::ostream& os, T const value) {
433 return os << "\033[" << static_cast<int>(value) << "m";
434 }
435#endif
436} // namespace rang_implementation
437
438template <typename T>
439inline rang_implementation::enableStd<T> operator<<(std::ostream& os, const T value) {
441 switch (option) {
442 case control::Auto:
445 : os;
446 case control::Force:
447 return rang_implementation::setColor(os, value);
448 default:
449 return os;
450 }
451}
452
453inline void setWinTermMode(const rang::winTerm value) noexcept {
455}
456
457inline void setControlMode(const control value) noexcept {
459}
460
461} // namespace rang
462
463#undef OS_LINUX
464#undef OS_WIN
465#undef OS_MAC
466
467#endif /* ifndef RANG_DOT_HPP */
#define TRUE
#define FALSE
@ info
Definition Dialogs.h:92
Definition rang.h:125
std::atomic< winTerm > & winTermMode() noexcept
Definition rang.h:132
bool supportsColor() noexcept
Definition rang.h:137
std::atomic< control > & controlMode() noexcept
Definition rang.h:127
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 enableStd
Definition rang.h:235
enableStd< T > setColor(std::ostream &os, T const value)
Definition rang.h:432
bool isTerminal(const std::streambuf *osbuf) noexcept
Definition rang.h:210
Definition rang.h:45
void setWinTermMode(const rang::winTerm value) noexcept
Definition rang.h:453
fgB
Definition rang.h:88
fg
Definition rang.h:64
@ black
Definition rang.h:65
@ blue
Definition rang.h:69
@ magenta
Definition rang.h:70
@ cyan
Definition rang.h:71
@ reset
Definition rang.h:73
@ green
Definition rang.h:67
@ red
Definition rang.h:66
@ gray
Definition rang.h:72
@ yellow
Definition rang.h:68
bgB
Definition rang.h:99
control
Definition rang.h:110
@ Auto
Definition rang.h:112
@ Force
Definition rang.h:113
@ Off
Definition rang.h:111
bg
Definition rang.h:76
@ reset
Definition rang.h:85
rang_implementation::enableStd< T > operator<<(std::ostream &os, const T value)
Definition rang.h:439
style
Definition rang.h:51
@ italic
Definition rang.h:55
@ rblink
Definition rang.h:58
@ blink
Definition rang.h:57
@ dim
Definition rang.h:54
@ bold
Definition rang.h:53
@ underline
Definition rang.h:56
@ reset
Definition rang.h:52
@ reversed
Definition rang.h:59
@ conceal
Definition rang.h:60
@ crossed
Definition rang.h:61
void setControlMode(const control value) noexcept
Definition rang.h:457
winTerm
Definition rang.h:117
@ Auto
Definition rang.h:118
@ Ansi
Definition rang.h:119
@ Native
Definition rang.h:120