diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp
index a11adad726..38a59ffdef 100644
--- a/rpcs3/rpcs3qt/log_frame.cpp
+++ b/rpcs3/rpcs3qt/log_frame.cpp
@@ -79,7 +79,6 @@ struct gui_listener : logs::listener
}
_new->msg += text;
- _new->msg += '\n';
queue.push(std::move(p));
}
@@ -587,87 +586,153 @@ void log_frame::UpdateUI()
const QString font_start_tag_stack = "";
const QString font_end_tag = QStringLiteral("");
- static constexpr auto escaped = [](QString& text)
+ static constexpr auto escaped = [](const QString& text)
{
return text.toHtmlEscaped().replace(QStringLiteral("\n"), QStringLiteral("
"));
};
+ // Preserve capacity
+ m_log_text.resize(0);
+
+ // Handle a special case in which we may need to override the previous repetition count
+ bool is_first_rep = true;
+
+ // Batch output of multiple lines if possible (optimization)
+ auto flush = [&]()
+ {
+ if (m_log_text.isEmpty() && !is_first_rep)
+ {
+ return;
+ }
+
+ // save old log state
+ QScrollBar* sb = m_log->verticalScrollBar();
+ const bool isMax = sb->value() == sb->maximum();
+ const int sb_pos = sb->value();
+
+ QTextCursor text_cursor = m_log->textCursor();
+ const int sel_pos = text_cursor.position();
+ int sel_start = text_cursor.selectionStart();
+ int sel_end = text_cursor.selectionEnd();
+
+ // clear selection or else it will get colorized as well
+ text_cursor.clearSelection();
+
+ m_log->setTextCursor(text_cursor);
+
+ if (is_first_rep)
+ {
+ // Override repetition count of previous UpdateUI() (special case)
+ text_cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+ text_cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, (m_log_counter != 2 ? 1 + QString::number(m_log_counter - 1).size() : 0));
+ text_cursor.insertHtml(font_start_tag_stack % QStringLiteral(" x") % QString::number(m_log_counter) % font_end_tag);
+ }
+ else if (m_log_counter > 1)
+ {
+ // Insert both messages and repetition prefix (append is more optimized than concatenation)
+ m_log_text += font_end_tag;
+ m_log_text += font_start_tag_stack;
+ m_log_text += QStringLiteral(" x");
+ m_log_text += QString::number(m_log_counter);
+ m_log_text += font_end_tag;
+
+ m_log->appendHtml(m_log_text);
+ m_log_counter = 0;
+ }
+ else
+ {
+ m_log_text += font_end_tag;
+ m_log->appendHtml(m_log_text);
+ }
+
+ // if we mark text from right to left we need to swap sides (start is always smaller than end)
+ if (sel_pos < sel_end)
+ {
+ std::swap(sel_start, sel_end);
+ }
+
+ // reset old text cursor and selection
+ text_cursor.setPosition(sel_start);
+ text_cursor.setPosition(sel_end, QTextCursor::KeepAnchor);
+ m_log->setTextCursor(text_cursor);
+
+ // set scrollbar to max means auto-scroll
+ sb->setValue(isMax ? sb->maximum() : sb_pos);
+ m_log_text.clear();
+ };
+
// Check main logs
while (auto* packet = s_gui_listener.get())
{
// Confirm log level
if (packet->sev <= s_gui_listener.enabled)
{
- QString text;
- switch (packet->sev)
+ if (m_stack_log && m_old_log_level == packet->sev && packet->msg == m_old_log_text)
{
- case logs::level::always: text = QStringLiteral("- "); break;
- case logs::level::fatal: text = QStringLiteral("F "); break;
- case logs::level::error: text = QStringLiteral("E "); break;
- case logs::level::todo: text = QStringLiteral("U "); break;
- case logs::level::success: text = QStringLiteral("S "); break;
- case logs::level::warning: text = QStringLiteral("W "); break;
- case logs::level::notice: text = QStringLiteral("! "); break;
- case logs::level::trace: text = QStringLiteral("T "); break;
+ m_log_counter++;
+
+ if (is_first_rep)
+ {
+ flush();
+ }
+
+ s_gui_listener.pop();
+ continue;
}
- // Print UTF-8 text.
- text += qstr(packet->msg);
+ is_first_rep = false;
- // save old log state
- QScrollBar* sb = m_log->verticalScrollBar();
- const bool isMax = sb->value() == sb->maximum();
- const int sb_pos = sb->value();
-
- QTextCursor text_cursor = m_log->textCursor();
- const int sel_pos = text_cursor.position();
- int sel_start = text_cursor.selectionStart();
- int sel_end = text_cursor.selectionEnd();
-
- // clear selection or else it will get colorized as well
- text_cursor.clearSelection();
-
- // remove the new line because Qt's append adds a new line already.
- text.chop(1);
-
- // create counter suffix and remove recurring line if needed
- if (m_stack_log)
+ if (m_log_counter > 1)
{
- // add counter suffix if needed
- if (text == m_old_log_text)
+ // Add counter suffix if needed
+ flush();
+ }
+
+ if (m_log_text.size() > 0x1000)
+ {
+ // Try not to hold too much data at a time so the frame content will be updated frequently
+ flush();
+ }
+
+ if (!m_log_text.isEmpty())
+ {
+ if (packet->sev != m_old_log_level)
{
- text_cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
- text_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
- text_cursor.insertHtml(font_start_tag(m_color[static_cast(packet->sev)]) % escaped(text) % font_start_tag_stack % QStringLiteral(" x") % QString::number(++m_log_counter) % font_end_tag % font_end_tag);
+ flush();
+ m_old_log_level = packet->sev;
+ m_log_text += font_start_tag(m_color[static_cast(m_old_log_level)]);
}
else
{
- m_log_counter = 1;
- m_old_log_text = text;
-
- m_log->setTextCursor(text_cursor);
- m_log->appendHtml(font_start_tag(m_color[static_cast(packet->sev)]) % escaped(text) % font_end_tag);
+ m_log_text += QStringLiteral("
");
}
}
else
{
- m_log->setTextCursor(text_cursor);
- m_log->appendHtml(font_start_tag(m_color[static_cast(packet->sev)]) % escaped(text) % font_end_tag);
+ m_old_log_level = packet->sev;
+ m_log_text += font_start_tag(m_color[static_cast(m_old_log_level)]);
}
- // if we mark text from right to left we need to swap sides (start is always smaller than end)
- if (sel_pos < sel_end)
+ switch (packet->sev)
{
- std::swap(sel_start, sel_end);
+ case logs::level::always: m_log_text += QStringLiteral("- "); break;
+ case logs::level::fatal: m_log_text += QStringLiteral("F "); break;
+ case logs::level::error: m_log_text += QStringLiteral("E "); break;
+ case logs::level::todo: m_log_text += QStringLiteral("U "); break;
+ case logs::level::success: m_log_text += QStringLiteral("S "); break;
+ case logs::level::warning: m_log_text += QStringLiteral("W "); break;
+ case logs::level::notice: m_log_text += QStringLiteral("! "); break;
+ case logs::level::trace: m_log_text += QStringLiteral("T "); break;
}
- // reset old text cursor and selection
- text_cursor.setPosition(sel_start);
- text_cursor.setPosition(sel_end, QTextCursor::KeepAnchor);
- m_log->setTextCursor(text_cursor);
+ // Print UTF-8 text.
+ m_log_text += escaped(qstr(packet->msg));
- // set scrollbar to max means auto-scroll
- sb->setValue(isMax ? sb->maximum() : sb_pos);
+ if (m_stack_log)
+ {
+ m_log_counter = 1;
+ m_old_log_text = std::move(packet->msg);
+ }
}
// Drop packet
@@ -676,6 +741,9 @@ void log_frame::UpdateUI()
// Limit processing time
if (steady_clock::now() >= start + 7ms) break;
}
+
+ is_first_rep = false;
+ flush();
}
void log_frame::closeEvent(QCloseEvent *event)
diff --git a/rpcs3/rpcs3qt/log_frame.h b/rpcs3/rpcs3qt/log_frame.h
index d4f9900f1a..127b84efcb 100644
--- a/rpcs3/rpcs3qt/log_frame.h
+++ b/rpcs3/rpcs3qt/log_frame.h
@@ -49,12 +49,14 @@ private:
QList m_color;
QColor m_color_stack;
QPlainTextEdit* m_log = nullptr;
- QString m_old_log_text;
+ std::string m_old_log_text;
QString m_old_tty_text;
+ QString m_log_text;
ullong m_log_counter{};
ullong m_tty_counter{};
bool m_stack_log{};
bool m_stack_tty{};
+ logs::level m_old_log_level{};
fs::file m_tty_file;
QWidget* m_tty_container = nullptr;