QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input) { enum AnsiEscapeCodes { ResetFormat = 0, BoldText = 1, TextColorStart = 30, TextColorEnd = 37, RgbTextColor = 38, DefaultTextColor = 39, BackgroundColorStart = 40, BackgroundColorEnd = 47, RgbBackgroundColor = 48, DefaultBackgroundColor = 49 }; const QString escape = QLatin1String("\x1b["); const QChar semicolon = QLatin1Char(';'); const QChar colorTerminator = QLatin1Char('m'); const QChar eraseToEol = QLatin1Char('K'); QList<FormattedText> outputData; QTextCharFormat charFormat = m_previousFormatClosed ? input.format : m_previousFormat; QString strippedText; if (m_pendingText.isEmpty()) { strippedText = input.text; } else { strippedText = m_pendingText.append(input.text); m_pendingText.clear(); } while (!strippedText.isEmpty()) { const int escapePos = strippedText.indexOf(escape[0]); if (escapePos < 0) { outputData << FormattedText(strippedText, charFormat); break; } else if (escapePos != 0) { outputData << FormattedText(strippedText.left(escapePos), charFormat); strippedText.remove(0, escapePos); } while (!strippedText.isEmpty() && escape[0] == strippedText[0]) { if (escape.startsWith(strippedText)) { // control secquence is not complete m_pendingText += strippedText; strippedText.clear(); break; } if (!strippedText.startsWith(escape)) { // not a control sequence m_pendingText.clear(); outputData << FormattedText(strippedText.left(1), charFormat); strippedText.remove(0, 1); continue; } m_pendingText += strippedText.mid(0, escape.length()); strippedText.remove(0, escape.length()); // \e[K is not supported. Just strip it. if (strippedText.startsWith(eraseToEol)) { m_pendingText.clear(); strippedText.remove(0, 1); continue; } // get the number QString strNumber; QStringList numbers; while (!strippedText.isEmpty()) { if (strippedText.at(0).isDigit()) { strNumber += strippedText.at(0); } else { if (!strNumber.isEmpty()) numbers << strNumber; if (strNumber.isEmpty() || strippedText.at(0) != semicolon) break; strNumber.clear(); } m_pendingText += strippedText.mid(0, 1); strippedText.remove(0, 1); } if (strippedText.isEmpty()) break; // remove terminating char if (!strippedText.startsWith(colorTerminator)) { m_pendingText.clear(); strippedText.remove(0, 1); break; } // got consistent control sequence, ok to clear pending text m_pendingText.clear(); strippedText.remove(0, 1); if (numbers.isEmpty()) { charFormat = input.format; endFormatScope(); } for (int i = 0; i < numbers.size(); ++i) { const int code = numbers.at(i).toInt(); if (code >= TextColorStart && code <= TextColorEnd) { charFormat.setForeground(ansiColor(code - TextColorStart)); setFormatScope(charFormat); } else if (code >= BackgroundColorStart && code <= BackgroundColorEnd) { charFormat.setBackground(ansiColor(code - BackgroundColorStart)); setFormatScope(charFormat); } else { switch (code) { case ResetFormat: charFormat = input.format; endFormatScope(); break; case BoldText: charFormat.setFontWeight(QFont::Bold); setFormatScope(charFormat); break; case DefaultTextColor: charFormat.setForeground(input.format.foreground()); setFormatScope(charFormat); break; case DefaultBackgroundColor: charFormat.setBackground(input.format.background()); setFormatScope(charFormat); break; case RgbTextColor: case RgbBackgroundColor: // See http://en.wikipedia.org/wiki/ANSI_escape_code#Colors if (++i >= numbers.size()) break; switch (numbers.at(i).toInt()) { case 2: // RGB set with format: 38;2;<r>;<g>;<b> if ((i + 3) < numbers.size()) { (code == RgbTextColor) ? charFormat.setForeground(QColor(numbers.at(i + 1).toInt(), numbers.at(i + 2).toInt(), numbers.at(i + 3).toInt())) : charFormat.setBackground(QColor(numbers.at(i + 1).toInt(), numbers.at(i + 2).toInt(), numbers.at(i + 3).toInt())); setFormatScope(charFormat); } i += 3; break; case 5: // 256 color mode with format: 38;5;<i> uint index = numbers.at(i + 1).toInt(); QColor color; if (index < 8) { // The first 8 colors are standard low-intensity ANSI colors. color = ansiColor(index); } else if (index < 16) { // The next 8 colors are standard high-intensity ANSI colors. color = ansiColor(index - 8).lighter(150); } else if (index < 232) { // The next 216 colors are a 6x6x6 RGB cube. uint o = index - 16; color = QColor((o / 36) * 51, ((o / 6) % 6) * 51, (o % 6) * 51); } else { // The last 24 colors are a greyscale gradient. uint grey = (index - 232) * 11; color = QColor(grey, grey, grey); } if (code == RgbTextColor) charFormat.setForeground(color); else charFormat.setBackground(color); setFormatScope(charFormat); ++i; break; } break; default: break; } } } } } return outputData; }
} void Terminal::setBackgroundColor(uint32_t color) { if(!m_valid) return; if(m_truecolor) { char buf[256]; snprintf(buf, sizeof(buf), "\033[48;2;%d;%d;%dm", color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF); fputs(buf, stdout); } else { char* out = tiparm(m_bgColorStr.c_str(), ansiColor(color)); putp(out); } } void Terminal::setForegroundColor(uint32_t color) { if(!m_valid) return; if(m_truecolor) { char buf[256]; snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF); fputs(buf, stdout); }