24 апреля 2010

Кроссплатформенный цветной вывод в консоль.

Цветной вывод строки в *nix системах реализуеться с помощью вставки в строки специальных последовательностей. Данная последовательность должна начинаться с символа \033 (ESC).
Для наглядного примера привожу картинку (взято здесь):


Цветной вывод в win* системах реализуется вызовом функции SetConsoleTextAttribute.

Задача: под *nix системой оставить все как есть, а в win среде написать парсер ESC-последовательности, и в зависимости от встречаемых параметров менять цвета вывода.



Реализация парсера на qt-c++:
#define NIX_BACK_BLACK  40
#define NIX_BACK_RED   41
#define NIX_BACK_GREEN  42
#define NIX_BACK_YELLOW  43
#define NIX_BACK_BLUE   44
#define NIX_BACK_MAGNETTA 45
#define NIX_BACK_CYAN   46
#define NIX_BACK_GRAY   47

#define NIX_FORE_BLACK  30
#define NIX_FORE_RED   31
#define NIX_FORE_GREEN  32
#define NIX_FORE_YELLOW  33
#define NIX_FORE_BLUE   34
#define NIX_FORE_MAGNETTA 35
#define NIX_FORE_CYAN   36
#define NIX_FORE_GRAY   37

#define NIX_FORE_BOLD   1

#define NIX_DEFAULT    0

#if defined(__WIN32__) || defined(WIN) || defined(WIN32) || defined(Q_OS_WIN32)
//#include 
#define WIN_BACK_BLACK      0
#define WIN_BACK_RED       BACKGROUND_RED
#define WIN_BACK_LIGHT_RED    BACKGROUND_RED | BACKGROUND_INTENSITY
#define WIN_BACK_GREEN      BACKGROUND_GREEN
#define WIN_BACK_LIGHT_GREEN   BACKGROUND_GREEN | BACKGROUND_INTENSITY
#define WIN_BACK_YELLOW      BACKGROUND_GREEN | BACKGROUND_RED
#define WIN_BACK_LIGHT_YELLOW   BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
#define WIN_BACK_BLUE       BACKGROUND_BLUE
#define WIN_BACK_LIGHT_BLUE    BACKGROUND_BLUE | BACKGROUND_INTENSITY
#define WIN_BACK_MAGNETTA     BACKGROUND_RED | BACKGROUND_BLUE
#define WIN_BACK_LIGHT_MAGNETTA  BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY
#define WIN_BACK_CYAN       BACKGROUND_BLUE | BACKGROUND_GREEN
#define WIN_BACK_LIGHT_CYAN    BACKGROUND_BLUE | BACKGROUND_GREEN
#define WIN_BACK_GRAY       BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED
#define WIN_BACK_WHITE      BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY

#define WIN_FORE_BLACK      0
#define WIN_FORE_RED       FOREGROUND_RED
#define WIN_FORE_LIGHT_RED    FOREGROUND_RED | FOREGROUND_INTENSITY
#define WIN_FORE_GREEN      FOREGROUND_GREEN
#define WIN_FORE_LIGHT_GREEN   FOREGROUND_GREEN | FOREGROUND_INTENSITY
#define WIN_FORE_YELLOW      FOREGROUND_GREEN | FOREGROUND_RED
#define WIN_FORE_LIGHT_YELLOW   FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
#define WIN_FORE_BLUE       FOREGROUND_BLUE
#define WIN_FORE_LIGHT_BLUE    FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define WIN_FORE_MAGNETTA     FOREGROUND_RED | FOREGROUND_BLUE
#define WIN_FORE_LIGHT_MAGNETTA  FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define WIN_FORE_CYAN       FOREGROUND_BLUE | FOREGROUND_GREEN
#define WIN_FORE_LIGHT_CYAN    FOREGROUND_BLUE | FOREGROUND_GREEN
#define WIN_FORE_GRAY       FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
#define WIN_FORE_WHITE      FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY

#define WIN_FORE_BOLD       FOREGROUND_INTENSITY

#define WIN_DEFAULT        FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED

// STD_OUTPUT_HANDLE
// STD_ERROR_HANDLE
void colorOutputString(HANDLE hConsole, const QString& output)
{
  QString message = output;

  // save colors
  CONSOLE_SCREEN_BUFFER_INFO cbi = {sizeof(cbi)};
  GetConsoleScreenBufferInfo(hConsole,&cbi);

  wchar_t *wideMessage;

  QStringList colorizedMessage = message.split('\033');

  int actualSize;
  DWORD out;

  WORD color = 0;
  WORD newColor = 0;
  QString parsedWordString;
  QStringList escParams;
  int indexOfM;
  // display first part of message
  if (!colorizedMessage.at(0).isEmpty()) {
    wideMessage = new wchar_t [colorizedMessage.at(0).size()];
    actualSize = colorizedMessage.at(0).toWCharArray(wideMessage);
    WriteConsoleW(hConsole, wideMessage, actualSize, &out, 0);
    delete [] wideMessage;
    colorizedMessage.removeAt(0);
  }
  foreach (QString it, colorizedMessage) {
    // color setted
    if (it.startsWith("[")) {
      indexOfM = it.indexOf('m');
      // not esc-sequence
      if (indexOfM != -1) {
        parsedWordString = it.mid(1, indexOfM - 1);

        escParams = parsedWordString.split(';');
        foreach(QString param, escParams) {
          color = param.toUInt();
          switch(color) {
          case NIX_DEFAULT:
            newColor = WIN_DEFAULT;
            break;
          case NIX_FORE_BOLD:
            newColor |= WIN_FORE_BOLD;
            break;
          case NIX_BACK_BLACK :
            newColor = (newColor & 0x0f) | WIN_BACK_BLACK;
            break;
          case NIX_BACK_RED :
            newColor = (newColor & 0x0f) | WIN_BACK_RED;
            break;
          case NIX_BACK_GREEN :
            newColor = (newColor & 0x0f) | WIN_BACK_GREEN;
            break;
          case NIX_BACK_YELLOW :
            newColor = (newColor & 0x0f) | WIN_BACK_YELLOW;
            break;
          case NIX_BACK_BLUE :
            newColor = (newColor & 0x0f) | WIN_BACK_BLUE;
            break;
          case NIX_BACK_MAGNETTA :
            newColor = (newColor & 0x0f) | WIN_BACK_MAGNETTA;
            break;
          case NIX_BACK_CYAN :
            newColor = (newColor & 0x0f) | WIN_BACK_CYAN;
            break;
          case NIX_BACK_GRAY :
            newColor = (newColor & 0x0f) | WIN_BACK_GRAY;
            break;
          case NIX_FORE_BLACK :
            newColor = (newColor & 0xF8)| WIN_FORE_BLACK;
            break;
          case NIX_FORE_RED :
            newColor = (newColor & 0xF8) | WIN_FORE_RED;
            break;
          case NIX_FORE_GREEN :
            newColor = (newColor & 0xF8) | WIN_FORE_GREEN;
            break;
          case NIX_FORE_YELLOW :
            newColor = (newColor & 0xF8) | WIN_FORE_YELLOW;
            break;
          case NIX_FORE_BLUE :
            newColor = (newColor & 0xF8) | WIN_FORE_BLUE;
            break;
          case NIX_FORE_MAGNETTA :
            newColor = (newColor & 0xF8) | WIN_FORE_MAGNETTA;
            break;
          case NIX_FORE_CYAN :
            newColor = (newColor & 0xF8) | WIN_FORE_CYAN;
            break;
          case NIX_FORE_GRAY :
            newColor = (newColor & 0xF8) | WIN_FORE_GRAY;
            break;
          default:;
          }
        }
        it = it.mid(indexOfM + 1);

        SetConsoleTextAttribute(hConsole, newColor);
      }
    }

    wideMessage = new wchar_t [it.size()];
    actualSize = it.toWCharArray(wideMessage);
    WriteConsoleW(hConsole, wideMessage, actualSize, &out, 0);
    delete [] wideMessage;
  }
  // load old colors
  SetConsoleTextAttribute(hConsole, cbi.wAttributes);

  //qDebug() << colorizedMessage;  
} 
#endif //windows

Использование в qt-c++ коде:
QString message = "\e[47m gray background \e[1,31m with light red text";
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
colorOutputString(hConsole, message);
CloseHandle(hConsole);
После вышеперечисленных манипуляций я вывел палитру в win*:

Комментариев нет:

Отправить комментарий