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