/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see .
*/
#include "PrecompiledHeader.h"
#include "DebuggerWindow.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "VMManager.h"
#include "QtHost.h"
#include "MainWindow.h"
DebuggerWindow::DebuggerWindow(QWidget* parent)
: QMainWindow(parent)
{
m_ui.setupUi(this);
// Easiest way to handle cross platform monospace fonts
// There are issues related to TabWidget -> Children font inheritance otherwise
#if defined(WIN32)
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 8pt 'Lucida Console'"));
#elif defined(__APPLE__)
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 10pt 'Monaco'"));
#else
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 8pt 'Monospace'"));
#endif
connect(m_ui.actionRun, &QAction::triggered, this, &DebuggerWindow::onRunPause);
connect(m_ui.actionStepInto, &QAction::triggered, this, &DebuggerWindow::onStepInto);
connect(m_ui.actionStepOver, &QAction::triggered, this, &DebuggerWindow::onStepOver);
connect(m_ui.actionStepOut, &QAction::triggered, this, &DebuggerWindow::onStepOut);
connect(m_ui.actionOnTop, &QAction::triggered, [this] { this->setWindowFlags(this->windowFlags() ^ Qt::WindowStaysOnTopHint); this->show(); });
connect(g_emu_thread, &EmuThread::onVMPaused, this, &DebuggerWindow::onVMStateChanged);
connect(g_emu_thread, &EmuThread::onVMResumed, this, &DebuggerWindow::onVMStateChanged);
onVMStateChanged(); // If we missed a state change while we weren't loaded
// We can't do this in the designer, but we want to right align the actionOnTop action in the toolbar
QWidget* spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui.toolBar->insertWidget(m_ui.actionOnTop, spacer);
m_cpuWidget_r5900 = new CpuWidget(this, r5900Debug);
m_cpuWidget_r3000 = new CpuWidget(this, r3000Debug);
m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900");
m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000");
CBreakPoints::SetUpdateHandler(std::bind(&DebuggerWindow::onBreakpointsChanged, this));
return;
}
DebuggerWindow::~DebuggerWindow() = default;
// There is no straightforward way to set the tab text to bold in Qt
// Sorry colour blind people, but this is the best we can do for now
void DebuggerWindow::setTabActiveStyle(BreakPointCpu enabledCpu)
{
m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r5900), (enabledCpu == BREAKPOINT_EE) ? Qt::red : this->palette().text().color());
m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r3000), (enabledCpu == BREAKPOINT_IOP) ? Qt::red : this->palette().text().color());
}
void DebuggerWindow::onVMStateChanged()
{
if (!QtHost::IsVMPaused())
{
m_ui.actionRun->setText(tr("Pause"));
m_ui.actionRun->setIcon(QIcon::fromTheme(QStringLiteral("pause-line")));
m_ui.actionStepInto->setEnabled(false);
m_ui.actionStepOver->setEnabled(false);
m_ui.actionStepOut->setEnabled(false);
setTabActiveStyle(BREAKPOINT_IOP_AND_EE);
}
else
{
m_ui.actionRun->setText(tr("Run"));
m_ui.actionRun->setIcon(QIcon::fromTheme(QStringLiteral("play-line")));
m_ui.actionStepInto->setEnabled(true);
m_ui.actionStepOver->setEnabled(true);
m_ui.actionStepOut->setEnabled(true);
// Switch to the CPU tab that triggered the breakpoint
// Also bold the tab text to indicate that a breakpoint was triggered
if (CBreakPoints::GetBreakpointTriggered())
{
const BreakPointCpu triggeredCpu = CBreakPoints::GetBreakpointTriggeredCpu();
setTabActiveStyle(triggeredCpu);
switch (triggeredCpu)
{
case BREAKPOINT_EE:
m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900);
break;
case BREAKPOINT_IOP:
m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000);
break;
default:
break;
}
Host::RunOnCPUThread([] {
CBreakPoints::ClearTemporaryBreakPoints();
CBreakPoints::SetBreakpointTriggered(false, BREAKPOINT_IOP_AND_EE);
// Our current PC is on a breakpoint.
// When we run the core again, we want to skip this breakpoint and run
CBreakPoints::SetSkipFirst(BREAKPOINT_EE, r5900Debug.getPC());
CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, r3000Debug.getPC());
});
}
}
return;
}
void DebuggerWindow::onRunPause()
{
g_emu_thread->setVMPaused(!QtHost::IsVMPaused());
}
void DebuggerWindow::onStepInto()
{
CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget());
currentCpu->onStepInto();
}
void DebuggerWindow::onStepOver()
{
CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget());
currentCpu->onStepOver();
}
void DebuggerWindow::onStepOut()
{
CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget());
currentCpu->onStepOut();
}
void DebuggerWindow::onBreakpointsChanged()
{
m_cpuWidget_r5900->reloadCPUWidgets();
m_cpuWidget_r3000->reloadCPUWidgets();
}