Enable ImDebugger everywhere, on top of the regular UI.

This commit is contained in:
Henrik Rydgård committed 2026-08-07 17:35:15 +02:00
1 parent ca3fc0fa9e
commit db8ff4391e
7 files changed
+311 -263

No files matched your search

+3
View File
@@ -48,6 +48,9 @@ public:
virtual bool AllowKeyboardNavigation() const { return true; }
// Process keyboard shortcuts even outside the game.
virtual InputMode PassInputToMapper() const { return InputMode::Keyboard; }
protected:
virtual void CreateViews() = 0;
virtual bool AllowFocusMovement() const { return true; }
+6 -2
View File
@@ -179,8 +179,12 @@ bool PortManager::Initialize(const unsigned int timeout) {
}
// Using Game ID & Player Name as default description for mapping
std::string gameID = g_paramSFO.GetDiscID();
m_defaultDesc = "PPSSPP:" + gameID + ":" + g_Config.sNickName; // Some routers may automatically prefixed it with "UPnP:"
if (PSP_IsInited()) {
std::string gameID = g_paramSFO.GetDiscID();
m_defaultDesc = "PPSSPP:" + gameID + ":" + g_Config.sNickName; // Some routers may automatically prefixed it with "UPnP:"
} else {
m_defaultDesc = "PPSSPP:at_menu:" + g_Config.sNickName;
}
freeUPNPDevlist(devlist);
+4 -165
View File
@@ -75,7 +75,6 @@ using namespace std::placeholders;
#include "Core/RetroAchievements.h"
#include "Core/SaveState.h"
#include "Core/Screenshot.h"
#include "UI/ImDebugger/ImDebugger.h"
#include "Core/HLE/__sceAudio.h"
#include "Core/HW/Display.h"
@@ -98,12 +97,6 @@ using namespace std::placeholders;
#include "UI/ChatScreen.h"
#include "UI/DebugOverlay.h"
#include "ext/imgui/imgui.h"
#include "ext/imgui/imgui_internal.h"
#include "ext/imgui/imgui_impl_thin3d.h"
#include "ext/imgui/imgui_impl_platform.h"
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
#include "Windows/MainWindow.h"
#endif
@@ -114,15 +107,6 @@ static AVIDump avi;
extern bool g_TakeScreenshot;
static void AssertCancelCallback(const char *message, void *userdata) {
NOTICE_LOG(Log::CPU, "Broke after assert: %s", message);
Core_Break(BreakReason::AssertChoice);
g_Config.bShowImDebugger = true;
EmuScreen *emuScreen = (EmuScreen *)userdata;
emuScreen->SendImDebuggerCommand(ImCommand{ ImCmd::SHOW_IN_CPU_DISASM, currentMIPS->pc });
}
// Handles control rotation due to internal screen rotation.
void EmuScreen::UpdatePSPButtons(uint32_t bitsToSet, uint32_t bitsToClear) {
if (!IsOnTop()) {
@@ -283,8 +267,6 @@ void EmuScreen::ProcessGameBoot(const Path &filename) {
break;
}
SetAssertCancelCallback(&AssertCancelCallback, this);
if (!g_Config.bShaderCache) {
// Only developers should ever see this.
g_OSD.Show(OSDType::MESSAGE_WARNING, "Shader cache is disabled (developer)");
@@ -454,11 +436,6 @@ void EmuScreen::bootComplete() {
EmuScreen::~EmuScreen() {
g_controlMapper.RemoveListener(this);
if (imguiInited_) {
ImGui_ImplThin3d_Shutdown();
ImGui::DestroyContext(ctx_);
}
std::string gameID = g_paramSFO.GetValueString("DISC_ID");
g_Config.TimeTracker().Stop(gameID);
@@ -533,9 +510,6 @@ void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) {
}
SetExtraAssertInfo(extraAssertInfoStr_.c_str());
// Make sure we re-enable keyboard mode if it was disabled by the dialog, and if needed.
lastImguiEnabled_ = false;
}
void EmuScreen::focusChanged(ScreenFocusChange focusChange) {
@@ -771,11 +745,6 @@ void EmuScreen::ProcessVKey(VirtKey virtKey, bool down) {
}
break;
case VIRTKEY_TOGGLE_DEBUGGER:
if (down) {
g_Config.bShowImDebugger = !g_Config.bShowImDebugger;
}
break;
case VIRTKEY_TOGGLE_TILT:
if (down) {
g_Config.bTiltInputEnabled = !g_Config.bTiltInputEnabled;
@@ -1160,27 +1129,12 @@ InputMode EmuScreen::PassInputToMapper() const {
return InputMode::None;
}
InputMode modes = InputMode::Keyboard | InputMode::Mouse | InputMode::Other | InputMode::ImDebuggerToggle;
if (g_Config.bShowImDebugger && imguiInited_) {
if (ImGui::GetIO().WantCaptureKeyboard) {
modes &= ~InputMode::Keyboard;
}
if (ImGui::GetIO().WantCaptureMouse) {
modes &= ~InputMode::Mouse;
}
return modes;
}
return modes;
return InputMode::Keyboard | InputMode::Mouse | InputMode::Other | InputMode::ImDebuggerToggle;
}
bool EmuScreen::key(const KeyInput &key) {
bool retval = UIScreen::key(key);
if (!retval && g_Config.bShowImDebugger && imguiInited_) {
ImGui_ImplPlatform_KeyEvent(key);
}
if (!retval && (key.flags & KeyInputFlags::DOWN) != 0 && UI::IsEscapeKey(key)) {
if (chatMenu_)
chatMenu_->Close();
@@ -1206,18 +1160,13 @@ bool EmuScreen::touch(const TouchInput &touch) {
}
if (touch.flags & TouchInputFlags::DOWN) {
if (!(g_Config.bShowImDebugger && imguiInited_) && !ignoreGamepad) {
if (!g_Config.bShowImDebugger && !ignoreGamepad) {
// This just prevents the gamepad from timing out.
GamepadTouch();
}
}
if (g_Config.bShowImDebugger && imguiInited_) {
ImGui_ImplPlatform_TouchEvent(touch);
if (!ImGui::GetIO().WantCaptureMouse) {
return UIScreen::touch(touch);
}
} else if (g_Config.bMouseControl && !(touch.flags & TouchInputFlags::UP) && (touch.flags & TouchInputFlags::MOUSE)) {
if (g_Config.bMouseControl && !(touch.flags & TouchInputFlags::UP) && (touch.flags & TouchInputFlags::MOUSE)) {
// don't do anything as the mouse pointer is hidden in this case.
// But we let touch-up events through to avoid getting stuck if the user toggles mouse control.
} else {
@@ -1385,20 +1334,10 @@ void EmuScreen::deviceLost() {
}
UIScreen::deviceLost();
if (imguiInited_) {
if (imDebugger_) {
imDebugger_->DeviceLost();
}
ImGui_ImplThin3d_DestroyDeviceObjects();
}
}
void EmuScreen::deviceRestored(Draw::DrawContext *draw) {
UIScreen::deviceRestored(draw);
if (imguiInited_) {
ImGui_ImplThin3d_CreateDeviceObjects(draw);
}
}
void EmuScreen::OnDevTools(UI::EventParams &params) {
@@ -1707,7 +1646,6 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
Draw::BackendState state = draw->GetCurrentBackendState();
if (!(mode & ScreenRenderMode::TOP)) {
renderImDebugger();
// We're in run-behind mode, but we don't want to draw chat, debug UI and stuff. We do draw the imdebugger though.
// So, darken and bail here.
// Reset viewport/scissor to be sure.
@@ -1735,7 +1673,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
SetVRAppMode(screenManager()->topScreen() == this ? VRAppMode::VR_GAME_MODE : VRAppMode::VR_DIALOG_MODE);
}
renderImDebugger();
// renderImDebugger();
return screenRenderFlags;
}
@@ -1859,108 +1797,9 @@ ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) {
}
}
runImDebugger();
return flags;
}
void EmuScreen::runImDebugger() {
if (!lastImguiEnabled_ && g_Config.bShowImDebugger) {
#if !defined(MOBILE_DEVICE)
// On mobile devices (specifically iOS) we don't want to pop the keyboard
// on activating imgui. Instead, we should do it when a text edit field in imgui gets focus,
// although we'll still have ugly overlap problems.
System_NotifyUIEvent(UIEventNotification::TEXT_GOTFOCUS);
#endif
VERBOSE_LOG(Log::System, "activating keyboard");
} else if (lastImguiEnabled_ && !g_Config.bShowImDebugger) {
System_NotifyUIEvent(UIEventNotification::TEXT_LOSTFOCUS);
VERBOSE_LOG(Log::System, "deactivating keyboard");
}
lastImguiEnabled_ = g_Config.bShowImDebugger;
if (g_Config.bShowImDebugger) {
Draw::DrawContext *draw = screenManager()->getDrawContext();
if (!imguiInited_) {
// TODO: Do this only on demand.
IMGUI_CHECKVERSION();
ctx_ = ImGui::CreateContext();
ImGui_ImplPlatform_Init(GetSysDirectory(DIRECTORY_SYSTEM) / "imgui.ini");
imDebugger_ = std::make_unique<ImDebugger>();
// Read the TTF font
size_t propSize = 0;
const uint8_t *propFontData = g_VFS.ReadFile("Roboto_Condensed-Regular.ttf", &propSize);
size_t fixedSize = 0;
const uint8_t *fixedFontData = g_VFS.ReadFile("Inconsolata-Regular.ttf", &fixedSize);
// This call works even if fontData is nullptr, in which case the font just won't get loaded.
// This takes ownership of the font array.
ImGui_ImplThin3d_Init(draw, propFontData, propSize, fixedFontData, fixedSize);
imguiInited_ = true;
}
if (PSP_IsInited()) {
_dbg_assert_(imDebugger_);
ImGui_ImplPlatform_NewFrame();
ImGui_ImplThin3d_NewFrame(draw, ui_draw2d.GetDrawMatrix());
ImGui::NewFrame();
if (imCmd_.cmd != ImCmd::NONE) {
imDebugger_->PostCmd(imCmd_);
imCmd_.cmd = ImCmd::NONE;
}
// Update keyboard modifiers.
auto &io = ImGui::GetIO();
KeyModifier modifiers = NativeGetKeyModifiers();
const bool keyCtrl = (modifiers & KeyModifier::LCTRL) || (modifiers & KeyModifier::RCTRL);
const bool keyShift = (modifiers & KeyModifier::LSHIFT) || (modifiers & KeyModifier::RSHIFT);
const bool keyAlt = (modifiers & KeyModifier::LALT) || (modifiers & KeyModifier::RALT);
io.AddKeyEvent(ImGuiMod_Ctrl, keyCtrl);
io.AddKeyEvent(ImGuiMod_Shift, keyShift);
io.AddKeyEvent(ImGuiMod_Alt, keyAlt);
// io.AddKeyEvent(ImGuiMod_Super, e.key.super);
ImGuiID dockID = ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);
ImGuiDockNode* node = ImGui::DockBuilderGetCentralNode(dockID);
// Not elegant! But don't know how else to pass through the bounds, without making a mess.
Bounds centralNode(node->Pos.x, node->Pos.y, node->Size.x, node->Size.y);
SetOverrideScreenFrame(&centralNode);
if (!io.WantCaptureKeyboard) {
// Draw a focus rectangle to indicate inputs will be passed through.
ImGui::GetBackgroundDrawList()->AddRect
(
node->Pos,
{ node->Pos.x + node->Size.x, node->Pos.y + node->Size.y },
IM_COL32(255, 255, 255, 90),
0.f,
ImDrawFlags_None,
1.f
);
}
imDebugger_->Frame(currentDebugMIPS, gpu, draw);
// Convert to drawlists.
ImGui::Render();
}
}
}
void EmuScreen::renderImDebugger() {
if (g_Config.bShowImDebugger) {
Draw::DrawContext *draw = screenManager()->getDrawContext();
if (PSP_IsInited() && imDebugger_) {
ImGui_ImplThin3d_RenderDrawData(ImGui::GetDrawData(), draw);
}
}
}
bool EmuScreen::hasVisibleUI() {
// Regular but uncommon UI.
if (saveStatePreview_->GetVisibility() != UI::V_GONE || loadingSpinner_->GetVisibility() == UI::V_VISIBLE)
-16
View File
@@ -61,10 +61,6 @@ public:
void deviceLost() override;
void deviceRestored(Draw::DrawContext *draw) override;
void SendImDebuggerCommand(const ImCommand &command) {
imCmd_ = command;
}
protected:
void darken();
void focusChanged(ScreenFocusChange focusChange) override;
@@ -91,9 +87,6 @@ private:
void bootComplete();
bool hasVisibleUI();
void renderUI();
void runImDebugger();
void renderImDebugger();
void AutoLoadSaveState();
bool checkPowerDown();
@@ -141,18 +134,9 @@ private:
std::string extraAssertInfoStr_;
std::unique_ptr<ImDebugger> imDebugger_;
ImCommand imCmd_{}; // needed to buffer commands in case imgui wasn't created yet.
bool imguiInited_ = false;
bool lastImguiEnabled_ = false;
std::mutex queuedVirtKeysLock_;
std::vector<std::pair<VirtKey, bool>> queuedVirtKeys_;
ImGuiContext *ctx_ = nullptr;
bool frameStep_ = false;
#ifndef MOBILE_DEVICE
bool startDumping_ = false;
+84 -76
View File
@@ -2363,19 +2363,21 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
// Watch the step counters to figure out when to update things.
if (lastCpuStepCount_ != Core_GetSteppingCounter()) {
lastCpuStepCount_ = Core_GetSteppingCounter();
snapshot_ = newSnapshot_; // Compare against the previous snapshot.
Snapshot(currentMIPS);
disasm_.NotifyStep();
}
if (PSP_IsInited()) {
if (lastCpuStepCount_ != Core_GetSteppingCounter()) {
lastCpuStepCount_ = Core_GetSteppingCounter();
snapshot_ = newSnapshot_; // Compare against the previous snapshot.
Snapshot(currentMIPS);
disasm_.NotifyStep();
}
if (lastGpuStepCount_ != GPUStepping::GetSteppingCounter()) {
// A GPU step has happened since last time. This means that we should re-center the cursor.
// Snapshot();
lastGpuStepCount_ = GPUStepping::GetSteppingCounter();
SnapshotGPU(gpuDebug);
geDebugger_.NotifyStep();
if (gpuDebug && lastGpuStepCount_ != GPUStepping::GetSteppingCounter()) {
// A GPU step has happened since last time. This means that we should re-center the cursor.
// Snapshot();
lastGpuStepCount_ = GPUStepping::GetSteppingCounter();
SnapshotGPU(gpuDebug);
geDebugger_.NotifyStep();
}
}
ImControl control{};
@@ -2645,12 +2647,14 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
atracToolWindow_.Draw(cfg_);
}
if (cfg_.framebuffersOpen) {
DrawFramebuffersWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
}
if (gpuDebug) {
if (cfg_.framebuffersOpen) {
DrawFramebuffersWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
}
if (cfg_.texturesOpen) {
DrawTexturesWindow(cfg_, gpuDebug->GetTextureCacheCommon());
if (cfg_.texturesOpen) {
DrawTexturesWindow(cfg_, gpuDebug->GetTextureCacheCommon());
}
}
if (cfg_.logConfigOpen) {
@@ -2661,78 +2665,82 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
logWindow_.Draw(cfg_);
}
if (cfg_.jitViewerOpen) {
jitViewer_.Draw(cfg_, control);
}
if (PSP_IsInited()) {
if (cfg_.jitViewerOpen) {
jitViewer_.Draw(cfg_, control);
}
if (cfg_.displayOpen) {
DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
}
if (cfg_.displayOpen) {
DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
}
if (cfg_.debugStatsOpen) {
DrawDebugStatsWindow(cfg_);
if (cfg_.debugStatsOpen) {
DrawDebugStatsWindow(cfg_);
}
}
if (cfg_.structViewerOpen) {
structViewer_.Draw(cfg_, control, mipsDebug);
}
if (cfg_.paramSFOOpen) {
DrawParamSFO(cfg_, control);
}
if (cfg_.geDebuggerOpen) {
geDebugger_.Draw(cfg_, control, gpuDebug, draw);
}
if (cfg_.geStateOpen) {
geStateWindow_.Draw(cfg_, control, gpuDebug);
}
if (cfg_.geVertsOpen) {
DrawImGeVertsWindow(cfg_, control, gpuDebug);
}
if (cfg_.schedulerOpen) {
DrawSchedulerView(cfg_);
}
if (cfg_.timeOpen) {
DrawTimeView(cfg_);
}
if (cfg_.pixelViewerOpen) {
pixelViewer_.Draw(cfg_, control, gpuDebug, draw);
}
if (cfg_.memDumpOpen) {
memDumpWindow_.Draw(cfg_, mipsDebug);
}
if (cfg_.watchOpen) {
watchWindow_.Draw(cfg_, control, mipsDebug);
}
for (int i = 0; i < 4; i++) {
if (cfg_.memViewOpen[i]) {
mem_[i].Draw(mipsDebug, cfg_, control, i);
if (PSP_IsInited()) {
if (cfg_.paramSFOOpen) {
DrawParamSFO(cfg_, control);
}
}
if (cfg_.socketsOpen) {
DrawSockets(cfg_);
}
if (cfg_.geDebuggerOpen) {
geDebugger_.Draw(cfg_, control, gpuDebug, draw);
}
if (cfg_.npOpen) {
DrawNp(cfg_);
}
if (cfg_.geStateOpen) {
geStateWindow_.Draw(cfg_, control, gpuDebug);
}
if (cfg_.adhocOpen) {
DrawAdhoc(cfg_);
}
if (cfg_.geVertsOpen) {
DrawImGeVertsWindow(cfg_, control, gpuDebug);
}
if (cfg_.apctlOpen) {
DrawApctl(cfg_);
if (cfg_.schedulerOpen) {
DrawSchedulerView(cfg_);
}
if (cfg_.timeOpen) {
DrawTimeView(cfg_);
}
if (cfg_.pixelViewerOpen) {
pixelViewer_.Draw(cfg_, control, gpuDebug, draw);
}
if (cfg_.memDumpOpen) {
memDumpWindow_.Draw(cfg_, mipsDebug);
}
if (cfg_.watchOpen) {
watchWindow_.Draw(cfg_, control, mipsDebug);
}
for (int i = 0; i < 4; i++) {
if (cfg_.memViewOpen[i]) {
mem_[i].Draw(mipsDebug, cfg_, control, i);
}
}
if (cfg_.socketsOpen) {
DrawSockets(cfg_);
}
if (cfg_.npOpen) {
DrawNp(cfg_);
}
if (cfg_.adhocOpen) {
DrawAdhoc(cfg_);
}
if (cfg_.apctlOpen) {
DrawApctl(cfg_);
}
}
if (cfg_.internalsOpen) {
+12 -2
View File
@@ -436,14 +436,24 @@ void ViewSearch::ApplySearchFilter(UI::ViewGroup *viewGroup, bool setKeyboardFoc
}
}
static bool IsSearchableChar(int unichar) {
// 127 gets produced from Ctrl+Backspace on Windows for some reason.
return unichar >= 0x20 && unichar != 127;
}
static bool IsFirstSearchableChar(int unichar) {
// Don't allow spaces as the first character, it looks confusing (empty search field)
return IsSearchableChar(unichar) && unichar != ' ' && unichar != '`' && unichar != '.' && unichar != ',';
}
bool ViewSearch::Key(UI::ViewGroup *viewGroup, const KeyInput &input) {
bool retval = false;
// Only one is visible at a time, so we can just grab all Char input.
if (input.flags & KeyInputFlags::CHAR) {
const int unichar = input.keyCode;
if (unichar >= 0x20 && unichar != 127) { // 127 gets produced from Ctrl+Backspace on Windows for some reason.
if (IsSearchableChar(unichar)) {
// Don't allow spaces as the first character, it looks confusing (empty search field)
if (searchFilter.empty() && unichar == ' ') {
if (searchFilter.empty() && !IsFirstSearchableChar(unichar)) {
return false;
}
+202 -2
View File
@@ -38,6 +38,13 @@
#include <mutex>
#include <thread>
#include "ext/imgui/imgui.h"
#include "ext/imgui/imgui_internal.h"
#include "ext/imgui/imgui_impl_thin3d.h"
#include "ext/imgui/imgui_impl_platform.h"
#if defined(_WIN32)
#include "Windows/WindowsAudio.h"
#include "Windows/MainWindow.h"
@@ -121,6 +128,7 @@
#include "GPU/GPUCommon.h"
#include "GPU/Common/PresentationCommon.h"
#include "UI/ImDebugger/ImDebugger.h"
#include "UI/AudioCommon.h"
#include "UI/Background.h"
#include "UI/BackgroundAudio.h"
@@ -139,7 +147,6 @@
#include "UI/Theme.h"
#include "UI/PauseScreen.h"
#include "UI/UIAtlas.h"
#if PPSSPP_PLATFORM(UWP)
#include <dwrite_3.h>
#include "UWP/UWPHelpers/InputHelpers.h"
@@ -202,6 +209,32 @@ static bool g_nativeMainThreadReady = false;
static std::mutex g_inputEventQueueLock;
static std::vector<QueuedEvent> g_inputEventQueue;
static std::unique_ptr<ImDebugger> imDebugger_;
static ImCommand imCmd_{}; // needed to buffer commands in case imgui wasn't created yet.
static bool imguiInited_ = false;
static bool lastImguiEnabled_ = false;
static ImGuiContext *ctx_ = nullptr;
class GlobalListener : public ControlListener {
virtual void OnVKey(VirtKey vkey, bool down) {
switch (vkey) {
case VIRTKEY_TOGGLE_DEBUGGER:
if (down) {
g_Config.bShowImDebugger = !g_Config.bShowImDebugger;
}
break;
}
}
};
GlobalListener g_globalListener;
static void AssertCancelCallback(const char *message, void *userdata) {
NOTICE_LOG(Log::CPU, "Broke after assert: %s", message);
Core_Break(BreakReason::AssertChoice);
g_Config.bShowImDebugger = true;
imCmd_ = ImCommand{ImCmd::SHOW_IN_CPU_DISASM, currentMIPS->pc};
}
static void ApplyAchievementsRuntimeSettings() {
auto *client = Achievements::GetClient();
if (!client) {
@@ -261,6 +294,101 @@ static void RunAchievementsOverrideUpdate(std::function<void()> func) {
}
}
void runImDebugger(Draw::DrawContext *draw) {
bool lastImguiEnabled_ = false; // temp
if (lastImguiEnabled_ && g_Config.bShowImDebugger) {
#if !defined(MOBILE_DEVICE)
// On mobile devices (specifically iOS) we don't want to pop the keyboard
// on activating imgui. Instead, we should do it when a text edit field in imgui gets focus,
// although we'll still have ugly overlap problems.
System_NotifyUIEvent(UIEventNotification::TEXT_GOTFOCUS);
#endif
VERBOSE_LOG(Log::System, "activating keyboard");
} else if (lastImguiEnabled_ && !g_Config.bShowImDebugger) {
System_NotifyUIEvent(UIEventNotification::TEXT_LOSTFOCUS);
VERBOSE_LOG(Log::System, "deactivating keyboard");
}
lastImguiEnabled_ = g_Config.bShowImDebugger;
if (g_Config.bShowImDebugger) {
if (!imguiInited_) {
// TODO: Do this only on demand.
IMGUI_CHECKVERSION();
ctx_ = ImGui::CreateContext();
ImGui_ImplPlatform_Init(GetSysDirectory(DIRECTORY_SYSTEM) / "imgui.ini");
imDebugger_ = std::make_unique<ImDebugger>();
// Read the TTF font
size_t propSize = 0;
const uint8_t *propFontData = g_VFS.ReadFile("Roboto_Condensed-Regular.ttf", &propSize);
size_t fixedSize = 0;
const uint8_t *fixedFontData = g_VFS.ReadFile("Inconsolata-Regular.ttf", &fixedSize);
// This call works even if fontData is nullptr, in which case the font just won't get loaded.
// This takes ownership of the font array.
ImGui_ImplThin3d_Init(draw, propFontData, propSize, fixedFontData, fixedSize);
imguiInited_ = true;
}
_dbg_assert_(imDebugger_);
ImGui_ImplPlatform_NewFrame();
ImGui_ImplThin3d_NewFrame(draw, ui_draw2d.GetDrawMatrix());
ImGui::NewFrame();
if (imCmd_.cmd != ImCmd::NONE) {
imDebugger_->PostCmd(imCmd_);
imCmd_.cmd = ImCmd::NONE;
}
// Update keyboard modifiers.
auto &io = ImGui::GetIO();
KeyModifier modifiers = NativeGetKeyModifiers();
const bool keyCtrl = (modifiers & KeyModifier::LCTRL) || (modifiers & KeyModifier::RCTRL);
const bool keyShift = (modifiers & KeyModifier::LSHIFT) || (modifiers & KeyModifier::RSHIFT);
const bool keyAlt = (modifiers & KeyModifier::LALT) || (modifiers & KeyModifier::RALT);
io.AddKeyEvent(ImGuiMod_Ctrl, keyCtrl);
io.AddKeyEvent(ImGuiMod_Shift, keyShift);
io.AddKeyEvent(ImGuiMod_Alt, keyAlt);
// io.AddKeyEvent(ImGuiMod_Super, e.key.super);
ImGuiID dockID = ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);
ImGuiDockNode* node = ImGui::DockBuilderGetCentralNode(dockID);
// Not elegant! But don't know how else to pass through the bounds, without making a mess.
Bounds centralNode(node->Pos.x, node->Pos.y, node->Size.x, node->Size.y);
SetOverrideScreenFrame(&centralNode);
if (!io.WantCaptureKeyboard) {
// Draw a focus rectangle to indicate inputs will be passed through.
ImGui::GetBackgroundDrawList()->AddRect
(
node->Pos,
{node->Pos.x + node->Size.x, node->Pos.y + node->Size.y},
IM_COL32(255, 255, 255, 90),
0.f,
ImDrawFlags_None,
1.f
);
}
imDebugger_->Frame(currentDebugMIPS, gpu, draw);
// Convert to drawlists.
ImGui::Render();
}
}
void renderImDebugger(Draw::DrawContext *draw) {
if (g_Config.bShowImDebugger) {
if (imDebugger_) {
ImGui_ImplThin3d_RenderDrawData(ImGui::GetDrawData(), draw);
}
}
}
std::vector<std::function<void()>> g_pendingClosures;
AudioBackend *g_audioBackend = nullptr;
@@ -395,6 +523,10 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
g_Config.Init();
g_controlMapper.AddListener(&g_globalListener);
SetAssertCancelCallback(&AssertCancelCallback, nullptr);
IncrementDebugCounter(DebugCounter::APP_BOOT);
// Probably an excessive timeout. it only causes delays on shutdown, though.
@@ -851,6 +983,11 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
gpu->DeviceRestore(g_draw);
}
if (imguiInited_) {
ImGui_ImplThin3d_CreateDeviceObjects(g_draw);
}
INFO_LOG(Log::System, "NativeInitGraphics completed");
return true;
@@ -922,6 +1059,15 @@ void NativeShutdownGraphics(GraphicsContext *graphicsContext) {
gpu->DeviceLost();
}
if (imguiInited_) {
if (imDebugger_) {
imDebugger_->DeviceLost();
}
ImGui_ImplThin3d_DestroyDeviceObjects();
ImGui_ImplThin3d_Shutdown();
ImGui::DestroyContext(ctx_);
}
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
if (winCamera) {
winCamera->waitShutDown();
@@ -1038,8 +1184,47 @@ void NativeFrame(GraphicsContext *graphicsContext) {
}
for (auto &event : inputEvents) {
g_screenManager->ProcessInputEvent(event);
bool filterTouch = false;
bool filterKey = false;
if (g_Config.bShowImDebugger && imguiInited_) {
// Let ImGui handle input if it's active.
if (ImGui::GetIO().WantCaptureMouse) {
filterTouch = true;
}
if (ImGui::GetIO().WantCaptureKeyboard) {
filterKey = true;
}
}
switch (event.type) {
case QueuedEventType::KEY:
// Let through up events to avoid stuck keys.
if (!filterKey || (event.key.flags & KeyInputFlags::UP)) {
g_screenManager->ProcessInputEvent(event);
}
break;
case QueuedEventType::TOUCH:
if (!filterTouch) {
g_screenManager->ProcessInputEvent(event);
}
break;
default:
g_screenManager->ProcessInputEvent(event);
break;
}
if (g_Config.bShowImDebugger && imguiInited_) {
switch (event.type) {
case QueuedEventType::KEY:
ImGui_ImplPlatform_KeyEvent(event.key);
break;
case QueuedEventType::TOUCH:
ImGui_ImplPlatform_TouchEvent(event.touch);
break;
}
}
}
g_screenManager->Update(); // This must happen *after* input event processing!
// Do this after g_screenManager.update() so we can receive setting changes before rendering.
@@ -1111,6 +1296,8 @@ void NativeFrame(GraphicsContext *graphicsContext) {
ui_draw2d.PopDrawMatrix();
runImDebugger(g_draw);
renderImDebugger(g_draw);
g_draw->EndFrame();
// This, between EndFrame and Present, is where we should actually wait to do present time management.
@@ -1379,6 +1566,16 @@ bool NativeKey(const KeyInput &key) {
// Filtering, detailed rules needed for good imgui behavior without having to ask the screen about what to do.
InputMode inputMode = g_screenManager->PassInputToMapper();
if (g_Config.bShowImDebugger && imguiInited_) {
if (ImGui::GetIO().WantCaptureKeyboard) {
inputMode &= ~InputMode::Keyboard;
}
if (ImGui::GetIO().WantCaptureMouse) {
inputMode &= ~InputMode::Mouse;
}
}
bool passKeyThrough = false;
if (inputMode != InputMode::None) {
if ((inputMode & InputMode::ImDebuggerToggle) && (key.flags & (KeyInputFlags::UP | KeyInputFlags::DOWN))) {
@@ -1394,6 +1591,7 @@ bool NativeKey(const KeyInput &key) {
}
}
}
if (key.deviceId == DEVICE_ID_MOUSE) {
if (inputMode & InputMode::Mouse) {
passKeyThrough = true;
@@ -1637,6 +1835,8 @@ void NativeShutdown() {
ClearAchievementsHostOverride();
g_nativeMainThreadReady = false;
g_controlMapper.RemoveListener(&g_globalListener);
Achievements::Shutdown();
if (g_Config.bAchievementsEnable) {