diff --git a/README.md b/README.md index 537e215a..46c69e38 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ We use icons from https://materialdesignicons.com for UI. ## License -- `src/ansi_escape.hpp`, `src/ansi_escape.cpp`, `src/log_formatter.hpp` and `src/log_formatter.cpp` are licensed under GPL-3.0 only. - `src/param`, `src/pfs` and `src/pkg` are licensed under LGPL-3.0. - All other source code are licensed under either MIT License or Apache License, Version 2.0; or both. - All release binaries are under GPL-3.0. diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 82b254ac..57422115 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -12,7 +12,6 @@ endif() # Setup GUI. add_executable(obliteration WIN32 MACOSX_BUNDLE - ansi_escape.cpp app_data.cpp core.cpp cpu_settings.cpp @@ -20,8 +19,6 @@ add_executable(obliteration WIN32 MACOSX_BUNDLE game_models.cpp initialize_wizard.cpp launch_settings.cpp - log_formatter.cpp - logs_viewer.cpp main.cpp main_window.cpp path.cpp diff --git a/gui/ansi_escape.cpp b/gui/ansi_escape.cpp deleted file mode 100644 index d4052798..00000000 --- a/gui/ansi_escape.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// This file is a derived works from Qt Creator. -// Licensed under GPL-3.0-only. -#include "ansi_escape.hpp" - -static QColor ansiColor(uint code) -{ - // QTC_ASSERT(code < 8, return QColor()); - if (!(code < 8)) { - return QColor(); - } - - const int red = code & 1 ? 170 : 0; - const int green = code & 2 ? 170 : 0; - const int blue = code & 4 ? 170 : 0; - - return QColor(red, green, blue); -} - -QList AnsiEscape::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 = "\x1b["; - const QChar semicolon = ';'; - const QChar colorTerminator = 'm'; - const QChar eraseToEol = 'K'; - - QList 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()) { - // QTC_ASSERT(m_pendingText.isEmpty(), break); - if (!m_pendingText.isEmpty()) { - break; - } - - if (m_waitingForTerminator) { - // We ignore all escape codes taking string arguments. - QString terminator = "\x1b\\"; - int terminatorPos = strippedText.indexOf(terminator); - if (terminatorPos == -1 && !m_alternateTerminator.isEmpty()) { - terminator = m_alternateTerminator; - terminatorPos = strippedText.indexOf(terminator); - } - if (terminatorPos == -1) { - m_pendingText = strippedText; - break; - } - m_waitingForTerminator = false; - m_alternateTerminator.clear(); - strippedText.remove(0, terminatorPos + terminator.length()); - if (strippedText.isEmpty()) - break; - } - const int escapePos = strippedText.indexOf(escape.at(0)); - if (escapePos < 0) { - outputData << FormattedText(strippedText, charFormat); - break; - } else if (escapePos != 0) { - outputData << FormattedText(strippedText.left(escapePos), charFormat); - strippedText.remove(0, escapePos); - } - - // QTC_ASSERT(strippedText.at(0) == escape.at(0), break); - if (!(strippedText.at(0) == escape.at(0))) { - break; - } - - while (!strippedText.isEmpty() && escape.at(0) == strippedText.at(0)) { - if (escape.startsWith(strippedText)) { - // control secquence is not complete - m_pendingText += strippedText; - strippedText.clear(); - break; - } - if (!strippedText.startsWith(escape)) { - switch (strippedText.at(1).toLatin1()) { - case '\\': // Unexpected terminator sequence. - Q_FALLTHROUGH(); - case 'N': case 'O': // Ignore unsupported single-character sequences. - strippedText.remove(0, 2); - break; - case ']': - m_alternateTerminator = QChar(7); - Q_FALLTHROUGH(); - case 'P': case 'X': case '^': case '_': - strippedText.remove(0, 2); - m_waitingForTerminator = true; - break; - default: - // not a control sequence - m_pendingText.clear(); - outputData << FormattedText(strippedText.left(1), charFormat); - strippedText.remove(0, 1); - continue; - } - break; - } - 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 uint code = numbers.at(i).toUInt(); - - 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;;; - 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; - uint index = numbers.at(i + 1).toUInt(); - - 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. - int grey = int((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 AnsiEscape::endFormatScope() -{ - m_previousFormatClosed = true; -} - -void AnsiEscape::setFormatScope(const QTextCharFormat &charFormat) -{ - m_previousFormat = charFormat; - m_previousFormatClosed = false; -} diff --git a/gui/ansi_escape.hpp b/gui/ansi_escape.hpp deleted file mode 100644 index 329a9b59..00000000 --- a/gui/ansi_escape.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// This file is a derived works from Qt Creator. -// Licensed under GPL-3.0-only. -#pragma once - -#include -#include -#include - -class FormattedText { -public: - FormattedText() = default; - FormattedText(const QString &txt, const QTextCharFormat &fmt = QTextCharFormat()) : - text(txt), - format(fmt) - { - } - - QString text; - QTextCharFormat format; -}; - -class AnsiEscape { -public: - QList parseText(const FormattedText &input); - void endFormatScope(); - -private: - void setFormatScope(const QTextCharFormat &charFormat); - -private: - bool m_previousFormatClosed = true; - bool m_waitingForTerminator = false; - QString m_alternateTerminator; - QTextCharFormat m_previousFormat; - QString m_pendingText; -}; diff --git a/gui/log_formatter.cpp b/gui/log_formatter.cpp deleted file mode 100644 index 5ad2cbc0..00000000 --- a/gui/log_formatter.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// This file is a derived works from Qt Creator. -// Licensed under GPL-3.0-only. -#include "log_formatter.hpp" - -#include -#include -#include - -static QString normalizeNewlines(const QString &text) -{ - QString res = text; - const auto newEnd = std::unique(res.begin(), res.end(), [](const QChar c1, const QChar c2) { - return c1 == '\r' && c2 == '\r'; // QTCREATORBUG-24556 - }); - res.chop(std::distance(newEnd, res.end())); - res.replace("\r\n", "\n"); - return res; -} - -LogFormatter::LogFormatter(QPlainTextEdit *output, QObject *parent) : - QObject(parent), - m_output(output), - m_cursor(output->textCursor()), - m_prependLineFeed(false), - m_prependCarriageReturn(false) -{ - m_cursor.movePosition(QTextCursor::End); -} - -LogFormatter::~LogFormatter() -{ -} - -void LogFormatter::appendMessage(const QString &text) -{ - if (text.isEmpty()) { - return; - } - - QString out = text; - - if (m_prependCarriageReturn) { - m_prependCarriageReturn = false; - out.prepend('\r'); - } - - out = normalizeNewlines(out); - - if (out.endsWith('\r')) { - m_prependCarriageReturn = true; - out.chop(1); - } - - // Forward all complete lines to the specialized formatting code, and handle a - // potential trailing incomplete line the same way as above. - for (qsizetype startPos = 0; startPos < out.size();) { - auto eolPos = out.indexOf('\n', startPos); - - if (eolPos == -1) { - doAppendMessage(out.mid(startPos)); - break; - } - - doAppendMessage(out.mid(startPos, eolPos - startPos)); - - m_prependLineFeed = true; - startPos = eolPos + 1; - } -} - -void LogFormatter::reset() -{ - m_output->clear(); - m_prependLineFeed = false; - m_prependCarriageReturn = false; - m_escapeCodeHandler = AnsiEscape(); -} - -void LogFormatter::doAppendMessage(const QString &text) -{ - QTextCharFormat charFmt; - QList formattedText = parseAnsi(text, charFmt); - - for (FormattedText output : formattedText) { - append(output.text, output.format); - } - - if (formattedText.isEmpty()) { - append({}, charFmt); // This might cause insertion of a newline character. - } -} - -void LogFormatter::append(const QString &text, const QTextCharFormat &format) -{ - flushTrailingNewline(); - - int startPos = 0; - int crPos = -1; - - while ((crPos = text.indexOf('\r', startPos)) >= 0) { - m_cursor.insertText(text.mid(startPos, crPos - startPos), format); - m_cursor.clearSelection(); - m_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); - startPos = crPos + 1; - } - - if (startPos < text.size()) { - m_cursor.insertText(text.mid(startPos), format); - } -} - -void LogFormatter::flushTrailingNewline() -{ - if (m_prependLineFeed) { - m_cursor.insertText("\n"); - m_prependLineFeed = false; - scroll(); - } -} - -QList LogFormatter::parseAnsi(const QString &text, const QTextCharFormat &format) -{ - return m_escapeCodeHandler.parseText(FormattedText(text, format)); -} - -void LogFormatter::scroll() -{ - auto bar = m_output->verticalScrollBar(); - auto max = bar->maximum(); - auto bottom = (bar->value() >= (max - 4)); // 4 is an error threshold. - - if (bottom) { - bar->setValue(max); - } -} diff --git a/gui/log_formatter.hpp b/gui/log_formatter.hpp deleted file mode 100644 index 654b229d..00000000 --- a/gui/log_formatter.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// This file is a derived works from Qt Creator. -// Licensed under GPL-3.0-only. -#pragma once - -#include "ansi_escape.hpp" - -#include -#include -#include -#include - -class QPlainTextEdit; - -class LogFormatter : public QObject { - Q_OBJECT -public: - LogFormatter(QPlainTextEdit *output, QObject *parent = nullptr); - ~LogFormatter() override; - -public: - void appendMessage(const QString &text); - void reset(); - -private: - void doAppendMessage(const QString &text); - void append(const QString &text, const QTextCharFormat &format); - void flushTrailingNewline(); - QList parseAnsi(const QString &text, const QTextCharFormat &format); - void scroll(); - -private: - AnsiEscape m_escapeCodeHandler; - QPlainTextEdit *m_output; - QTextCursor m_cursor; - bool m_prependLineFeed; - bool m_prependCarriageReturn; -}; diff --git a/gui/logs_viewer.cpp b/gui/logs_viewer.cpp deleted file mode 100644 index e8799f06..00000000 --- a/gui/logs_viewer.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "logs_viewer.hpp" -#include "log_formatter.hpp" - -#include -#include - -LogsViewer::LogsViewer() : - m_formatter(nullptr) -{ - auto layout = new QHBoxLayout(); - - setWindowTitle("Obliteration Logs"); - resize(1000, 500); - - // Setup viewer. - auto viewer = new QPlainTextEdit(); - - viewer->setReadOnly(true); - viewer->setLineWrapMode(QPlainTextEdit::NoWrap); - -#ifdef _WIN32 - viewer->document()->setDefaultFont(QFont("Courier New", 10)); -#elif __APPLE__ - viewer->document()->setDefaultFont(QFont("menlo", 10)); -#else - viewer->document()->setDefaultFont(QFont("monospace", 10)); -#endif - - layout->addWidget(viewer); - - // Setup formatter. - m_formatter = new LogFormatter(viewer, this); - - setLayout(layout); -} - -LogsViewer::~LogsViewer() -{ -} - -void LogsViewer::append(const QString &text) -{ - m_formatter->appendMessage(text); -} diff --git a/gui/logs_viewer.hpp b/gui/logs_viewer.hpp deleted file mode 100644 index 3ca85988..00000000 --- a/gui/logs_viewer.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -class LogFormatter; - -class LogsViewer final : public QWidget { -public: - LogsViewer(); - ~LogsViewer() override; - - void append(const QString &text); -private: - LogFormatter *m_formatter; -}; diff --git a/gui/main_window.cpp b/gui/main_window.cpp index d42e5298..62868be4 100644 --- a/gui/main_window.cpp +++ b/gui/main_window.cpp @@ -3,7 +3,6 @@ #include "display_settings.hpp" #include "game_models.hpp" #include "launch_settings.hpp" -#include "logs_viewer.hpp" #include "path.hpp" #include "pkg_installer.hpp" #include "profile_models.hpp" @@ -79,14 +78,6 @@ MainWindow::MainWindow( fileMenu->addSeparator(); fileMenu->addAction(quit); - // View menu. - auto viewMenu = menuBar()->addMenu("&View"); - auto logs = new QAction("&Logs", this); - - connect(logs, &QAction::triggered, this, &MainWindow::viewLogs); - - viewMenu->addAction(logs); - // Help menu. auto helpMenu = menuBar()->addMenu("&Help"); auto reportIssue = new QAction("&Report Issue", this); @@ -254,11 +245,6 @@ void MainWindow::closeEvent(QCloseEvent *event) killVmm(); } - // Close child windows. - if (m_logs && !m_logs->close()) { - return; - } - // Save geometry. QSettings settings; @@ -312,18 +298,6 @@ void MainWindow::openSystemFolder() QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } -void MainWindow::viewLogs() -{ - if (m_logs) { - m_logs->activateWindow(); - m_logs->raise(); - } else { - m_logs = new LogsViewer(); - m_logs->setAttribute(Qt::WA_DeleteOnClose); - m_logs->show(); - } -} - void MainWindow::reportIssue() { if (!QDesktopServices::openUrl(QUrl("https://github.com/obhq/obliteration/issues/new"))) { @@ -447,18 +421,14 @@ void MainWindow::waitKernelExit(bool success) void MainWindow::log(VmmLog type, const QString &msg) { - if (m_logs) { - m_logs->append(msg); - } else { - switch (type) { - case VmmLog_Info: - std::cout << msg.toStdString(); - break; - case VmmLog_Warn: - case VmmLog_Error: - std::cerr << msg.toStdString(); - break; - } + switch (type) { + case VmmLog_Info: + std::cout << msg.toStdString(); + break; + case VmmLog_Warn: + case VmmLog_Error: + std::cerr << msg.toStdString(); + break; } } diff --git a/gui/main_window.hpp b/gui/main_window.hpp index b4f983b0..453cddb7 100644 --- a/gui/main_window.hpp +++ b/gui/main_window.hpp @@ -4,14 +4,12 @@ #include #include -#include #ifndef __APPLE__ #include #endif class GameListModel; class LaunchSettings; -class LogsViewer; class ProfileList; class QCommandLineOption; class QCommandLineParser; @@ -41,7 +39,6 @@ protected: private slots: void installPkg(); void openSystemFolder(); - void viewLogs(); void reportIssue(); void aboutObliteration(); void saveProfile(Profile *p); @@ -66,7 +63,6 @@ private: GameListModel *m_games; LaunchSettings *m_launch; Screen *m_screen; - QPointer m_logs; Rust m_debugServer; QSocketNotifier *m_debugNoti; Rust m_vmm; // Destroy first.