terminal.cpp
Go to the documentation of this file.
1 // Encapsulates terminal control (colors, cursor, ...)
2 // Author: Max Schwarz <max.schwarz@uni-bonn.de>
3 
4 #include "terminal.h"
5 
6 #include <termios.h>
7 
8 #include <term.h>
9 #include <curses.h>
10 
11 #include <cstdio>
12 
13 
14 #include <sys/ioctl.h>
15 #include <sys/types.h>
16 
17 #include <boost/lexical_cast.hpp>
18 #include <boost/regex.hpp>
19 #include <boost/tokenizer.hpp>
20 
21 #include <fmt/format.h>
22 
23 namespace rosmon
24 {
25 
27  : m_state(STATE_ESCAPE)
28  , m_fgColor(-1)
29  , m_bgColor(-1)
30  , m_bold(false)
31 {
32 }
33 
34 void Terminal::Parser::parseSetAttributes(const std::string& str)
35 {
36  using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
37 
38  boost::char_separator<char> sep(";");
39  Tokenizer tok(str, sep);
40 
41  for(Tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
42  {
43  errno = 0;
44  auto endptr = const_cast<char*>(it->c_str());
45  int code = strtoul(it->c_str(), &endptr, 10);
46 
47  if(errno != 0 || *endptr != 0)
48  {
49  // Error in specification, break out of here
50  m_fgColor = -1;
51  m_bgColor = -1;
52  return;
53  }
54 
55  if(code == 0)
56  {
57  m_fgColor = -1;
58  m_bgColor = -1;
59  }
60  else if(code >= 30 && code <= 37)
61  m_fgColor = code - 30;
62  else if(code >= 40 && code <= 47)
63  m_bgColor = code - 40;
64  else if(code == 1)
65  m_bold = true;
66  }
67 }
68 
70 {
71  switch(m_state)
72  {
73  case STATE_ESCAPE:
74  if(c == '\033')
76  break;
77  case STATE_TYPE:
78  if(c == '[')
79  {
81  m_buf.clear();
82  }
83  else
85  break;
86  case STATE_CSI:
87  if(c == 'm')
88  {
91  }
92  else
93  {
94  m_buf.push_back(c);
95  if(m_buf.length() >= 16)
97  }
98  break;
99  }
100 }
101 
102 void Terminal::Parser::parse(const std::string& str)
103 {
104  for(char c : str)
105  parse(c);
106 }
107 
109 {
110  if(m_fgColor >= 0 && m_bgColor >= 0)
112  else
113  {
114  term->setStandardColors();
115  if(m_fgColor >= 0)
116  term->setSimpleForeground((SimpleColor)m_fgColor);
117  else if(m_bgColor >= 0)
118  term->setSimpleBackground((SimpleColor)m_fgColor);
119  }
120 }
121 
123  : m_valid(false)
124  , m_256colors(false)
125  , m_truecolor(false)
126 {
127  // Override using environment variable
128  char* overrideMode = getenv("ROSMON_COLOR_MODE");
129  const char* termOverride = nullptr;
130  if(overrideMode)
131  {
132  if(strcasecmp(overrideMode, "truecolor") == 0)
133  {
134  termOverride = "xterm-256color";
135  m_256colors = true;
136  m_truecolor = true;
137  }
138  else if(strcasecmp(overrideMode, "256colors") == 0)
139  {
140  termOverride = "xterm-256color";
141  m_256colors = true;
142  m_truecolor = false;
143  }
144  else if(strcasecmp(overrideMode, "ansi") == 0)
145  {
146  m_256colors = false;
147  m_truecolor = false;
148  }
149  else
150  {
151  fmt::print(stderr, "Warning: Unknown ROSMON_COLOR_MODE value: '{}'\n", overrideMode);
152  }
153  }
154  else
155  {
156  // Detect truecolor-capable terminals
157  if(getenv("KONSOLE_DBUS_SESSION"))
158  {
159  // Sadly, there is no way to determine the Konsole version. Since
160  // any reasonably recent version supports true colors, just assume
161  // true color support
162  m_truecolor = true;
163  m_256colors = true;
164  }
165 
166  char* vte_version = getenv("VTE_VERSION");
167  if(vte_version && boost::lexical_cast<unsigned int>(vte_version) >= 3600)
168  {
169  m_256colors = true;
170  m_truecolor = true;
171  }
172  }
173 
174  int ret;
175  if(setupterm(termOverride, STDOUT_FILENO, &ret) != OK)
176  {
177  fmt::print("Could not setup the terminal. Disabling all colors...\n");
178  return;
179  }
180 
181  m_valid = true;
182 
183  if(!m_256colors && !overrideMode)
184  {
185  // Detect 256 color terminals
186  int num_colors = tigetnum("colors");
187  m_256colors = num_colors >= 256;
188  }
189 
190  {
191  const char* bgColor = tigetstr("setab");
192  if(bgColor && bgColor != (char*)-1)
193  m_bgColorStr = bgColor;
194  else
195  fmt::print("Your terminal does not support ANSI background!\n");
196  }
197  {
198  const char* fgColor = tigetstr("setaf");
199  if(fgColor && fgColor != (char*)-1)
200  m_fgColorStr = fgColor;
201  else
202  fmt::print("Your terminal does not support ANSI foreground!\n");
203  }
204 
205  m_opStr = tigetstr("op");
206  m_sgr0Str = tigetstr("sgr0");
207  m_elStr = tigetstr("el");
208  m_upStr = tigetstr("cuu");
209 
210  m_boldStr = tigetstr("bold");
211 
212  // Map function keys
213  for(int i = 0; i < 12; ++i)
214  {
215  char* code = tigetstr(fmt::format("kf{}", i+1).c_str());
216 
217  // Who comes up with these return codes?
218  if(code && code != reinterpret_cast<char*>(-1))
219  m_specialKeys[code] = static_cast<SpecialKey>(SK_F1 + i);
220  }
221 }
222 
224 {
225  return m_256colors;
226 }
227 
229 {
230  if(!m_valid)
231  return;
232 
233  putp(tigetstr("civis"));
234 }
235 
237 {
238  if(!m_valid)
239  return;
240 
241  putp(tigetstr("cnorm"));
242 }
243 
244 static int ansiColor(uint32_t rgb)
245 {
246  int r = (rgb & 0xFF);
247  int g = (rgb >> 8) & 0xFF;
248  int b = (rgb >> 16) & 0xFF;
249 
250  r = r * 6 / 256;
251  g = g * 6 / 256;
252  b = b * 6 / 256;
253 
254  return 16 + 36 * r + 6 * g + b;
255 }
256 
257 void Terminal::setBackgroundColor(uint32_t color)
258 {
259  if(!m_valid)
260  return;
261 
262  if(m_truecolor)
263  {
264  char buf[256];
265  snprintf(buf, sizeof(buf), "\033[48;2;%d;%d;%dm", color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);
266  fputs(buf, stdout);
267  }
268  else
269  {
270  char* out = tiparm(m_bgColorStr.c_str(), ansiColor(color));
271  putp(out);
272  }
273 }
274 
275 void Terminal::setForegroundColor(uint32_t color)
276 {
277  if(!m_valid)
278  return;
279 
280  if(m_truecolor)
281  {
282  char buf[256];
283  snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);
284  fputs(buf, stdout);
285  }
286  else
287  {
288  char* out = tiparm(m_fgColorStr.c_str(), ansiColor(color));
289  putp(out);
290  }
291 }
292 
293 void Terminal::setEcho(bool on)
294 {
295  // Switch character echo on
296  termios ios;
297  if(tcgetattr(STDIN_FILENO, &ios) == 0)
298  {
299  if(on)
300  {
301  ios.c_lflag |= ECHO;
302  ios.c_lflag |= ICANON;
303  }
304  else
305  {
306  ios.c_lflag &= ~ECHO;
307  ios.c_lflag &= ~ICANON;
308  }
309 
310  tcsetattr(STDIN_FILENO, TCSANOW, &ios);
311  }
312 }
313 
314 void Terminal::setBold(bool on)
315 {
316  if(!m_valid)
317  return;
318 
319  if(on)
320  putp(m_boldStr.c_str());
321 }
322 
324 {
325  if(!m_valid)
326  return;
327 
328  char* out = tiparm(m_fgColorStr.c_str(), color);
329  putp(out);
330 }
331 
333 {
334  if(!m_valid)
335  return;
336 
337  char* out = tiparm(m_bgColorStr.c_str(), color);
338  putp(out);
339 }
340 
342 {
343  if(!m_valid)
344  return;
345 
348 }
349 
351 {
352  if(!m_valid)
353  return;
354 
355  putp(m_opStr.c_str());
356  putp(m_sgr0Str.c_str());
357 }
358 
360 {
361  if(!m_valid)
362  return;
363 
364  putp(m_elStr.c_str());
365 }
366 
367 void Terminal::moveCursorUp(int numLines)
368 {
369  if(!m_valid)
370  return;
371 
372  putp(tparm(m_upStr.c_str(), numLines));
373 }
374 
376 {
377  putchar('\r');
378 }
379 
380 bool Terminal::getSize(int* outColumns, int* outRows)
381 {
382  struct winsize w;
383  if(ioctl(STDIN_FILENO, TIOCGWINSZ, &w) == 0)
384  {
385  *outColumns = w.ws_col;
386  *outRows = w.ws_row;
387  return true;
388  }
389  else
390  return false;
391 }
392 
393 void Terminal::setWindowTitle(const std::string& title)
394 {
395  char buf[256];
396 
397  // Konsole style
398  snprintf(buf, sizeof(buf), "\033]30;%s\007", title.c_str());
399  fputs(buf, stdout);
400 
401  // screen/tmux style
402  snprintf(buf, sizeof(buf), "\033k%s\033\\", title.c_str());
403  fputs(buf, stdout);
404 }
405 
406 void Terminal::clearWindowTitle(const std::string& backup)
407 {
408  fputs("\033]30;%d : %n\007", stdout);
409 
410  // screen/tmux style
411  fmt::print("\033k{}\033\\", backup);
412 }
413 
415 {
416  char c;
417  if(read(STDIN_FILENO, &c, 1) != 1)
418  return -1;
419 
420  if(m_currentEscapeStr.empty() && c == '\E')
421  {
422  m_currentEscapeStr.push_back(c);
423  }
424  else if(!m_currentEscapeStr.empty())
425  {
426  m_currentEscapeStr.push_back(c);
427 
428  std::size_t matches = 0;
429  int lastMatch = -1;
430  bool completeMatch = false;
431 
432  for(auto& pair : m_specialKeys)
433  {
434  if(m_currentEscapeStr.length() > pair.first.length())
435  continue;
436 
437  if(pair.first.substr(0, m_currentEscapeStr.length()) == m_currentEscapeStr)
438  {
439  matches++;
440  lastMatch = pair.second;
441 
442  if(m_currentEscapeStr.length() == pair.first.length())
443  {
444  completeMatch = true;
445  break;
446  }
447  }
448  }
449 
450  if(matches == 0)
451  {
452  // We don't understand this code, just switch back to normal mode
453  m_currentEscapeStr.clear();
454  }
455  else if(completeMatch)
456  {
457  m_currentEscapeStr.clear();
458  return lastMatch;
459  }
460  }
461 
462  return c;
463 }
464 
465 }
static int ansiColor(uint32_t rgb)
Definition: terminal.cpp:244
SimpleColor
Simple colors.
Definition: terminal.h:29
bool getSize(int *columns, int *rows)
Get current window size.
Definition: terminal.cpp:380
void parse(char c)
parse single character c
Definition: terminal.cpp:69
void setForegroundColor(uint32_t color)
Set 24-bit foreground color.
Definition: terminal.cpp:275
void setSimpleBackground(SimpleColor color)
Definition: terminal.cpp:332
std::map< std::string, SpecialKey > m_specialKeys
Definition: terminal.h:185
void moveCursorToStartOfLine()
Move cursor to start of the line.
Definition: terminal.cpp:375
void setBackgroundColor(uint32_t color)
Set 24-bit background color.
Definition: terminal.cpp:257
void parseSetAttributes(const std::string &str)
Definition: terminal.cpp:34
std::string m_fgColorStr
Definition: terminal.h:178
void setEcho(bool on)
Definition: terminal.cpp:293
void setSimplePair(SimpleColor fg, SimpleColor bg)
Definition: terminal.cpp:341
std::string m_currentEscapeStr
Definition: terminal.h:187
std::string m_opStr
Definition: terminal.h:179
std::string m_boldStr
Definition: terminal.h:183
void clearToEndOfLine()
Clear characters from cursor to end of line.
Definition: terminal.cpp:359
string b
Definition: busy_node.py:4
void setStandardColors()
Reset fg + bg to standard terminal colors.
Definition: terminal.cpp:350
std::string m_bgColorStr
Definition: terminal.h:177
std::string m_elStr
Definition: terminal.h:181
std::string m_upStr
Definition: terminal.h:182
void setCursorVisible()
restore cursor
Definition: terminal.cpp:236
void setSimpleForeground(SimpleColor color)
Definition: terminal.cpp:323
Encapsulates terminal control.
Definition: terminal.h:20
void moveCursorUp(int numLines)
Move cursor up by numLines.
Definition: terminal.cpp:367
bool has256Colors() const
Definition: terminal.cpp:223
void setWindowTitle(const std::string &title)
Definition: terminal.cpp:393
std::string m_sgr0Str
Definition: terminal.h:180
void setCursorInvisible()
hide cursor
Definition: terminal.cpp:228
void apply(Terminal *term)
Apply the current internal state (colors) on the terminal.
Definition: terminal.cpp:108
void clearWindowTitle(const std::string &backup)
Definition: terminal.cpp:406
void setBold(bool on)
Definition: terminal.cpp:314


rosmon_core
Author(s): Max Schwarz
autogenerated on Wed Jul 10 2019 03:10:12