From b5595ea4501132c8344dcdc045d997afef4aa03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Aug 2025 00:04:54 +0200 Subject: [PATCH] Add simple imgui log window (just reads the short log ring buffer) --- Common/Log/LogManager.h | 16 +++++++-- UI/DevScreens.cpp | 18 +++-------- UI/EmuScreen.cpp | 2 +- UI/ImDebugger/ImDebugger.cpp | 63 ++++++++++++++++++++++++++++++++++++ UI/ImDebugger/ImDebugger.h | 11 +++++++ assets/lang/sv_SE.ini | 2 +- 6 files changed, 94 insertions(+), 18 deletions(-) diff --git a/Common/Log/LogManager.h b/Common/Log/LogManager.h index a366972866..2a4c6a2218 100644 --- a/Common/Log/LogManager.h +++ b/Common/Log/LogManager.h @@ -137,8 +137,8 @@ public: } #endif - const RingbufferLog *GetRingbuffer() const { - return &ringLog_; + const RingbufferLog &GetRingbuffer() const { + return ringLog_; } void Init(bool *enabledSetting, bool headless = false); @@ -157,6 +157,18 @@ public: static const char *GetLogTypeName(Log type); + static u32 GetLevelColor(LogLevel level) { + switch (level) { + case LogLevel::LDEBUG: return 0xE0E0E0; + case LogLevel::LWARNING: return 0x50FFFF; + case LogLevel::LERROR: return 0x5050FF; + case LogLevel::LNOTICE: return 0x30FF30; + case LogLevel::LINFO: return 0xFFFF60; + case LogLevel::LVERBOSE: + default: return 0xC0C0C0; + } + } + private: // Prevent copies. LogManager(const LogManager &) = delete; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 5ea9925d0c..8ef5f268a4 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -260,23 +260,13 @@ void GPIGPOScreen::CreatePopupContents(UI::ViewGroup *parent) { void LogViewScreen::UpdateLog() { using namespace UI; - const RingbufferLog *ring = g_logManager.GetRingbuffer(); - if (!ring) - return; + const RingbufferLog &ring = g_logManager.GetRingbuffer(); vert_->Clear(); // TODO: Direct rendering without TextViews. - for (int i = ring->GetCount() - 1; i >= 0; i--) { - TextView *v = vert_->Add(new TextView(StripSpaces(ring->TextAt(i)), FLAG_DYNAMIC_ASCII, true)); - uint32_t color = 0xFFFFFF; - switch (ring->LevelAt(i)) { - case LogLevel::LDEBUG: color = 0xE0E0E0; break; - case LogLevel::LWARNING: color = 0x50FFFF; break; - case LogLevel::LERROR: color = 0x5050FF; break; - case LogLevel::LNOTICE: color = 0x30FF30; break; - case LogLevel::LINFO: color = 0xFFFFFF; break; - case LogLevel::LVERBOSE: color = 0xC0C0C0; break; - } + for (int i = ring.GetCount() - 1; i >= 0; i--) { + TextView *v = vert_->Add(new TextView(StripSpaces(ring.TextAt(i)), FLAG_DYNAMIC_ASCII, true)); + uint32_t color = LogManager::GetLevelColor(ring.LevelAt(i)); v->SetTextColor(0xFF000000 | color); } toBottom_ = true; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 4e33b30bed..744d6787f2 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -455,7 +455,7 @@ EmuScreen::~EmuScreen() { SetExtraAssertInfo(nullptr); SetAssertCancelCallback(nullptr, nullptr); - g_logManager.EnableOutput(LogOutput::RingBuffer); + g_logManager.DisableOutput(LogOutput::RingBuffer); #ifndef MOBILE_DEVICE if (g_Config.bDumpFrames && startDumping_) diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index c41b9a2eb0..958feecc9b 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1440,6 +1440,60 @@ void DrawAudioChannels(ImConfig &cfg, ImControl &control) { ImGui::End(); } +void ImLogWindow::Draw(ImConfig &cfg) { + ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Log", &cfg.logOpen)) { + ImGui::End(); + return; + } + + const RingbufferLog &ring = g_logManager.GetRingbuffer(); + + // Options menu + if (ImGui::BeginPopup("Options")) { + ImGui::Checkbox("Auto-scroll", &AutoScroll); + ImGui::EndPopup(); + } + + // Main window + if (ImGui::Button("Options")) + ImGui::OpenPopup("Options"); + ImGui::SameLine(); + bool copy = ImGui::Button("Copy"); + ImGui::Separator(); + + if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) { + if (copy) + ImGui::LogToClipboard(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + + ImGuiListClipper clipper; + clipper.Begin(ring.GetCount()); + while (clipper.Step()) { + for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) { + int n = ring.GetCount() - 1 - line_no; + + const std::string_view line = ring.TextAt(n); + const LogLevel level = ring.LevelAt(n); + const u32 color = 0xFF000000 | LogManager::GetLevelColor(level); + ImGui::PushStyleColor(ImGuiCol_Text, color); + ImGui::TextUnformatted(line); + ImGui::PopStyleColor(); + } + } + clipper.End(); + ImGui::PopStyleVar(); + + // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame. + // Using a scrollbar or mouse-wheel will take away from the bottom edge. + if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 20) + ImGui::SetScrollHereY(1.0f); + } + ImGui::EndChild(); + ImGui::End(); +} + void DrawLogConfig(ImConfig &cfg) { if (!ImGui::Begin("Logs", &cfg.logConfigOpen)) { ImGui::End(); @@ -2115,6 +2169,9 @@ ImDebugger::ImDebugger() { reqToken_ = g_requestManager.GenerateRequesterToken(); cfg_.LoadConfig(ConfigPath()); g_normalTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + + // Just enable ring buffer logging here, it's cheap (but needed for the log window) + g_logManager.EnableOutput(LogOutput::RingBuffer); } ImDebugger::~ImDebugger() { @@ -2308,6 +2365,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Lua Console", nullptr, &cfg_.luaConsoleOpen); + ImGui::MenuItem("Log", nullptr, &cfg_.logOpen); ImGui::MenuItem("Debug stats", nullptr, &cfg_.debugStatsOpen); ImGui::MenuItem("Struct viewer", nullptr, &cfg_.structViewerOpen); ImGui::MenuItem("Log channels", nullptr, &cfg_.logConfigOpen); @@ -2432,6 +2490,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu DrawLogConfig(cfg_); } + if (cfg_.logOpen) { + logWindow_.Draw(cfg_); + } + if (cfg_.displayOpen) { DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon()); } @@ -2645,6 +2707,7 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("internalsOpen", &internalsOpen, false); sync.Sync("sasAudioOpen", &sasAudioOpen, false); sync.Sync("logConfigOpen", &logConfigOpen, false); + sync.Sync("logOpen", &logOpen, false); sync.Sync("luaConsoleOpen", &luaConsoleOpen, false); sync.Sync("utilityModulesOpen", &utilityModulesOpen, false); sync.Sync("memDumpOpen", &memDumpOpen, false); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index d069feca16..388f6fbafb 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -83,6 +83,7 @@ struct ImConfig { bool internalsOpen; bool sasAudioOpen; bool logConfigOpen; + bool logOpen; bool utilityModulesOpen; bool atracToolOpen; bool memViewOpen[4]; @@ -150,6 +151,15 @@ private: bool setEditFocus_ = false; }; +class ImLogWindow { +public: + ImLogWindow() {} + void Draw(ImConfig &cfg); + +private: + bool AutoScroll = true; // Keep scrolling if already at the bottom. +}; + enum class ImCmd { NONE = 0, TRIGGER_FIND_POPUP, @@ -204,6 +214,7 @@ private: ImWatchWindow watchWindow_; ImAtracToolWindow atracToolWindow_; ImConsole luaConsole_; + ImLogWindow logWindow_; ImSnapshotState newSnapshot_; ImSnapshotState snapshot_; diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index 7a1cf348fc..edb41f24ad 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -733,7 +733,7 @@ Stereo rendering = Stereo-rendering Stretch = Sträck ut Texture Filter = Texturfilter Texture Filtering = Texturfiltrering -Texture replacement pack activated = Texturerersättningspaket aktiverat +Texture replacement pack activated = Texturersättningspaket aktiverat Texture upscaling = Texturuppskalning The chosen ZIP file doesn't contain a valid driver = Den valda ZIP-filen innehåller inte en giltig drivrutin Unlimited = Obegränsad