Tulip 5.7.1
Large graphs analysis and drawing
Loading...
Searching...
No Matches
ConsoleUtils.h
1/*
2 *
3 * This file is part of Tulip (https://tulip.labri.fr)
4 *
5 * Authors: David Auber and the Tulip development Team
6 * from LaBRI, University of Bordeaux
7 *
8 * Tulip is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * Tulip is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 *
18 */
19///@cond DOXYGEN_HIDDEN
20
21#ifndef CONSOLE_UTILS
22#define CONSOLE_UTILS
23
24#include <iostream>
25#include <string>
26#include <cstdlib>
27#include <cstdio>
28#include <map>
29
30#ifdef WIN32
31#include <windows.h>
32#include <io.h>
33#endif
34
35#if defined(__unix__) || defined(__APPLE__)
36
37#include <termios.h>
38#include <sys/ioctl.h>
39#include <unistd.h>
40
41static struct termios stored_settings;
42
43static void setConsoleSettingsForAnsiRequest(void) {
44 struct termios new_settings;
45 tcgetattr(0, &stored_settings);
46 new_settings = stored_settings;
47 new_settings.c_lflag &= ~ICANON;
48 new_settings.c_lflag &= ~ECHO;
49 tcsetattr(0, TCSANOW, &new_settings);
50}
51
52static void resetConsoleSettings(void) {
53 tcsetattr(0, TCSANOW, &stored_settings);
54}
55
56static bool processInForeground(void) {
57 pid_t fg = tcgetpgrp(STDIN_FILENO);
58 return fg == getpgrp();
59}
60
61static std::pair<int, int> getConsoleCursorPosition() {
62 int row = 0, col = 0;
63
64 if (isatty(fileno(stdin)) && processInForeground()) {
65 setConsoleSettingsForAnsiRequest();
66 fprintf(stdout, "\x1b[6n");
67
68 if (fscanf(stdin, "\x1b[%d;%dR", &row, &col)) {
69 }
70
71 resetConsoleSettings();
72 }
73
74 return std::make_pair(row, col);
75}
76
77static std::pair<int, int> getConsoleSize() {
78 int rows = -1, cols = -1;
79 struct winsize ws;
80
81 if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
82 rows = ws.ws_row;
83 cols = ws.ws_col;
84 }
85
86 return std::make_pair(rows, cols);
87}
88
89#elif defined(WIN32)
90
91inline std::pair<int, int> getConsoleCursorPosition() {
92 int row = 0, col = 0;
93 CONSOLE_SCREEN_BUFFER_INFO SBInfo;
94
95 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &SBInfo)) {
96 row = SBInfo.dwCursorPosition.Y;
97 col = SBInfo.dwCursorPosition.X;
98 }
99
100 return std::make_pair(row, col);
101}
102
103inline std::pair<int, int> getConsoleSize() {
104 int rows = 200, cols = 200;
105 CONSOLE_SCREEN_BUFFER_INFO SBInfo;
106
107 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &SBInfo)) {
108 rows = SBInfo.dwSize.Y;
109 cols = SBInfo.dwSize.X - 1;
110 }
111
112 return std::make_pair(rows, cols);
113}
114
115#endif
116
117enum TextEffect { NORMAL = 1, BOLD = 2, UNDERLINED = 4, BLINK = 8 };
118
119enum TextColor {
120 BLACK,
121 RED,
122 GREEN,
123 YELLOW,
124 BLUE,
125 MAGENTA,
126 CYAN,
127 LIGHT_GRAY,
128 DARK_GRAY,
129 LIGHT_RED,
130 LIGHT_GREEN,
131 LIGHT_YELLOW,
132 LIGHT_BLUE,
133 LIGHT_MAGENTA,
134 LIGHT_CYAN,
135 WHITE
136};
137
138class TextFgColorSetup {
139
140public:
141 TextFgColorSetup(TextColor color) : color(color) {}
142
143 TextColor color;
144};
145
146class TextBgColorSetup {
147
148public:
149 TextBgColorSetup(TextColor color) : color(color) {}
150
151 TextColor color;
152};
153
154class TextEffectSetup {
155
156public:
157 TextEffectSetup(int effect) : effect(effect) {}
158
159 int effect;
160};
161
162static std::map<TextColor, std::string> initAnsiFgColors() {
163 const char *colorterm = getenv("COLORTERM");
164 bool rxvt = colorterm && std::string(colorterm).find("rxvt") != std::string::npos;
165 std::map<TextColor, std::string> ret;
166 ret[BLACK] = "30";
167 ret[RED] = "31";
168 ret[GREEN] = "32";
169 ret[YELLOW] = "33";
170 ret[BLUE] = "34";
171 ret[MAGENTA] = "35";
172 ret[CYAN] = "36";
173 ret[LIGHT_GRAY] = "37";
174 ret[DARK_GRAY] = rxvt ? ret[BLACK] : "90";
175 ret[LIGHT_RED] = rxvt ? ret[RED] : "91";
176 ret[LIGHT_GREEN] = rxvt ? ret[GREEN] : "92";
177 ret[LIGHT_YELLOW] = rxvt ? ret[YELLOW] : "93";
178 ret[LIGHT_BLUE] = rxvt ? ret[BLUE] : "94";
179 ret[LIGHT_MAGENTA] = rxvt ? ret[MAGENTA] : "95";
180 ret[LIGHT_CYAN] = rxvt ? ret[CYAN] : "96";
181 ret[WHITE] = rxvt ? ret[LIGHT_GRAY] : "97";
182 return ret;
183}
184
185static std::map<TextColor, std::string> initAnsiBgColors() {
186 const char *colorterm = getenv("COLORTERM");
187 bool rxvt = colorterm && std::string(colorterm).find("rxvt") != std::string::npos;
188 std::map<TextColor, std::string> ret;
189 ret[BLACK] = "40";
190 ret[RED] = "41";
191 ret[GREEN] = "42";
192 ret[YELLOW] = "43";
193 ret[BLUE] = "44";
194 ret[MAGENTA] = "45";
195 ret[CYAN] = "46";
196 ret[LIGHT_GRAY] = "47";
197 ret[DARK_GRAY] = rxvt ? ret[BLACK] : "100";
198 ret[LIGHT_RED] = rxvt ? ret[RED] : "101";
199 ret[LIGHT_GREEN] = rxvt ? ret[GREEN] : "102";
200 ret[LIGHT_YELLOW] = rxvt ? ret[YELLOW] : "103";
201 ret[LIGHT_BLUE] = rxvt ? ret[BLUE] : "104";
202 ret[LIGHT_MAGENTA] = rxvt ? ret[MAGENTA] : "105";
203 ret[LIGHT_CYAN] = rxvt ? ret[CYAN] : "106";
204 ret[WHITE] = rxvt ? ret[LIGHT_GRAY] : "107";
205 return ret;
206}
207
208static std::map<TextColor, std::string> ansiFgColors = initAnsiFgColors();
209static std::map<TextColor, std::string> ansiBgColors = initAnsiBgColors();
210
211#ifdef WIN32
212static std::map<TextColor, int> initCrtFgColors() {
213 std::map<TextColor, int> ret;
214 ret[BLACK] = 0;
215 ret[RED] = FOREGROUND_RED;
216 ret[GREEN] = FOREGROUND_GREEN;
217 ret[YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN;
218 ret[BLUE] = FOREGROUND_BLUE;
219 ret[MAGENTA] = FOREGROUND_BLUE | FOREGROUND_RED;
220 ret[CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE;
221 ret[LIGHT_GRAY] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
222 ret[DARK_GRAY] = FOREGROUND_INTENSITY;
223 ret[LIGHT_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY;
224 ret[LIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
225 ret[LIGHT_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
226 ret[LIGHT_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
227 ret[LIGHT_MAGENTA] = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
228 ret[LIGHT_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
229 ret[WHITE] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
230 return ret;
231}
232
233static std::map<TextColor, int> initCrtBgColors() {
234 std::map<TextColor, int> ret;
235 ret[BLACK] = 0;
236 ret[RED] = BACKGROUND_RED;
237 ret[GREEN] = BACKGROUND_GREEN;
238 ret[YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN;
239 ret[BLUE] = BACKGROUND_BLUE;
240 ret[MAGENTA] = BACKGROUND_BLUE | BACKGROUND_RED;
241 ret[CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE;
242 ret[LIGHT_GRAY] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
243 ret[DARK_GRAY] = BACKGROUND_INTENSITY;
244 ret[LIGHT_RED] = BACKGROUND_RED | BACKGROUND_INTENSITY;
245 ret[LIGHT_GREEN] = BACKGROUND_GREEN | BACKGROUND_INTENSITY;
246 ret[LIGHT_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
247 ret[LIGHT_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
248 ret[LIGHT_MAGENTA] = BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY;
249 ret[LIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
250 ret[WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
251 return ret;
252}
253
254static WORD getCurrentBgColor() {
255 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
256 CONSOLE_SCREEN_BUFFER_INFO info;
257 GetConsoleScreenBufferInfo(hStdout, &info);
258 return info.wAttributes &
259 (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
260}
261
262static WORD getCurrentFgColor() {
263 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
264 CONSOLE_SCREEN_BUFFER_INFO info;
265 GetConsoleScreenBufferInfo(hStdout, &info);
266 return info.wAttributes &
267 (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
268}
269static std::map<TextColor, int> crtFgColors = initCrtFgColors();
270static std::map<TextColor, int> crtBgColors = initCrtBgColors();
271#endif
272
273static void escapeAnsiCode(std::ostream &os, const std::string &ansiCode,
274 const std::string &endEscape = "m") {
275 static const char *stdOutNoAnsiEscapes = getenv("STDOUT_NO_ANSI_ESCAPES");
276 static const char *stdErrNoAnsiEscapes = getenv("STDERR_NO_ANSI_ESCAPES");
277#ifndef WIN32
278
279 if ((&os == &std::cout && !stdOutNoAnsiEscapes && isatty(fileno(stdout))) ||
280 (&os == &std::cerr && !stdErrNoAnsiEscapes && isatty(fileno(stderr))))
281#else
282 if ((&os == &std::cout && !stdOutNoAnsiEscapes) || (&os == &std::cerr && !stdErrNoAnsiEscapes))
283#endif
284 os << "\x1b[" << ansiCode << endEscape;
285}
286
287/*
288 This set of stream manipulation operators allow to produce
289 a colored console output. Except for windows console application (based on cmd.exe),
290 the colors are activated through ANSI escape sequences. The writing of these sequences
291 can be disabled by setting the STDOUT_N0_ANSI_ESCAPES and STDERR_N0_ANSI_ESCAPES
292 environment variables (for instance, when working with a terminal who does not understand
293 these sequences).
294
295 Below is an example on how to use it :
296
297 // Output a bold text colored in red on a white background
298 std::cout << setTextEffect(BOLD) << setTextColor(RED) << setTextBackgroundColor(WHITE) << "Hello
299 World!" << std::endl;
300
301 // Reset the text colors to the default ones
302 std::cout << defaultTextColor;
303
304 // You can also use some predefined color schemes (only affects text color) : black, white, red,
305 green,
306 // blue, yellow, white, cyan, magenta, darkGray, lightGray, lightRed, lightGreen, ...
307 std::cout << red << "Hello " << blue << World !!" << defaulTextColor << std::endl;
308
309 */
310
311inline std::ostream &defaultTextColor(std::ostream &s) {
312 if (&s == &std::cout || &s == &std::cerr) {
313#ifndef WIN32
314 escapeAnsiCode(s, "0");
315 escapeAnsiCode(s, "39");
316 escapeAnsiCode(s, "49");
317#else
318 // on windows, if the TERM environment variable is not defined
319 // it means that the shell used is cmd.exe, whose does not understand ANSI escape sequences
320 static const char *term = getenv("TERM");
321
322 if (term && std::string(term) != "cygwin") {
323 escapeAnsiCode(s, "0");
324 escapeAnsiCode(s, "39");
325 escapeAnsiCode(s, "49");
326 } else {
327 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
328 SetConsoleTextAttribute(hStdout, crtBgColors[BLACK] | crtFgColors[WHITE]);
329 }
330
331#endif
332 }
333
334 return s;
335}
336
337inline std::ostream &operator<<(std::ostream &s, const TextFgColorSetup &tgfcs) {
338 if (&s == &std::cout || &s == &std::cerr) {
339#ifndef WIN32
340 escapeAnsiCode(s, ansiFgColors[tgfcs.color]);
341#else
342 // on windows, if the TERM environment variable is not defined
343 // it means that the shell used is cmd.exe, whose does not understand ANSI escape sequences
344 static const char *term = getenv("TERM");
345
346 if (term && std::string(term) != "cygwin") {
347 escapeAnsiCode(s, ansiFgColors[tgfcs.color]);
348 } else {
349 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
350 SetConsoleTextAttribute(hStdout, crtFgColors[tgfcs.color] | getCurrentBgColor());
351 }
352
353#endif
354 }
355
356 return s;
357}
358
359inline std::ostream &operator<<(std::ostream &s, const TextBgColorSetup &tbgcs) {
360 if (&s == &std::cout || &s == &std::cerr) {
361#ifndef WIN32
362 escapeAnsiCode(s, ansiBgColors[tbgcs.color]);
363#else
364 // on windows, if the TERM environment variable is not defined
365 // it means that the shell used is cmd.exe, whose does not understand ANSI escape sequences
366 static const char *term = getenv("TERM");
367
368 if (term && std::string(term) != "cygwin") {
369 escapeAnsiCode(s, ansiBgColors[tbgcs.color]);
370 } else {
371 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
372 SetConsoleTextAttribute(hStdout, getCurrentFgColor() | crtBgColors[tbgcs.color]);
373 }
374
375#endif
376 }
377
378 return s;
379}
380
381inline void setEffects(std::ostream &s, const int &effect) {
382 if (effect & NORMAL)
383 escapeAnsiCode(s, "0");
384
385 if (effect & BOLD)
386 escapeAnsiCode(s, "1");
387
388 if (effect & UNDERLINED)
389 escapeAnsiCode(s, "4");
390
391 if (effect & BLINK)
392 escapeAnsiCode(s, "5");
393}
394
395inline std::ostream &operator<<(std::ostream &s, const TextEffectSetup &tes) {
396 if (&s == &std::cout || &s == &std::cerr) {
397#ifndef WIN32
398 setEffects(s, tes.effect);
399#else
400 static const char *term = getenv("TERM");
401
402 if (term && std::string(term) != "cygwin") {
403 setEffects(s, tes.effect);
404 }
405
406#endif
407 }
408
409 return s;
410}
411
412inline TextBgColorSetup setTextBackgroundColor(TextColor color) {
413 return TextBgColorSetup(color);
414}
415
416inline TextFgColorSetup setTextColor(TextColor color) {
417 return TextFgColorSetup(color);
418}
419
420inline TextEffectSetup setTextEffect(int effect) {
421 return TextEffectSetup(effect);
422}
423
424inline std::ostream &bold(std::ostream &s) {
425 return s << setTextEffect(BOLD);
426}
427
428inline std::ostream &underlined(std::ostream &s) {
429 return s << setTextEffect(UNDERLINED);
430}
431
432inline std::ostream &red(std::ostream &s) {
433 return s << setTextColor(RED);
434}
435
436inline std::ostream &green(std::ostream &s) {
437 return s << setTextColor(GREEN);
438}
439
440inline std::ostream &blue(std::ostream &s) {
441 return s << setTextColor(BLUE);
442}
443
444inline std::ostream &yellow(std::ostream &s) {
445 return s << setTextColor(YELLOW);
446}
447
448inline std::ostream &white(std::ostream &s) {
449 return s << setTextColor(WHITE);
450}
451
452inline std::ostream &black(std::ostream &s) {
453 return s << setTextColor(BLACK);
454}
455
456inline std::ostream &magenta(std::ostream &s) {
457 return s << setTextColor(MAGENTA);
458}
459
460inline std::ostream &cyan(std::ostream &s) {
461 return s << setTextColor(CYAN);
462}
463
464inline std::ostream &lightRed(std::ostream &s) {
465 return s << setTextColor(LIGHT_RED);
466}
467
468inline std::ostream &lightGreen(std::ostream &s) {
469 return s << setTextColor(LIGHT_GREEN);
470}
471
472inline std::ostream &lightBlue(std::ostream &s) {
473 return s << setTextColor(LIGHT_BLUE);
474}
475
476inline std::ostream &lightYellow(std::ostream &s) {
477 return s << setTextColor(LIGHT_YELLOW);
478}
479
480inline std::ostream &lightGray(std::ostream &s) {
481 return s << setTextColor(LIGHT_GRAY);
482}
483
484inline std::ostream &darkGray(std::ostream &s) {
485 return s << setTextColor(DARK_GRAY);
486}
487
488inline std::ostream &lightMagenta(std::ostream &s) {
489 return s << setTextColor(LIGHT_MAGENTA);
490}
491
492inline std::ostream &lightCyan(std::ostream &s) {
493 return s << setTextColor(LIGHT_CYAN);
494}
495
496// Fill the current line of the terminal with space characters from the
497// current cursor position to the end of the line.
498// The purpose is to have a constant background color on the line.
499inline std::ostream &fillToEndOfLine(std::ostream &s) {
500#ifndef WIN32
501
502 if (processInForeground() && ((&s == &std::cout && isatty(fileno(stdout))) ||
503 (&s == &std::cerr && isatty(fileno(stderr))))) {
504#endif
505 std::pair<int, int> cursorPos = getConsoleCursorPosition();
506 std::pair<int, int> consoleSize = getConsoleSize();
507
508 if ((cursorPos.second + consoleSize.second) != 0) {
509 for (int i = cursorPos.second; i <= consoleSize.second; ++i) {
510 s << " ";
511 }
512 } else {
513 s << std::endl;
514 }
515
516#ifndef WIN32
517 }
518
519#endif
520
521 if ((&s == &std::cout && !isatty(fileno(stdout))) ||
522 (&s == &std::cerr && !isatty(fileno(stderr)))) {
523 s << std::endl;
524 }
525
526#ifndef WIN32
527
528 if (!processInForeground()) {
529 s << defaultTextColor << std::endl;
530 }
531
532#endif
533
534 return s;
535}
536
537#endif
538///@endcond
std::ostream & operator<<(std::ostream &os, const Array< T, N > &array)
operator << stream operator to easily print an array, or save it to a file.