Removes Qt log viewer (#1122)

This commit is contained in:
Putta Khunchalee 2024-11-21 01:39:08 +07:00 committed by GitHub
parent c25bf3e09d
commit 89dfe18453
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 8 additions and 570 deletions

View File

@ -24,7 +24,6 @@ We use icons from https://materialdesignicons.com for UI.
## License ## 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. - `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 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. - All release binaries are under GPL-3.0.

View File

@ -12,7 +12,6 @@ endif()
# Setup GUI. # Setup GUI.
add_executable(obliteration WIN32 MACOSX_BUNDLE add_executable(obliteration WIN32 MACOSX_BUNDLE
ansi_escape.cpp
app_data.cpp app_data.cpp
core.cpp core.cpp
cpu_settings.cpp cpu_settings.cpp
@ -20,8 +19,6 @@ add_executable(obliteration WIN32 MACOSX_BUNDLE
game_models.cpp game_models.cpp
initialize_wizard.cpp initialize_wizard.cpp
launch_settings.cpp launch_settings.cpp
log_formatter.cpp
logs_viewer.cpp
main.cpp main.cpp
main_window.cpp main_window.cpp
path.cpp path.cpp

View File

@ -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<FormattedText> 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<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()) {
// 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;<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).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;
}

View File

@ -1,36 +0,0 @@
// This file is a derived works from Qt Creator.
// Licensed under GPL-3.0-only.
#pragma once
#include <QList>
#include <QString>
#include <QTextCharFormat>
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<FormattedText> 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;
};

View File

@ -1,135 +0,0 @@
// This file is a derived works from Qt Creator.
// Licensed under GPL-3.0-only.
#include "log_formatter.hpp"
#include <QBrush>
#include <QPlainTextEdit>
#include <QScrollBar>
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> 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<FormattedText> 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);
}
}

View File

@ -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 <QList>
#include <QObject>
#include <QTextCharFormat>
#include <QTextCursor>
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<FormattedText> 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;
};

View File

@ -1,44 +0,0 @@
#include "logs_viewer.hpp"
#include "log_formatter.hpp"
#include <QHBoxLayout>
#include <QPlainTextEdit>
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);
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <QWidget>
class LogFormatter;
class LogsViewer final : public QWidget {
public:
LogsViewer();
~LogsViewer() override;
void append(const QString &text);
private:
LogFormatter *m_formatter;
};

View File

@ -3,7 +3,6 @@
#include "display_settings.hpp" #include "display_settings.hpp"
#include "game_models.hpp" #include "game_models.hpp"
#include "launch_settings.hpp" #include "launch_settings.hpp"
#include "logs_viewer.hpp"
#include "path.hpp" #include "path.hpp"
#include "pkg_installer.hpp" #include "pkg_installer.hpp"
#include "profile_models.hpp" #include "profile_models.hpp"
@ -79,14 +78,6 @@ MainWindow::MainWindow(
fileMenu->addSeparator(); fileMenu->addSeparator();
fileMenu->addAction(quit); 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. // Help menu.
auto helpMenu = menuBar()->addMenu("&Help"); auto helpMenu = menuBar()->addMenu("&Help");
auto reportIssue = new QAction("&Report Issue", this); auto reportIssue = new QAction("&Report Issue", this);
@ -254,11 +245,6 @@ void MainWindow::closeEvent(QCloseEvent *event)
killVmm(); killVmm();
} }
// Close child windows.
if (m_logs && !m_logs->close()) {
return;
}
// Save geometry. // Save geometry.
QSettings settings; QSettings settings;
@ -312,18 +298,6 @@ void MainWindow::openSystemFolder()
QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); 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() void MainWindow::reportIssue()
{ {
if (!QDesktopServices::openUrl(QUrl("https://github.com/obhq/obliteration/issues/new"))) { if (!QDesktopServices::openUrl(QUrl("https://github.com/obhq/obliteration/issues/new"))) {
@ -447,9 +421,6 @@ void MainWindow::waitKernelExit(bool success)
void MainWindow::log(VmmLog type, const QString &msg) void MainWindow::log(VmmLog type, const QString &msg)
{ {
if (m_logs) {
m_logs->append(msg);
} else {
switch (type) { switch (type) {
case VmmLog_Info: case VmmLog_Info:
std::cout << msg.toStdString(); std::cout << msg.toStdString();
@ -459,7 +430,6 @@ void MainWindow::log(VmmLog type, const QString &msg)
std::cerr << msg.toStdString(); std::cerr << msg.toStdString();
break; break;
} }
}
} }
void MainWindow::setupDebugger() void MainWindow::setupDebugger()

View File

@ -4,14 +4,12 @@
#include <QList> #include <QList>
#include <QMainWindow> #include <QMainWindow>
#include <QPointer>
#ifndef __APPLE__ #ifndef __APPLE__
#include <QVulkanInstance> #include <QVulkanInstance>
#endif #endif
class GameListModel; class GameListModel;
class LaunchSettings; class LaunchSettings;
class LogsViewer;
class ProfileList; class ProfileList;
class QCommandLineOption; class QCommandLineOption;
class QCommandLineParser; class QCommandLineParser;
@ -41,7 +39,6 @@ protected:
private slots: private slots:
void installPkg(); void installPkg();
void openSystemFolder(); void openSystemFolder();
void viewLogs();
void reportIssue(); void reportIssue();
void aboutObliteration(); void aboutObliteration();
void saveProfile(Profile *p); void saveProfile(Profile *p);
@ -66,7 +63,6 @@ private:
GameListModel *m_games; GameListModel *m_games;
LaunchSettings *m_launch; LaunchSettings *m_launch;
Screen *m_screen; Screen *m_screen;
QPointer<LogsViewer> m_logs;
Rust<DebugServer> m_debugServer; Rust<DebugServer> m_debugServer;
QSocketNotifier *m_debugNoti; QSocketNotifier *m_debugNoti;
Rust<Vmm> m_vmm; // Destroy first. Rust<Vmm> m_vmm; // Destroy first.