void log_frame::UpdateUI() { std::vector<char> buf(4096); // Get UTF-8 string from file auto get_utf8 = [&](const fs::file& file, u64 size) -> QString { size = file.read(buf.data(), size); for (u64 i = 0; i < size; i++) { // Get UTF-8 sequence length (no real validation performed) const u64 tail = (buf[i] & 0xF0) == 0xF0 ? 3 : (buf[i] & 0xE0) == 0xE0 ? 2 : (buf[i] & 0xC0) == 0xC0 ? 1 : 0; if (i + tail >= size) { // Copying is expensive-- O(i)-- but I suspect this corruption will be exceptionally unlikely. file.seek(i - size, fs::seek_cur); std::vector<char> sub(&buf[0], &buf[i]); return QString(sub.data()); } } return QString(buf.data()); }; const auto start = steady_clock::now(); // Check TTY logs while (const u64 size = std::min<u64>(buf.size(), m_tty_file.size() - m_tty_file.pos())) { QString text = get_utf8(m_tty_file, size); // Hackily used the state of the check.. be better if I actually stored this value. if (m_TTYAct->isChecked()) { text.chop(1); // remove newline since Qt automatically adds a newline. m_tty->append(text); } // Limit processing time if (steady_clock::now() >= start + 4ms || text.isEmpty()) break; } // Check main logs while (const auto packet = s_gui_listener.read->next.load()) { // Confirm log level if (packet->sev <= s_gui_listener.enabled) { QString text; switch (packet->sev) { case logs::level::always: break; case logs::level::fatal: text = "F "; break; case logs::level::error: text = "E "; break; case logs::level::todo: text = "U "; break; case logs::level::success: text = "S "; break; case logs::level::warning: text = "W "; break; case logs::level::notice: text = "! "; break; case logs::level::trace: text = "T "; break; default: continue; } // Print UTF-8 text. text += qstr(packet->msg); // save old log state QScrollBar *sb = m_log->verticalScrollBar(); bool isMax = sb->value() == sb->maximum(); int sb_pos = sb->value(); int sel_pos = m_log->textCursor().position(); int sel_start = m_log->textCursor().selectionStart(); int sel_end = m_log->textCursor().selectionEnd(); // clear selection or else it will get colorized as well QTextCursor c = m_log->textCursor(); c.clearSelection(); m_log->setTextCursor(c); // remove the new line because Qt's append adds a new line already. text.chop(1); QString suffix; bool isSame = text == m_old_text; // create counter suffix and remove recurring line if needed if (m_stack_log) { if (isSame) { m_log_counter++; suffix = QString(" x%1").arg(m_log_counter); m_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); m_log->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); m_log->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); m_log->textCursor().removeSelectedText(); m_log->textCursor().deletePreviousChar(); } else { m_log_counter = 1; m_old_text = text; } } // add actual log message m_log->setTextColor(m_color[static_cast<int>(packet->sev)]); m_log->append(text); // add counter suffix if needed if (m_stack_log && isSame) { m_log->setTextColor(m_color_stack); m_log->insertPlainText(suffix); } // 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 c.setPosition(sel_start); c.setPosition(sel_end, QTextCursor::KeepAnchor); m_log->setTextCursor(c); // set scrollbar to max means auto-scroll sb->setValue(isMax ? sb->maximum() : sb_pos); } // Drop packet s_gui_listener.pop(); // Limit processing time if (steady_clock::now() >= start + 7ms) break; } }
void log_frame::UpdateUI() { const auto start = steady_clock::now(); // Check TTY logs while (const u64 size = std::max<s64>(0, g_tty_size.load() - m_tty_file.pos())) { std::string buf; buf.resize(size); buf.resize(m_tty_file.read(&buf.front(), buf.size())); if (buf.find_first_of('\0') != -1) { m_tty_file.seek(s64{0} - buf.size(), fs::seek_mode::seek_cur); break; } if (buf.size() && m_TTYAct->isChecked()) { QTextCursor text_cursor{m_tty->document()}; text_cursor.movePosition(QTextCursor::End); text_cursor.insertText(qstr(buf)); } // Limit processing time if (steady_clock::now() >= start + 4ms || buf.empty()) break; } // Check main logs while (const auto packet = s_gui_listener.read->next.load()) { // Confirm log level if (packet->sev <= s_gui_listener.enabled) { QString text; switch (packet->sev) { case logs::level::always: break; case logs::level::fatal: text = "F "; break; case logs::level::error: text = "E "; break; case logs::level::todo: text = "U "; break; case logs::level::success: text = "S "; break; case logs::level::warning: text = "W "; break; case logs::level::notice: text = "! "; break; case logs::level::trace: text = "T "; break; default: continue; } // Print UTF-8 text. text += qstr(packet->msg); // save old log state QScrollBar *sb = m_log->verticalScrollBar(); bool isMax = sb->value() == sb->maximum(); int sb_pos = sb->value(); int sel_pos = m_log->textCursor().position(); int sel_start = m_log->textCursor().selectionStart(); int sel_end = m_log->textCursor().selectionEnd(); // clear selection or else it will get colorized as well QTextCursor c = m_log->textCursor(); c.clearSelection(); m_log->setTextCursor(c); // remove the new line because Qt's append adds a new line already. text.chop(1); QString suffix; bool isSame = text == m_old_text; // create counter suffix and remove recurring line if needed if (m_stack_log) { if (isSame) { m_log_counter++; suffix = QString(" x%1").arg(m_log_counter); m_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); m_log->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); m_log->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); m_log->textCursor().removeSelectedText(); m_log->textCursor().deletePreviousChar(); } else { m_log_counter = 1; m_old_text = text; } } // add actual log message m_log->setTextColor(m_color[static_cast<int>(packet->sev)]); m_log->append(text); // add counter suffix if needed if (m_stack_log && isSame) { m_log->setTextColor(m_color_stack); m_log->insertPlainText(suffix); } // 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 c.setPosition(sel_start); c.setPosition(sel_end, QTextCursor::KeepAnchor); m_log->setTextCursor(c); // set scrollbar to max means auto-scroll sb->setValue(isMax ? sb->maximum() : sb_pos); } // Drop packet s_gui_listener.pop(); // Limit processing time if (steady_clock::now() >= start + 7ms) break; } }