From c76cca874b0fc47e050a0466a704ecfc5aff2f7f Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Sat, 7 Dec 2024 17:36:29 +0000 Subject: [PATCH] Debugger: Redesign UI based on KDDockWidgets --- pcsx2-qt/CMakeLists.txt | 52 +- .../{ => Breakpoints}/BreakpointDialog.cpp | 0 .../{ => Breakpoints}/BreakpointDialog.h | 4 +- .../{ => Breakpoints}/BreakpointDialog.ui | 0 .../BreakpointModel.cpp | 0 .../{Models => Breakpoints}/BreakpointModel.h | 0 .../Debugger/Breakpoints/BreakpointWidget.cpp | 183 +++++ .../Debugger/Breakpoints/BreakpointWidget.h | 41 + .../Debugger/Breakpoints/BreakpointWidget.ui | 43 + pcsx2-qt/Debugger/CpuWidget.cpp | 732 ------------------ pcsx2-qt/Debugger/CpuWidget.h | 97 --- pcsx2-qt/Debugger/CpuWidget.ui | 445 ----------- pcsx2-qt/Debugger/DebuggerSettingsManager.cpp | 6 +- pcsx2-qt/Debugger/DebuggerSettingsManager.h | 4 +- pcsx2-qt/Debugger/DebuggerWidget.cpp | 31 + pcsx2-qt/Debugger/DebuggerWidget.h | 28 + pcsx2-qt/Debugger/DebuggerWindow.cpp | 50 +- pcsx2-qt/Debugger/DebuggerWindow.h | 13 +- pcsx2-qt/Debugger/DebuggerWindow.ui | 79 +- pcsx2-qt/Debugger/DisassemblyWidget.cpp | 90 ++- pcsx2-qt/Debugger/DisassemblyWidget.h | 13 +- pcsx2-qt/Debugger/DockManager.cpp | 108 +++ pcsx2-qt/Debugger/DockManager.h | 42 + .../{ => Memory}/MemorySearchWidget.cpp | 38 +- .../{ => Memory}/MemorySearchWidget.h | 26 +- .../{ => Memory}/MemorySearchWidget.ui | 0 .../{ => Memory}/MemoryViewWidget.cpp | 38 +- .../Debugger/{ => Memory}/MemoryViewWidget.h | 13 +- .../Debugger/{ => Memory}/MemoryViewWidget.ui | 3 + .../SavedAddressesModel.cpp | 0 .../{Models => Memory}/SavedAddressesModel.h | 0 .../Debugger/Memory/SavedAddressesWidget.cpp | 158 ++++ .../Debugger/Memory/SavedAddressesWidget.h | 29 + .../Debugger/Memory/SavedAddressesWidget.ui | 43 + pcsx2-qt/Debugger/RegisterWidget.cpp | 99 ++- pcsx2-qt/Debugger/RegisterWidget.h | 11 +- pcsx2-qt/Debugger/RegisterWidget.ui | 6 +- pcsx2-qt/Debugger/{Models => }/StackModel.cpp | 0 pcsx2-qt/Debugger/{Models => }/StackModel.h | 0 pcsx2-qt/Debugger/StackWidget.cpp | 76 ++ pcsx2-qt/Debugger/StackWidget.h | 26 + pcsx2-qt/Debugger/StackWidget.ui | 43 + .../Debugger/SymbolTree/SymbolTreeWidget.ui | 4 +- .../Debugger/SymbolTree/SymbolTreeWidgets.cpp | 32 +- .../Debugger/SymbolTree/SymbolTreeWidgets.h | 10 +- .../Debugger/{Models => }/ThreadModel.cpp | 0 pcsx2-qt/Debugger/{Models => }/ThreadModel.h | 0 pcsx2-qt/Debugger/ThreadWidget.cpp | 75 ++ pcsx2-qt/Debugger/ThreadWidget.h | 28 + pcsx2-qt/Debugger/ThreadWidget.ui | 43 + pcsx2-qt/MainWindow.cpp | 5 + pcsx2-qt/QtHost.h | 2 +- pcsx2-qt/pcsx2-qt.vcxproj | 80 +- pcsx2-qt/pcsx2-qt.vcxproj.filters | 130 +++- 54 files changed, 1475 insertions(+), 1604 deletions(-) rename pcsx2-qt/Debugger/{ => Breakpoints}/BreakpointDialog.cpp (100%) rename pcsx2-qt/Debugger/{ => Breakpoints}/BreakpointDialog.h (95%) rename pcsx2-qt/Debugger/{ => Breakpoints}/BreakpointDialog.ui (100%) rename pcsx2-qt/Debugger/{Models => Breakpoints}/BreakpointModel.cpp (100%) rename pcsx2-qt/Debugger/{Models => Breakpoints}/BreakpointModel.h (100%) create mode 100644 pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.cpp create mode 100644 pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.h create mode 100644 pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.ui delete mode 100644 pcsx2-qt/Debugger/CpuWidget.cpp delete mode 100644 pcsx2-qt/Debugger/CpuWidget.h delete mode 100644 pcsx2-qt/Debugger/CpuWidget.ui create mode 100644 pcsx2-qt/Debugger/DebuggerWidget.cpp create mode 100644 pcsx2-qt/Debugger/DebuggerWidget.h create mode 100644 pcsx2-qt/Debugger/DockManager.cpp create mode 100644 pcsx2-qt/Debugger/DockManager.h rename pcsx2-qt/Debugger/{ => Memory}/MemorySearchWidget.cpp (95%) rename pcsx2-qt/Debugger/{ => Memory}/MemorySearchWidget.h (90%) rename pcsx2-qt/Debugger/{ => Memory}/MemorySearchWidget.ui (100%) rename pcsx2-qt/Debugger/{ => Memory}/MemoryViewWidget.cpp (96%) rename pcsx2-qt/Debugger/{ => Memory}/MemoryViewWidget.h (93%) rename pcsx2-qt/Debugger/{ => Memory}/MemoryViewWidget.ui (81%) rename pcsx2-qt/Debugger/{Models => Memory}/SavedAddressesModel.cpp (100%) rename pcsx2-qt/Debugger/{Models => Memory}/SavedAddressesModel.h (100%) create mode 100644 pcsx2-qt/Debugger/Memory/SavedAddressesWidget.cpp create mode 100644 pcsx2-qt/Debugger/Memory/SavedAddressesWidget.h create mode 100644 pcsx2-qt/Debugger/Memory/SavedAddressesWidget.ui rename pcsx2-qt/Debugger/{Models => }/StackModel.cpp (100%) rename pcsx2-qt/Debugger/{Models => }/StackModel.h (100%) create mode 100644 pcsx2-qt/Debugger/StackWidget.cpp create mode 100644 pcsx2-qt/Debugger/StackWidget.h create mode 100644 pcsx2-qt/Debugger/StackWidget.ui rename pcsx2-qt/Debugger/{Models => }/ThreadModel.cpp (100%) rename pcsx2-qt/Debugger/{Models => }/ThreadModel.h (100%) create mode 100644 pcsx2-qt/Debugger/ThreadWidget.cpp create mode 100644 pcsx2-qt/Debugger/ThreadWidget.h create mode 100644 pcsx2-qt/Debugger/ThreadWidget.ui diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index 2d8a1af252..1bdff961fc 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -158,37 +158,48 @@ target_sources(pcsx2-qt PRIVATE Debugger/AnalysisOptionsDialog.cpp Debugger/AnalysisOptionsDialog.h Debugger/AnalysisOptionsDialog.ui - Debugger/CpuWidget.cpp - Debugger/CpuWidget.h - Debugger/CpuWidget.ui Debugger/DebuggerSettingsManager.cpp Debugger/DebuggerSettingsManager.h + Debugger/DebuggerWidget.cpp + Debugger/DebuggerWidget.h Debugger/DebuggerWindow.cpp Debugger/DebuggerWindow.h Debugger/DebuggerWindow.ui Debugger/DisassemblyWidget.cpp Debugger/DisassemblyWidget.h Debugger/DisassemblyWidget.ui - Debugger/MemorySearchWidget.cpp - Debugger/MemorySearchWidget.h - Debugger/MemorySearchWidget.ui - Debugger/MemoryViewWidget.cpp - Debugger/MemoryViewWidget.h - Debugger/MemoryViewWidget.ui + Debugger/DockManager.cpp + Debugger/DockManager.h Debugger/RegisterWidget.cpp Debugger/RegisterWidget.h Debugger/RegisterWidget.ui - Debugger/BreakpointDialog.cpp - Debugger/BreakpointDialog.h - Debugger/BreakpointDialog.ui - Debugger/Models/BreakpointModel.cpp - Debugger/Models/BreakpointModel.h - Debugger/Models/ThreadModel.cpp - Debugger/Models/ThreadModel.h - Debugger/Models/StackModel.cpp - Debugger/Models/StackModel.h - Debugger/Models/SavedAddressesModel.cpp - Debugger/Models/SavedAddressesModel.h + Debugger/StackModel.cpp + Debugger/StackModel.h + Debugger/StackWidget.cpp + Debugger/StackWidget.h + Debugger/ThreadModel.cpp + Debugger/ThreadModel.h + Debugger/ThreadWidget.cpp + Debugger/ThreadWidget.h + Debugger/Breakpoints/BreakpointDialog.cpp + Debugger/Breakpoints/BreakpointDialog.h + Debugger/Breakpoints/BreakpointDialog.ui + Debugger/Breakpoints/BreakpointModel.cpp + Debugger/Breakpoints/BreakpointModel.h + Debugger/Breakpoints/BreakpointWidget.cpp + Debugger/Breakpoints/BreakpointWidget.h + Debugger/Breakpoints/BreakpointWidget.ui + Debugger/Memory/MemorySearchWidget.cpp + Debugger/Memory/MemorySearchWidget.h + Debugger/Memory/MemorySearchWidget.ui + Debugger/Memory/MemoryViewWidget.cpp + Debugger/Memory/MemoryViewWidget.h + Debugger/Memory/MemoryViewWidget.ui + Debugger/Memory/SavedAddressesModel.cpp + Debugger/Memory/SavedAddressesModel.h + Debugger/Memory/SavedAddressesWidget.cpp + Debugger/Memory/SavedAddressesWidget.h + Debugger/Memory/SavedAddressesWidget.ui Debugger/SymbolTree/NewSymbolDialogs.cpp Debugger/SymbolTree/NewSymbolDialogs.h Debugger/SymbolTree/NewSymbolDialog.ui @@ -232,6 +243,7 @@ target_link_libraries(pcsx2-qt PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets + KDAB::kddockwidgets ) # Our Qt builds may have exceptions on, so force them off. diff --git a/pcsx2-qt/Debugger/BreakpointDialog.cpp b/pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.cpp similarity index 100% rename from pcsx2-qt/Debugger/BreakpointDialog.cpp rename to pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.cpp diff --git a/pcsx2-qt/Debugger/BreakpointDialog.h b/pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.h similarity index 95% rename from pcsx2-qt/Debugger/BreakpointDialog.h rename to pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.h index 61fc3fea1c..d1b033e20d 100644 --- a/pcsx2-qt/Debugger/BreakpointDialog.h +++ b/pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.h @@ -5,9 +5,9 @@ #include "ui_BreakpointDialog.h" -#include "DebugTools/Breakpoints.h" +#include "BreakpointModel.h" -#include "Models/BreakpointModel.h" +#include "DebugTools/Breakpoints.h" #include <QtWidgets/QDialog> diff --git a/pcsx2-qt/Debugger/BreakpointDialog.ui b/pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.ui similarity index 100% rename from pcsx2-qt/Debugger/BreakpointDialog.ui rename to pcsx2-qt/Debugger/Breakpoints/BreakpointDialog.ui diff --git a/pcsx2-qt/Debugger/Models/BreakpointModel.cpp b/pcsx2-qt/Debugger/Breakpoints/BreakpointModel.cpp similarity index 100% rename from pcsx2-qt/Debugger/Models/BreakpointModel.cpp rename to pcsx2-qt/Debugger/Breakpoints/BreakpointModel.cpp diff --git a/pcsx2-qt/Debugger/Models/BreakpointModel.h b/pcsx2-qt/Debugger/Breakpoints/BreakpointModel.h similarity index 100% rename from pcsx2-qt/Debugger/Models/BreakpointModel.h rename to pcsx2-qt/Debugger/Breakpoints/BreakpointModel.h diff --git a/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.cpp b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.cpp new file mode 100644 index 0000000000..4617a6e3e1 --- /dev/null +++ b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.cpp @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "BreakpointWidget.h" + +#include "QtUtils.h" +#include "Debugger/DebuggerSettingsManager.h" +#include "BreakpointDialog.h" +#include "BreakpointModel.h" + +#include <QClipboard> + +BreakpointWidget::BreakpointWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) + , m_model(cpu) +{ + m_ui.setupUi(this); + + connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &BreakpointWidget::onContextMenu); + connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &BreakpointWidget::onDoubleClicked); + + m_ui.breakpointList->setModel(&m_model); + for (std::size_t i = 0; auto mode : BreakpointModel::HeaderResizeModes) + { + m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(i, mode); + i++; + } + + connect(&m_model, &BreakpointModel::dataChanged, &m_model, &BreakpointModel::refreshData); +} + +void BreakpointWidget::onDoubleClicked(const QModelIndex& index) +{ + if (index.isValid() && index.column() == BreakpointModel::OFFSET) + { + not_yet_implemented(); + //m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_model.data(index, BreakpointModel::DataRole).toUInt()); + } +} + +void BreakpointWidget::onContextMenu(QPoint pos) +{ + QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList); + if (cpu().isAlive()) + { + + QAction* newAction = new QAction(tr("New"), m_ui.breakpointList); + connect(newAction, &QAction::triggered, this, &BreakpointWidget::contextNew); + contextMenu->addAction(newAction); + + const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); + + if (selModel->hasSelection()) + { + QAction* editAction = new QAction(tr("Edit"), m_ui.breakpointList); + connect(editAction, &QAction::triggered, this, &BreakpointWidget::contextEdit); + contextMenu->addAction(editAction); + + if (selModel->selectedIndexes().count() == 1) + { + QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList); + connect(copyAction, &QAction::triggered, this, &BreakpointWidget::contextCopy); + contextMenu->addAction(copyAction); + } + + QAction* deleteAction = new QAction(tr("Delete"), m_ui.breakpointList); + connect(deleteAction, &QAction::triggered, this, &BreakpointWidget::contextDelete); + contextMenu->addAction(deleteAction); + } + } + + contextMenu->addSeparator(); + if (m_model.rowCount() > 0) + { + QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList); + connect(actionExport, &QAction::triggered, [this]() { + // It's important to use the Export Role here to allow pasting to be translation agnostic + QGuiApplication::clipboard()->setText( + QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), + BreakpointModel::ExportRole, true)); + }); + contextMenu->addAction(actionExport); + } + + if (cpu().isAlive()) + { + QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList); + connect(actionImport, &QAction::triggered, this, &BreakpointWidget::contextPasteCSV); + contextMenu->addAction(actionImport); + + QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.breakpointList); + connect(actionLoad, &QAction::triggered, [this]() { + m_model.clear(); + DebuggerSettingsManager::loadGameSettings(&m_model); + }); + contextMenu->addAction(actionLoad); + + QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.breakpointList); + connect(actionSave, &QAction::triggered, this, &BreakpointWidget::saveBreakpointsToDebuggerSettings); + contextMenu->addAction(actionSave); + } + + contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos)); +} + +void BreakpointWidget::contextCopy() +{ + const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + QGuiApplication::clipboard()->setText(m_model.data(selModel->currentIndex()).toString()); +} + +void BreakpointWidget::contextDelete() +{ + const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + QModelIndexList rows = selModel->selectedIndexes(); + + std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { + return a.row() > b.row(); + }); + + for (const QModelIndex& index : rows) + { + m_model.removeRows(index.row(), 1); + } +} + +void BreakpointWidget::contextNew() +{ + BreakpointDialog* bpDialog = new BreakpointDialog(this, &cpu(), m_model); + bpDialog->show(); +} + +void BreakpointWidget::contextEdit() +{ + const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + const int selectedRow = selModel->selectedIndexes().first().row(); + + auto bpObject = m_model.at(selectedRow); + + BreakpointDialog* bpDialog = new BreakpointDialog(this, &cpu(), m_model, bpObject, selectedRow); + bpDialog->show(); +} + +void BreakpointWidget::contextPasteCSV() +{ + QString csv = QGuiApplication::clipboard()->text(); + // Skip header + csv = csv.mid(csv.indexOf('\n') + 1); + + for (const QString& line : csv.split('\n')) + { + QStringList fields; + // In order to handle text with commas in them we must wrap values in quotes to mark + // where a value starts and end so that text commas aren't identified as delimiters. + // So matches each quote pair, parse it out, and removes the quotes to get the value. + QRegularExpression eachQuotePair(R"("([^"]|\\.)*")"); + QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line); + while (it.hasNext()) + { + QRegularExpressionMatch match = it.next(); + QString matchedValue = match.captured(0); + fields << matchedValue.mid(1, matchedValue.length() - 2); + } + m_model.loadBreakpointFromFieldList(fields); + } +} + +void BreakpointWidget::saveBreakpointsToDebuggerSettings() +{ + DebuggerSettingsManager::saveGameSettings(&m_model); +} diff --git a/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.h b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.h new file mode 100644 index 0000000000..4bb750ccbc --- /dev/null +++ b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "ui_BreakpointWidget.h" + +#include "BreakpointModel.h" + +#include "Debugger/DebuggerWidget.h" + +#include "DebugTools/DebugInterface.h" +#include "DebugTools/DisassemblyManager.h" + +#include <QtWidgets/QMenu> +#include <QtWidgets/QTabBar> +#include <QtGui/QPainter> + +class BreakpointWidget : public DebuggerWidget +{ + Q_OBJECT + +public: + BreakpointWidget(DebugInterface& cpu, QWidget* parent = nullptr); + + void onDoubleClicked(const QModelIndex& index); + void onContextMenu(QPoint pos); + + void contextCopy(); + void contextDelete(); + void contextNew(); + void contextEdit(); + void contextPasteCSV(); + + void saveBreakpointsToDebuggerSettings(); + +private: + Ui::BreakpointWidget m_ui; + + BreakpointModel m_model; +}; diff --git a/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.ui b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.ui new file mode 100644 index 0000000000..7dc2bf069c --- /dev/null +++ b/pcsx2-qt/Debugger/Breakpoints/BreakpointWidget.ui @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BreakpointWidget</class> + <widget class="QWidget" name="BreakpointWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTableView" name="breakpointList"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/pcsx2-qt/Debugger/CpuWidget.cpp b/pcsx2-qt/Debugger/CpuWidget.cpp deleted file mode 100644 index 76f49e67b2..0000000000 --- a/pcsx2-qt/Debugger/CpuWidget.cpp +++ /dev/null @@ -1,732 +0,0 @@ -// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team -// SPDX-License-Identifier: GPL-3.0+ - -#include "CpuWidget.h" - -#include "DisassemblyWidget.h" -#include "BreakpointDialog.h" -#include "Models/BreakpointModel.h" -#include "Models/ThreadModel.h" -#include "Models/SavedAddressesModel.h" -#include "Debugger/DebuggerSettingsManager.h" - -#include "DebugTools/DebugInterface.h" -#include "DebugTools/Breakpoints.h" -#include "DebugTools/MipsStackWalk.h" - -#include "QtUtils.h" - -#include "common/Console.h" - -#include <QtGui/QClipboard> -#include <QtWidgets/QMessageBox> -#include <QtConcurrent/QtConcurrent> -#include <QtCore/QFutureWatcher> -#include <QtCore/QRegularExpression> -#include <QtCore/QRegularExpressionMatchIterator> -#include <QtCore/QStringList> -#include <QtWidgets/QScrollBar> - -using namespace QtUtils; -using namespace MipsStackWalk; - -CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu) - : m_cpu(cpu) - , m_bpModel(cpu) - , m_threadModel(cpu) - , m_stackModel(cpu) - , m_savedAddressesModel(cpu) -{ - m_ui.setupUi(this); - - connect(g_emu_thread, &EmuThread::onVMPaused, this, &CpuWidget::onVMPaused); - connect(g_emu_thread, &EmuThread::onGameChanged, this, [this](const QString& title) { - if (title.isEmpty()) - return; - // Don't overwrite users BPs/Saved Addresses unless they have a clean state. - if (m_bpModel.rowCount() == 0) - DebuggerSettingsManager::loadGameSettings(&m_bpModel); - if (m_savedAddressesModel.rowCount() == 0) - DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel); - }); - - connect(m_ui.registerWidget, &RegisterWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress); - connect(m_ui.memoryviewWidget, &MemoryViewWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress); - connect(m_ui.memoryviewWidget, &MemoryViewWidget::addToSavedAddresses, this, &CpuWidget::addAddressToSavedAddressesList); - - connect(m_ui.registerWidget, &RegisterWidget::gotoInMemory, this, &CpuWidget::onGotoInMemory); - connect(m_ui.disassemblyWidget, &DisassemblyWidget::gotoInMemory, this, &CpuWidget::onGotoInMemory); - - connect(m_ui.memoryviewWidget, &MemoryViewWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets); - connect(m_ui.registerWidget, &RegisterWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets); - connect(m_ui.disassemblyWidget, &DisassemblyWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets); - - connect(m_ui.disassemblyWidget, &DisassemblyWidget::breakpointsChanged, this, &CpuWidget::updateBreakpoints); - - connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &CpuWidget::onBPListContextMenu); - connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &CpuWidget::onBPListDoubleClicked); - - m_ui.breakpointList->setModel(&m_bpModel); - for (std::size_t i = 0; auto mode : BreakpointModel::HeaderResizeModes) - { - m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(i, mode); - i++; - } - - connect(&m_bpModel, &BreakpointModel::dataChanged, this, &CpuWidget::updateBreakpoints); - - connect(m_ui.threadList, &QTableView::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu); - connect(m_ui.threadList, &QTableView::doubleClicked, this, &CpuWidget::onThreadListDoubleClick); - - m_threadProxyModel.setSourceModel(&m_threadModel); - m_threadProxyModel.setSortRole(Qt::UserRole); - m_ui.threadList->setModel(&m_threadProxyModel); - m_ui.threadList->setSortingEnabled(true); - m_ui.threadList->sortByColumn(ThreadModel::ThreadColumns::ID, Qt::SortOrder::AscendingOrder); - for (std::size_t i = 0; auto mode : ThreadModel::HeaderResizeModes) - { - m_ui.threadList->horizontalHeader()->setSectionResizeMode(i, mode); - i++; - } - - connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu); - connect(m_ui.stackList, &QTableView::doubleClicked, this, &CpuWidget::onStackListDoubleClick); - - m_ui.stackList->setModel(&m_stackModel); - for (std::size_t i = 0; auto mode : StackModel::HeaderResizeModes) - { - m_ui.stackList->horizontalHeader()->setSectionResizeMode(i, mode); - i++; - } - - m_ui.disassemblyWidget->SetCpu(&cpu); - m_ui.registerWidget->SetCpu(&cpu); - m_ui.memoryviewWidget->SetCpu(&cpu); - - this->repaint(); - - m_ui.savedAddressesList->setModel(&m_savedAddressesModel); - m_ui.savedAddressesList->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_ui.savedAddressesList, &QTableView::customContextMenuRequested, this, &CpuWidget::onSavedAddressesListContextMenu); - for (std::size_t i = 0; auto mode : SavedAddressesModel::HeaderResizeModes) - { - m_ui.savedAddressesList->horizontalHeader()->setSectionResizeMode(i++, mode); - } - QTableView* savedAddressesTableView = m_ui.savedAddressesList; - connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) { - savedAddressesTableView->resizeColumnToContents(topLeft.column()); - }); - - setupSymbolTrees(); - - DebuggerSettingsManager::loadGameSettings(&m_bpModel); - DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel); - - connect(m_ui.memorySearchWidget, &MemorySearchWidget::addAddressToSavedAddressesList, this, &CpuWidget::addAddressToSavedAddressesList); - connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView, - [this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address, true); }); - connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInMemoryView, m_ui.memoryviewWidget, &MemoryViewWidget::gotoAddress); - connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab, - [this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); }); - m_ui.memorySearchWidget->setCpu(&m_cpu); - - m_refreshDebuggerTimer.setInterval(1000); - connect(&m_refreshDebuggerTimer, &QTimer::timeout, this, &CpuWidget::refreshDebugger); - m_refreshDebuggerTimer.start(); -} - -CpuWidget::~CpuWidget() = default; - -void CpuWidget::setupSymbolTrees() -{ - m_ui.tabFunctions->setLayout(new QVBoxLayout()); - m_ui.tabGlobalVariables->setLayout(new QVBoxLayout()); - m_ui.tabLocalVariables->setLayout(new QVBoxLayout()); - m_ui.tabParameterVariables->setLayout(new QVBoxLayout()); - - m_ui.tabFunctions->layout()->setContentsMargins(0, 0, 0, 0); - m_ui.tabGlobalVariables->layout()->setContentsMargins(0, 0, 0, 0); - m_ui.tabLocalVariables->layout()->setContentsMargins(0, 0, 0, 0); - m_ui.tabParameterVariables->layout()->setContentsMargins(0, 0, 0, 0); - - m_function_tree = new FunctionTreeWidget(m_cpu); - m_global_variable_tree = new GlobalVariableTreeWidget(m_cpu); - m_local_variable_tree = new LocalVariableTreeWidget(m_cpu); - m_parameter_variable_tree = new ParameterVariableTreeWidget(m_cpu); - - m_function_tree->updateModel(); - m_global_variable_tree->updateModel(); - m_local_variable_tree->updateModel(); - m_parameter_variable_tree->updateModel(); - - m_ui.tabFunctions->layout()->addWidget(m_function_tree); - m_ui.tabGlobalVariables->layout()->addWidget(m_global_variable_tree); - m_ui.tabLocalVariables->layout()->addWidget(m_local_variable_tree); - m_ui.tabParameterVariables->layout()->addWidget(m_parameter_variable_tree); - - connect(m_function_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); - connect(m_global_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); - connect(m_local_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); - connect(m_parameter_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); - - connect(m_function_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory); - connect(m_global_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory); - connect(m_local_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory); - connect(m_parameter_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory); - - connect(m_function_tree, &SymbolTreeWidget::nameColumnClicked, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); - connect(m_function_tree, &SymbolTreeWidget::locationColumnClicked, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus); -} - -void CpuWidget::refreshDebugger() -{ - if (!m_cpu.isAlive()) - return; - - m_ui.registerWidget->update(); - m_ui.disassemblyWidget->update(); - m_ui.memoryviewWidget->update(); - m_ui.memorySearchWidget->update(); - - m_function_tree->updateModel(); - m_global_variable_tree->updateModel(); - m_local_variable_tree->updateModel(); - m_parameter_variable_tree->updateModel(); -} - -void CpuWidget::reloadCPUWidgets() -{ - updateThreads(); - updateStackFrames(); - - m_ui.registerWidget->update(); - m_ui.disassemblyWidget->update(); - m_ui.memoryviewWidget->update(); - - m_function_tree->updateModel(); - m_global_variable_tree->updateModel(); - m_local_variable_tree->updateModel(); - m_parameter_variable_tree->updateModel(); -} - -void CpuWidget::paintEvent(QPaintEvent* event) -{ - m_ui.registerWidget->update(); - m_ui.disassemblyWidget->update(); - m_ui.memoryviewWidget->update(); - m_ui.memorySearchWidget->update(); -} - -// The cpu shouldn't be alive when these are called -// But make sure it isn't just in case -void CpuWidget::onStepInto() -{ - if (!m_cpu.isAlive() || !m_cpu.isCpuPaused()) - return; - - // Allow the cpu to skip this pc if it is a breakpoint - CBreakPoints::SetSkipFirst(m_cpu.getCpuType(), m_cpu.getPC()); - - const u32 pc = m_cpu.getPC(); - const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(&m_cpu, pc); - - u32 bpAddr = pc + 0x4; // Default to the next instruction - - if (info.isBranch) - { - if (!info.isConditional) - { - bpAddr = info.branchTarget; - } - else - { - if (info.conditionMet) - { - bpAddr = info.branchTarget; - } - else - { - bpAddr = pc + (2 * 4); // Skip branch delay slot - } - } - } - - if (info.isSyscall) - bpAddr = info.branchTarget; // Syscalls are always taken - - Host::RunOnCPUThread([cpu = &m_cpu, bpAddr] { - CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true); - cpu->resumeCpu(); - }); - - this->repaint(); -} - -void CpuWidget::onStepOut() -{ - if (!m_cpu.isAlive() || !m_cpu.isCpuPaused()) - return; - - // Allow the cpu to skip this pc if it is a breakpoint - CBreakPoints::SetSkipFirst(m_cpu.getCpuType(), m_cpu.getPC()); - - if (m_stackModel.rowCount() < 2) - return; - - Host::RunOnCPUThread([cpu = &m_cpu, stackModel = &m_stackModel] { - CBreakPoints::AddBreakPoint(cpu->getCpuType(), stackModel->data(stackModel->index(1, StackModel::PC), Qt::UserRole).toUInt(), true); - cpu->resumeCpu(); - }); - - this->repaint(); -} - -void CpuWidget::onStepOver() -{ - if (!m_cpu.isAlive() || !m_cpu.isCpuPaused()) - return; - - const u32 pc = m_cpu.getPC(); - const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(&m_cpu, pc); - - u32 bpAddr = pc + 0x4; // Default to the next instruction - - if (info.isBranch) - { - if (!info.isConditional) - { - if (info.isLinkedBranch) // jal, jalr - { - // it's a function call with a delay slot - skip that too - bpAddr += 4; - } - else // j, ... - { - // in case of absolute branches, set the breakpoint at the branch target - bpAddr = info.branchTarget; - } - } - else // beq, ... - { - if (info.conditionMet) - { - bpAddr = info.branchTarget; - } - else - { - bpAddr = pc + (2 * 4); // Skip branch delay slot - } - } - } - - Host::RunOnCPUThread([cpu = &m_cpu, bpAddr] { - CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true); - cpu->resumeCpu(); - }); - - this->repaint(); -} - -void CpuWidget::onVMPaused() -{ - // Stops us from telling the disassembly dialog to jump somwhere because breakpoint code paused the core. - if (CBreakPoints::GetCorePaused()) - { - CBreakPoints::SetCorePaused(false); - } - else - { - m_ui.disassemblyWidget->gotoProgramCounterOnPause(); - } - - reloadCPUWidgets(); - this->repaint(); -} - -void CpuWidget::updateBreakpoints() -{ - m_bpModel.refreshData(); -} - -void CpuWidget::onBPListDoubleClicked(const QModelIndex& index) -{ - if (index.isValid()) - { - if (index.column() == BreakpointModel::OFFSET) - { - m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_bpModel.data(index, BreakpointModel::DataRole).toUInt()); - } - } -} - -void CpuWidget::onBPListContextMenu(QPoint pos) -{ - QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList); - if (m_cpu.isAlive()) - { - - QAction* newAction = new QAction(tr("New"), m_ui.breakpointList); - connect(newAction, &QAction::triggered, this, &CpuWidget::contextBPListNew); - contextMenu->addAction(newAction); - - const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); - - if (selModel->hasSelection()) - { - QAction* editAction = new QAction(tr("Edit"), m_ui.breakpointList); - connect(editAction, &QAction::triggered, this, &CpuWidget::contextBPListEdit); - contextMenu->addAction(editAction); - - if (selModel->selectedIndexes().count() == 1) - { - QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList); - connect(copyAction, &QAction::triggered, this, &CpuWidget::contextBPListCopy); - contextMenu->addAction(copyAction); - } - - QAction* deleteAction = new QAction(tr("Delete"), m_ui.breakpointList); - connect(deleteAction, &QAction::triggered, this, &CpuWidget::contextBPListDelete); - contextMenu->addAction(deleteAction); - } - } - - contextMenu->addSeparator(); - if (m_bpModel.rowCount() > 0) - { - QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList); - connect(actionExport, &QAction::triggered, [this]() { - // It's important to use the Export Role here to allow pasting to be translation agnostic - QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), BreakpointModel::ExportRole, true)); - }); - contextMenu->addAction(actionExport); - } - - if (m_cpu.isAlive()) - { - QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList); - connect(actionImport, &QAction::triggered, this, &CpuWidget::contextBPListPasteCSV); - contextMenu->addAction(actionImport); - - QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.breakpointList); - connect(actionLoad, &QAction::triggered, [this]() { - m_bpModel.clear(); - DebuggerSettingsManager::loadGameSettings(&m_bpModel); - }); - contextMenu->addAction(actionLoad); - - QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.breakpointList); - connect(actionSave, &QAction::triggered, this, &CpuWidget::saveBreakpointsToDebuggerSettings); - contextMenu->addAction(actionSave); - } - - contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos)); -} - -void CpuWidget::onGotoInMemory(u32 address) -{ - m_ui.memoryviewWidget->gotoAddress(address); - m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); -} - -void CpuWidget::contextBPListCopy() -{ - const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); - - if (!selModel->hasSelection()) - return; - - QGuiApplication::clipboard()->setText(m_bpModel.data(selModel->currentIndex()).toString()); -} - -void CpuWidget::contextBPListDelete() -{ - const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); - - if (!selModel->hasSelection()) - return; - - QModelIndexList rows = selModel->selectedIndexes(); - - std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { - return a.row() > b.row(); - }); - - for (const QModelIndex& index : rows) - { - m_bpModel.removeRows(index.row(), 1); - } -} - -void CpuWidget::contextBPListNew() -{ - BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel); - bpDialog->show(); -} - -void CpuWidget::contextBPListEdit() -{ - const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel(); - - if (!selModel->hasSelection()) - return; - - const int selectedRow = selModel->selectedIndexes().first().row(); - - auto bpObject = m_bpModel.at(selectedRow); - - BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel, bpObject, selectedRow); - bpDialog->show(); -} - -void CpuWidget::contextBPListPasteCSV() -{ - QString csv = QGuiApplication::clipboard()->text(); - // Skip header - csv = csv.mid(csv.indexOf('\n') + 1); - - for (const QString& line : csv.split('\n')) - { - QStringList fields; - // In order to handle text with commas in them we must wrap values in quotes to mark - // where a value starts and end so that text commas aren't identified as delimiters. - // So matches each quote pair, parse it out, and removes the quotes to get the value. - QRegularExpression eachQuotePair(R"("([^"]|\\.)*")"); - QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line); - while (it.hasNext()) - { - QRegularExpressionMatch match = it.next(); - QString matchedValue = match.captured(0); - fields << matchedValue.mid(1, matchedValue.length() - 2); - } - m_bpModel.loadBreakpointFromFieldList(fields); - } -} - -void CpuWidget::onSavedAddressesListContextMenu(QPoint pos) -{ - QMenu* contextMenu = new QMenu("Saved Addresses List Context Menu", m_ui.savedAddressesList); - - QAction* newAction = new QAction(tr("New"), m_ui.savedAddressesList); - connect(newAction, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListNew); - contextMenu->addAction(newAction); - - const QModelIndex indexAtPos = m_ui.savedAddressesList->indexAt(pos); - const bool isIndexValid = indexAtPos.isValid(); - - if (isIndexValid) - { - if (m_cpu.isAlive()) - { - QAction* goToAddressMemViewAction = new QAction(tr("Go to in Memory View"), m_ui.savedAddressesList); - connect(goToAddressMemViewAction, &QAction::triggered, this, [this, indexAtPos]() { - const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex()); - m_ui.memoryviewWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt()); - m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); - }); - contextMenu->addAction(goToAddressMemViewAction); - - QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList); - connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() { - const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex()); - m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt()); - }); - contextMenu->addAction(goToAddressDisassemblyAction); - } - - QAction* copyAction = new QAction(indexAtPos.column() == 0 ? tr("Copy Address") : tr("Copy Text"), m_ui.savedAddressesList); - connect(copyAction, &QAction::triggered, [this, indexAtPos]() { - QGuiApplication::clipboard()->setText(m_ui.savedAddressesList->model()->data(indexAtPos, Qt::DisplayRole).toString()); - }); - contextMenu->addAction(copyAction); - } - - if (m_ui.savedAddressesList->model()->rowCount() > 0) - { - QAction* actionExportCSV = new QAction(tr("Copy all as CSV"), m_ui.savedAddressesList); - connect(actionExportCSV, &QAction::triggered, [this]() { - QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.savedAddressesList->model(), Qt::DisplayRole, true)); - }); - contextMenu->addAction(actionExportCSV); - } - - QAction* actionImportCSV = new QAction(tr("Paste from CSV"), m_ui.savedAddressesList); - connect(actionImportCSV, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListPasteCSV); - contextMenu->addAction(actionImportCSV); - - if (m_cpu.isAlive()) - { - QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.savedAddressesList); - connect(actionLoad, &QAction::triggered, [this]() { - m_savedAddressesModel.clear(); - DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel); - }); - contextMenu->addAction(actionLoad); - - QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.savedAddressesList); - connect(actionSave, &QAction::triggered, this, &CpuWidget::saveSavedAddressesToDebuggerSettings); - contextMenu->addAction(actionSave); - } - - if (isIndexValid) - { - QAction* deleteAction = new QAction(tr("Delete"), m_ui.savedAddressesList); - connect(deleteAction, &QAction::triggered, this, [this, indexAtPos]() { - m_ui.savedAddressesList->model()->removeRows(indexAtPos.row(), 1); - }); - contextMenu->addAction(deleteAction); - } - - contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos)); -} - -void CpuWidget::contextSavedAddressesListPasteCSV() -{ - QString csv = QGuiApplication::clipboard()->text(); - // Skip header - csv = csv.mid(csv.indexOf('\n') + 1); - - for (const QString& line : csv.split('\n')) - { - QStringList fields; - // In order to handle text with commas in them we must wrap values in quotes to mark - // where a value starts and end so that text commas aren't identified as delimiters. - // So matches each quote pair, parse it out, and removes the quotes to get the value. - QRegularExpression eachQuotePair(R"("([^"]|\\.)*")"); - QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line); - while (it.hasNext()) - { - QRegularExpressionMatch match = it.next(); - QString matchedValue = match.captured(0); - fields << matchedValue.mid(1, matchedValue.length() - 2); - } - - m_savedAddressesModel.loadSavedAddressFromFieldList(fields); - } -} - -void CpuWidget::contextSavedAddressesListNew() -{ - qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow(); - const u32 rowCount = m_ui.savedAddressesList->model()->rowCount(); - m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 0)); -} - -void CpuWidget::addAddressToSavedAddressesList(u32 address) -{ - qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow(); - const u32 rowCount = m_ui.savedAddressesList->model()->rowCount(); - const QModelIndex addressIndex = m_ui.savedAddressesList->model()->index(rowCount - 1, 0); - m_ui.tabWidget->setCurrentWidget(m_ui.tab_savedaddresses); - m_ui.savedAddressesList->model()->setData(addressIndex, address, Qt::UserRole); - m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1)); -} - -void CpuWidget::updateThreads() -{ - m_threadModel.refreshData(); -} - -void CpuWidget::onThreadListContextMenu(QPoint pos) -{ - if (!m_ui.threadList->selectionModel()->hasSelection()) - return; - - QMenu* contextMenu = new QMenu(tr("Thread List Context Menu"), m_ui.threadList); - - QAction* actionCopy = new QAction(tr("Copy"), m_ui.threadList); - connect(actionCopy, &QAction::triggered, [this]() { - const auto* selModel = m_ui.threadList->selectionModel(); - - if (!selModel->hasSelection()) - return; - - QGuiApplication::clipboard()->setText(m_ui.threadList->model()->data(selModel->currentIndex()).toString()); - }); - contextMenu->addAction(actionCopy); - - contextMenu->addSeparator(); - - QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.threadList); - connect(actionExport, &QAction::triggered, [this]() { - QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.threadList->model())); - }); - contextMenu->addAction(actionExport); - - contextMenu->popup(m_ui.threadList->viewport()->mapToGlobal(pos)); -} - -void CpuWidget::onThreadListDoubleClick(const QModelIndex& index) -{ - switch (index.column()) - { - case ThreadModel::ThreadColumns::ENTRY: - m_ui.memoryviewWidget->gotoAddress(m_ui.threadList->model()->data(index, Qt::UserRole).toUInt()); - m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); - break; - default: // Default to PC - m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt()); - break; - } -} - -void CpuWidget::updateStackFrames() -{ - m_stackModel.refreshData(); -} - -void CpuWidget::onStackListContextMenu(QPoint pos) -{ - if (!m_ui.stackList->selectionModel()->hasSelection()) - return; - - QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList); - - QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList); - connect(actionCopy, &QAction::triggered, [this]() { - const auto* selModel = m_ui.stackList->selectionModel(); - - if (!selModel->hasSelection()) - return; - - QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString()); - }); - contextMenu->addAction(actionCopy); - - contextMenu->addSeparator(); - - QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.stackList); - connect(actionExport, &QAction::triggered, [this]() { - QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.stackList->model())); - }); - contextMenu->addAction(actionExport); - - contextMenu->popup(m_ui.stackList->viewport()->mapToGlobal(pos)); -} - -void CpuWidget::onStackListDoubleClick(const QModelIndex& index) -{ - switch (index.column()) - { - case StackModel::StackModel::ENTRY: - case StackModel::StackModel::ENTRY_LABEL: - m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt()); - break; - case StackModel::StackModel::SP: - m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt()); - m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); - break; - default: // Default to PC - m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt()); - break; - } -} - -void CpuWidget::saveBreakpointsToDebuggerSettings() -{ - DebuggerSettingsManager::saveGameSettings(&m_bpModel); -} - -void CpuWidget::saveSavedAddressesToDebuggerSettings() -{ - DebuggerSettingsManager::saveGameSettings(&m_savedAddressesModel); -} diff --git a/pcsx2-qt/Debugger/CpuWidget.h b/pcsx2-qt/Debugger/CpuWidget.h deleted file mode 100644 index 2f96ead065..0000000000 --- a/pcsx2-qt/Debugger/CpuWidget.h +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team -// SPDX-License-Identifier: GPL-3.0+ - -#pragma once - -#include "ui_CpuWidget.h" - -#include "DebugTools/DebugInterface.h" - -#include "Models/BreakpointModel.h" -#include "Models/ThreadModel.h" -#include "Models/StackModel.h" -#include "Models/SavedAddressesModel.h" -#include "Debugger/SymbolTree/SymbolTreeWidgets.h" - -#include "QtHost.h" -#include <QtWidgets/QWidget> -#include <QtWidgets/QTableWidget> -#include <QtCore/QSortFilterProxyModel> -#include <QtCore/QTimer> - -#include <vector> - -using namespace MipsStackWalk; - -class CpuWidget final : public QWidget -{ - Q_OBJECT - -public: - CpuWidget(QWidget* parent, DebugInterface& cpu); - ~CpuWidget(); - -public slots: - void paintEvent(QPaintEvent* event); - - void onStepInto(); - void onStepOver(); - void onStepOut(); - - void onVMPaused(); - - void updateBreakpoints(); - void onBPListDoubleClicked(const QModelIndex& index); - void onBPListContextMenu(QPoint pos); - void onGotoInMemory(u32 address); - - void contextBPListCopy(); - void contextBPListDelete(); - void contextBPListNew(); - void contextBPListEdit(); - void contextBPListPasteCSV(); - - void onSavedAddressesListContextMenu(QPoint pos); - void contextSavedAddressesListPasteCSV(); - void contextSavedAddressesListNew(); - void addAddressToSavedAddressesList(u32 address); - - void updateThreads(); - void onThreadListDoubleClick(const QModelIndex& index); - void onThreadListContextMenu(QPoint pos); - - void updateStackFrames(); - void onStackListContextMenu(QPoint pos); - void onStackListDoubleClick(const QModelIndex& index); - - void refreshDebugger(); - void reloadCPUWidgets(); - - void saveBreakpointsToDebuggerSettings(); - void saveSavedAddressesToDebuggerSettings(); - -private: - void setupSymbolTrees(); - - std::vector<QTableWidget*> m_registerTableViews; - - QMenu* m_stacklistContextMenu = 0; - QMenu* m_funclistContextMenu = 0; - QMenu* m_moduleTreeContextMenu = 0; - QTimer m_refreshDebuggerTimer; - - Ui::CpuWidget m_ui; - - DebugInterface& m_cpu; - - BreakpointModel m_bpModel; - ThreadModel m_threadModel; - QSortFilterProxyModel m_threadProxyModel; - StackModel m_stackModel; - SavedAddressesModel m_savedAddressesModel; - - FunctionTreeWidget* m_function_tree = nullptr; - GlobalVariableTreeWidget* m_global_variable_tree = nullptr; - LocalVariableTreeWidget* m_local_variable_tree = nullptr; - ParameterVariableTreeWidget* m_parameter_variable_tree = nullptr; -}; diff --git a/pcsx2-qt/Debugger/CpuWidget.ui b/pcsx2-qt/Debugger/CpuWidget.ui deleted file mode 100644 index 587fd31449..0000000000 --- a/pcsx2-qt/Debugger/CpuWidget.ui +++ /dev/null @@ -1,445 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>CpuWidget</class> - <widget class="QWidget" name="CpuWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>668</width> - <height>563</height> - </rect> - </property> - <property name="font"> - <font> - <family>Monospace</family> - </font> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - <property name="windowTitle"> - <string/> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QSplitter" name="verticalSplitter"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="childrenCollapsible"> - <bool>false</bool> - </property> - <widget class="QSplitter" name="horizontalSplitter"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="childrenCollapsible"> - <bool>false</bool> - </property> - <widget class="QTabWidget" name="tabWidgetRegFunc"> - <property name="minimumSize"> - <size> - <width>100</width> - <height>100</height> - </size> - </property> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="tabRegisters"> - <attribute name="title"> - <string>Registers</string> - </attribute> - <layout class="QHBoxLayout" name="registerLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="RegisterWidget" name="registerWidget" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>320</width> - <height>100</height> - </size> - </property> - <property name="font"> - <font> - <family>Monospace</family> - </font> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tabFunctions"> - <attribute name="title"> - <string>Functions</string> - </attribute> - </widget> - <widget class="QWidget" name="tabMemorySearch"> - <attribute name="title"> - <string>Memory Search</string> - </attribute> - <layout class="QHBoxLayout" name="memorySearchLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="MemorySearchWidget" name="memorySearchWidget" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <family>Monospace</family> - </font> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </widget> - <widget class="DisassemblyWidget" name="disassemblyWidget" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>100</width> - <height>100</height> - </size> - </property> - <property name="font"> - <font> - <family>Monospace</family> - </font> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - </widget> - </widget> - <widget class="QTabWidget" name="tabWidget"> - <property name="minimumSize"> - <size> - <width>150</width> - <height>0</height> - </size> - </property> - <property name="tabPosition"> - <enum>QTabWidget::North</enum> - </property> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="tab_memory"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <attribute name="title"> - <string>Memory</string> - </attribute> - <layout class="QHBoxLayout" name="memoryLayout"> - <property name="spacing"> - <number>6</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="MemoryViewWidget" name="memoryviewWidget" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>250</width> - <height>100</height> - </size> - </property> - <property name="font"> - <font> - <family>Monospace</family> - </font> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_breakpoints"> - <attribute name="title"> - <string>Breakpoints</string> - </attribute> - <layout class="QHBoxLayout" name="breakpointsLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QTableView" name="breakpointList"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="gridStyle"> - <enum>Qt::NoPen</enum> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_threads"> - <attribute name="title"> - <string>Threads</string> - </attribute> - <layout class="QHBoxLayout" name="threadsLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QTableView" name="threadList"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="selectionMode"> - <enum>QAbstractItemView::SingleSelection</enum> - </property> - <property name="gridStyle"> - <enum>Qt::NoPen</enum> - </property> - <property name="cornerButtonEnabled"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_callstack"> - <attribute name="title"> - <string>Active Call Stack</string> - </attribute> - <layout class="QHBoxLayout" name="callStackLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QTableView" name="stackList"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="sizeAdjustPolicy"> - <enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum> - </property> - <property name="selectionMode"> - <enum>QAbstractItemView::SingleSelection</enum> - </property> - <property name="gridStyle"> - <enum>Qt::NoPen</enum> - </property> - <property name="cornerButtonEnabled"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_savedaddresses"> - <attribute name="title"> - <string>Saved Addresses</string> - </attribute> - <layout class="QHBoxLayout" name="savedAddressesLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QTableView" name="savedAddressesList"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> - </property> - <property name="horizontalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOff</enum> - </property> - <property name="gridStyle"> - <enum>Qt::NoPen</enum> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tabGlobalVariables"> - <attribute name="title"> - <string>Globals</string> - </attribute> - </widget> - <widget class="QWidget" name="tabLocalVariables"> - <attribute name="title"> - <string>Locals</string> - </attribute> - </widget> - <widget class="QWidget" name="tabParameterVariables"> - <attribute name="title"> - <string>Parameters</string> - </attribute> - </widget> - </widget> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>DisassemblyWidget</class> - <extends>QWidget</extends> - <header>pcsx2-qt/Debugger/DisassemblyWidget.h</header> - <container>1</container> - </customwidget> - <customwidget> - <class>RegisterWidget</class> - <extends>QWidget</extends> - <header>pcsx2-qt/Debugger/RegisterWidget.h</header> - <container>1</container> - </customwidget> - <customwidget> - <class>MemoryViewWidget</class> - <extends>QWidget</extends> - <header>pcsx2-qt/Debugger/MemoryViewWidget.h</header> - <container>1</container> - </customwidget> - <customwidget> - <class>MemorySearchWidget</class> - <extends>QWidget</extends> - <header>pcsx2-qt/Debugger/MemorySearchWidget.h</header> - <container>1</container> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/pcsx2-qt/Debugger/DebuggerSettingsManager.cpp b/pcsx2-qt/Debugger/DebuggerSettingsManager.cpp index f4b7ace1ca..81a23c8858 100644 --- a/pcsx2-qt/Debugger/DebuggerSettingsManager.cpp +++ b/pcsx2-qt/Debugger/DebuggerSettingsManager.cpp @@ -10,12 +10,12 @@ #include "common/Console.h" #include "VMManager.h" -#include "Models/BreakpointModel.h" std::mutex DebuggerSettingsManager::writeLock; const QString DebuggerSettingsManager::settingsFileVersion = "0.00"; -QJsonObject DebuggerSettingsManager::loadGameSettingsJSON() { +QJsonObject DebuggerSettingsManager::loadGameSettingsJSON() +{ std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame(); QFile file(QString::fromStdString(path)); if (!file.open(QIODevice::ReadOnly)) @@ -134,7 +134,7 @@ void DebuggerSettingsManager::saveGameSettings(QAbstractTableModel* abstractTabl { const std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame(); if (path.empty()) - return; + return; const std::lock_guard<std::mutex> lock(writeLock); QJsonObject loadedSettings = loadGameSettingsJSON(); diff --git a/pcsx2-qt/Debugger/DebuggerSettingsManager.h b/pcsx2-qt/Debugger/DebuggerSettingsManager.h index 4cbf373543..6c264f670c 100644 --- a/pcsx2-qt/Debugger/DebuggerSettingsManager.h +++ b/pcsx2-qt/Debugger/DebuggerSettingsManager.h @@ -6,8 +6,8 @@ #include <mutex> -#include "Models/BreakpointModel.h" -#include "Models/SavedAddressesModel.h" +#include "Breakpoints/BreakpointModel.h" +#include "Memory/SavedAddressesModel.h" class DebuggerSettingsManager final { diff --git a/pcsx2-qt/Debugger/DebuggerWidget.cpp b/pcsx2-qt/Debugger/DebuggerWidget.cpp new file mode 100644 index 0000000000..c4d2148ba5 --- /dev/null +++ b/pcsx2-qt/Debugger/DebuggerWidget.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "DebuggerWidget.h" + +#include "common/Assertions.h" + +DebuggerWidget::DebuggerWidget(DebugInterface* cpu, QWidget* parent) + : QWidget(parent) + , m_cpu(cpu) +{ +} + +DebugInterface& DebuggerWidget::cpu() const +{ + pxAssertRel(m_cpu, "DebuggerWidget::cpu() called on object that doesn't have a CPU type set."); + return *m_cpu; +} + +void DebuggerWidget::applyMonospaceFont() +{ + // Easiest way to handle cross platform monospace fonts + // There are issues related to TabWidget -> Children font inheritance otherwise +#if defined(WIN32) + setStyleSheet(QStringLiteral("font: 10pt 'Lucida Console'")); +#elif defined(__APPLE__) + setStyleSheet(QStringLiteral("font: 10pt 'Monaco'")); +#else + setStyleSheet(QStringLiteral("font: 10pt 'Monospace'")); +#endif +} diff --git a/pcsx2-qt/Debugger/DebuggerWidget.h b/pcsx2-qt/Debugger/DebuggerWidget.h new file mode 100644 index 0000000000..53fec8c4b4 --- /dev/null +++ b/pcsx2-qt/Debugger/DebuggerWidget.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "DebugTools/DebugInterface.h" + +#include <QtWidgets/QWidget> + +inline void not_yet_implemented() +{ + abort(); +} + +class DebuggerWidget : public QWidget +{ + Q_OBJECT + +protected: + DebuggerWidget(DebugInterface* cpu, QWidget* parent = nullptr); + + DebugInterface& cpu() const; + + void applyMonospaceFont(); + +private: + DebugInterface* m_cpu; +}; diff --git a/pcsx2-qt/Debugger/DebuggerWindow.cpp b/pcsx2-qt/Debugger/DebuggerWindow.cpp index ee43eafa06..47be09fc85 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.cpp +++ b/pcsx2-qt/Debugger/DebuggerWindow.cpp @@ -12,20 +12,11 @@ #include "AnalysisOptionsDialog.h" DebuggerWindow::DebuggerWindow(QWidget* parent) - : QMainWindow(parent) + : KDDockWidgets::QtWidgets::MainWindow(QStringLiteral("DebuggerWindow"), {}, parent) + , m_dock_manager(this) { 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); @@ -39,15 +30,18 @@ DebuggerWindow::DebuggerWindow(QWidget* parent) 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.actionAnalyse, spacer); + //QWidget* spacer = new QWidget(this); + //spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //m_ui.toolBar->insertWidget(m_ui.actionAnalyse, 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"); - m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900"); - m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000"); + m_dock_manager.switchToLayout(0); + + //QTabBar* tabs = new QTabBar(); + //tabs->addTab("Test"); + //m_ui.menuBar->layout()->addWidget(tabs); } DebuggerWindow::~DebuggerWindow() = default; @@ -56,8 +50,8 @@ DebuggerWindow::~DebuggerWindow() = default; // 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()); + //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() @@ -87,10 +81,10 @@ void DebuggerWindow::onVMStateChanged() switch (triggeredCpu) { case BREAKPOINT_EE: - m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900); + //m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900); break; case BREAKPOINT_IOP: - m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000); + //m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000); break; default: break; @@ -115,20 +109,20 @@ void DebuggerWindow::onRunPause() void DebuggerWindow::onStepInto() { - CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); - currentCpu->onStepInto(); + //CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); + //currentCpu->onStepInto(); } void DebuggerWindow::onStepOver() { - CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); - currentCpu->onStepOver(); + //CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); + //currentCpu->onStepOver(); } void DebuggerWindow::onStepOut() { - CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); - currentCpu->onStepOut(); + //CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget()); + //currentCpu->onStepOut(); } void DebuggerWindow::onAnalyse() diff --git a/pcsx2-qt/Debugger/DebuggerWindow.h b/pcsx2-qt/Debugger/DebuggerWindow.h index 26b4b0fc5d..65df2dd71a 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.h +++ b/pcsx2-qt/Debugger/DebuggerWindow.h @@ -3,11 +3,13 @@ #pragma once -#include "CpuWidget.h" - #include "ui_DebuggerWindow.h" -class DebuggerWindow : public QMainWindow +#include "DockManager.h" + +#include <kddockwidgets/MainWindow.h> + +class DebuggerWindow : public KDDockWidgets::QtWidgets::MainWindow { Q_OBJECT @@ -25,7 +27,7 @@ public slots: protected: void showEvent(QShowEvent* event); - void hideEvent(QHideEvent *event); + void hideEvent(QHideEvent* event); private: Ui::DebuggerWindow m_ui; @@ -34,8 +36,7 @@ private: QAction* m_actionStepOver; QAction* m_actionStepOut; - CpuWidget* m_cpuWidget_r5900; - CpuWidget* m_cpuWidget_r3000; + DockManager m_dock_manager; void setTabActiveStyle(BreakPointCpu toggledCPU); }; diff --git a/pcsx2-qt/Debugger/DebuggerWindow.ui b/pcsx2-qt/Debugger/DebuggerWindow.ui index c668a6316b..c0bd9b9487 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.ui +++ b/pcsx2-qt/Debugger/DebuggerWindow.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>800</width> - <height>600</height> + <width>1000</width> + <height>750</height> </rect> </property> <property name="windowTitle"> @@ -18,19 +18,12 @@ <normalon>:/icons/AppIcon64.png</normalon> </iconset> </property> - <widget class="QWidget" name="centralwidget"> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QTabWidget" name="cpuTabs"/> - </item> - </layout> - </widget> - <widget class="QToolBar" name="toolBar"> + <widget class="QToolBar" name="debugToolBar"> <property name="contextMenuPolicy"> - <enum>Qt::ContextMenuPolicy::PreventContextMenu</enum> + <enum>Qt::PreventContextMenu</enum> </property> <property name="movable"> - <bool>false</bool> + <bool>true</bool> </property> <property name="iconSize"> <size> @@ -39,10 +32,10 @@ </size> </property> <property name="toolButtonStyle"> - <enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum> + <enum>Qt::ToolButtonTextBesideIcon</enum> </property> <property name="floatable"> - <bool>false</bool> + <bool>true</bool> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -54,7 +47,63 @@ <addaction name="actionStepInto"/> <addaction name="actionStepOver"/> <addaction name="actionStepOut"/> - <addaction name="actionAnalyse"/> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1000</width> + <height>20</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionAnalyse"/> + </widget> + <widget class="QMenu" name="menuDebug"> + <property name="title"> + <string>Debug</string> + </property> + <addaction name="actionRun"/> + <addaction name="actionStepInto"/> + <addaction name="actionStepOver"/> + <addaction name="actionStepOut"/> + </widget> + <widget class="QMenu" name="menuWindows"> + <property name="title"> + <string>Windows</string> + </property> + </widget> + <widget class="QMenu" name="menuLayouts"> + <property name="title"> + <string>Layouts</string> + </property> + </widget> + <widget class="QMenu" name="menuView"> + <property name="title"> + <string>View</string> + </property> + <addaction name="actionOnTop"/> + </widget> + <addaction name="menuFile"/> + <addaction name="menuView"/> + <addaction name="menuDebug"/> + <addaction name="menuWindows"/> + <addaction name="menuLayouts"/> + </widget> + <widget class="QToolBar" name="viewToolBar"> + <property name="windowTitle"> + <string>toolBar</string> + </property> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> <addaction name="actionOnTop"/> </widget> <action name="actionRun"> diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.cpp b/pcsx2-qt/Debugger/DisassemblyWidget.cpp index 097a5d566a..fb8f9b63f4 100644 --- a/pcsx2-qt/Debugger/DisassemblyWidget.cpp +++ b/pcsx2-qt/Debugger/DisassemblyWidget.cpp @@ -19,12 +19,16 @@ using namespace QtUtils; -DisassemblyWidget::DisassemblyWidget(QWidget* parent) - : QWidget(parent) +DisassemblyWidget::DisassemblyWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) { - ui.setupUi(this); + m_ui.setupUi(this); + + m_disassemblyManager.setCpu(&cpu); connect(this, &DisassemblyWidget::customContextMenuRequested, this, &DisassemblyWidget::customMenuRequested); + + applyMonospaceFont(); } DisassemblyWidget::~DisassemblyWidget() = default; @@ -46,7 +50,7 @@ void DisassemblyWidget::contextCopyInstructionText() void DisassemblyWidget::contextAssembleInstruction() { - if (!m_cpu->isCpuPaused()) + if (!cpu().isCpuPaused()) { QMessageBox::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running")); return; @@ -63,7 +67,7 @@ void DisassemblyWidget::contextAssembleInstruction() u32 encodedInstruction; std::string errorText; - bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), m_cpu, m_selectedAddressStart, encodedInstruction, errorText); + bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), &cpu(), m_selectedAddressStart, encodedInstruction, errorText); if (!valid) { @@ -72,7 +76,7 @@ void DisassemblyWidget::contextAssembleInstruction() } else { - Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu, val = encodedInstruction] { + Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu(), val = encodedInstruction] { for (u32 i = start; i <= end; i += 4) { this->m_nopedInstructions.insert({i, cpu->read32(i)}); @@ -85,7 +89,7 @@ void DisassemblyWidget::contextAssembleInstruction() void DisassemblyWidget::contextNoopInstruction() { - Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] { + Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] { for (u32 i = start; i <= end; i += 4) { this->m_nopedInstructions.insert({i, cpu->read32(i)}); @@ -97,7 +101,7 @@ void DisassemblyWidget::contextNoopInstruction() void DisassemblyWidget::contextRestoreInstruction() { - Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] { + Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] { for (u32 i = start; i <= end; i += 4) { if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end()) @@ -113,7 +117,7 @@ void DisassemblyWidget::contextRestoreInstruction() void DisassemblyWidget::contextRunToCursor() { const u32 selectedAddressStart = m_selectedAddressStart; - Host::RunOnCPUThread([cpu = m_cpu, selectedAddressStart] { + Host::RunOnCPUThread([cpu = &cpu(), selectedAddressStart] { CBreakPoints::AddBreakPoint(cpu->getCpuType(), selectedAddressStart, true); cpu->resumeCpu(); }); @@ -121,17 +125,17 @@ void DisassemblyWidget::contextRunToCursor() void DisassemblyWidget::contextJumpToCursor() { - m_cpu->setPc(m_selectedAddressStart); + cpu().setPc(m_selectedAddressStart); this->repaint(); } void DisassemblyWidget::contextToggleBreakpoint() { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; const u32 selectedAddressStart = m_selectedAddressStart; - const BreakPointCpu cpuType = m_cpu->getCpuType(); + const BreakPointCpu cpuType = cpu().getCpuType(); if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddressStart)) { Host::RunOnCPUThread([cpuType, selectedAddressStart] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddressStart); }); @@ -171,7 +175,7 @@ void DisassemblyWidget::contextGoToAddress() u64 address = 0; std::string error; - if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error)) + if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error)) { QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error)); return; @@ -182,7 +186,7 @@ void DisassemblyWidget::contextGoToAddress() void DisassemblyWidget::contextAddFunction() { - NewFunctionDialog* dialog = new NewFunctionDialog(*m_cpu, this); + NewFunctionDialog* dialog = new NewFunctionDialog(cpu(), this); dialog->setName(QString("func_%1").arg(m_selectedAddressStart, 8, 16, QChar('0'))); dialog->setAddress(m_selectedAddressStart); if (m_selectedAddressEnd != m_selectedAddressStart) @@ -193,13 +197,13 @@ void DisassemblyWidget::contextAddFunction() void DisassemblyWidget::contextCopyFunctionName() { - std::string name = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name; + std::string name = cpu().GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name; QGuiApplication::clipboard()->setText(QString::fromStdString(name)); } void DisassemblyWidget::contextRemoveFunction() { - m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) { + cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) { ccc::Function* curFunc = database.functions.symbol_overlapping_address(m_selectedAddressStart); if (!curFunc) return; @@ -215,7 +219,7 @@ void DisassemblyWidget::contextRemoveFunction() void DisassemblyWidget::contextRenameFunction() { - const FunctionInfo curFunc = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart); + const FunctionInfo curFunc = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart); if (!curFunc.address.valid()) { @@ -236,17 +240,17 @@ void DisassemblyWidget::contextRenameFunction() return; } - m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) { + cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) { database.functions.rename_symbol(curFunc.handle, newName.toStdString()); }); } void DisassemblyWidget::contextStubFunction() { - FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart); + FunctionInfo function = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart); u32 address = function.address.valid() ? function.address.value : m_selectedAddressStart; - Host::RunOnCPUThread([this, address, cpu = m_cpu] { + Host::RunOnCPUThread([this, address, cpu = &cpu()] { this->m_stubbedFunctions.insert({address, {cpu->read32(address), cpu->read32(address + 4)}}); cpu->write32(address, 0x03E00008); // jr ra cpu->write32(address + 4, 0x00000000); // nop @@ -257,7 +261,7 @@ void DisassemblyWidget::contextStubFunction() void DisassemblyWidget::contextRestoreFunction() { u32 address = m_selectedAddressStart; - m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { + cpu().GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { const ccc::Function* function = database.functions.symbol_overlapping_address(m_selectedAddressStart); if (function) address = function->address().value; @@ -266,7 +270,7 @@ void DisassemblyWidget::contextRestoreFunction() auto stub = m_stubbedFunctions.find(address); if (stub != m_stubbedFunctions.end()) { - Host::RunOnCPUThread([this, address, cpu = m_cpu, stub] { + Host::RunOnCPUThread([this, address, cpu = &cpu(), stub] { auto [first_instruction, second_instruction] = stub->second; cpu->write32(address, first_instruction); cpu->write32(address + 4, second_instruction); @@ -286,12 +290,6 @@ void DisassemblyWidget::contextShowOpcode() this->repaint(); } -void DisassemblyWidget::SetCpu(DebugInterface* cpu) -{ - m_cpu = cpu; - m_disassemblyManager.setCpu(cpu); -} - QString DisassemblyWidget::GetLineDisasm(u32 address) { DisassemblyLineInfo lineInfo; @@ -322,7 +320,7 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event) bool inSelectionBlock = false; bool alternate = m_visibleStart % 8; - const u32 curPC = m_cpu->getPC(); // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs + const u32 curPC = cpu().getPC(); // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs for (u32 i = 0; i <= m_visibleRows; i++) { @@ -347,7 +345,7 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event) // Breakpoint marker bool enabled; - if (CBreakPoints::IsAddressBreakPoint(m_cpu->getCpuType(), rowAddress, &enabled) && !CBreakPoints::IsTempBreakPoint(m_cpu->getCpuType(), rowAddress)) + if (CBreakPoints::IsAddressBreakPoint(cpu().getCpuType(), rowAddress, &enabled) && !CBreakPoints::IsTempBreakPoint(cpu().getCpuType(), rowAddress)) { if (enabled) { @@ -506,11 +504,11 @@ void DisassemblyWidget::mousePressEvent(QMouseEvent* event) void DisassemblyWidget::mouseDoubleClickEvent(QMouseEvent* event) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; const u32 selectedAddress = (static_cast<int>(event->position().y()) / m_rowHeight * 4) + m_visibleStart; - const BreakPointCpu cpuType = m_cpu->getCpuType(); + const BreakPointCpu cpuType = cpu().getCpuType(); if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddress)) { Host::RunOnCPUThread([cpuType, selectedAddress] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddress); }); @@ -598,7 +596,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event) contextFollowBranch(); break; case Qt::Key_Left: - gotoAddressAndSetFocus(m_cpu->getPC()); + gotoAddressAndSetFocus(cpu().getPC()); break; case Qt::Key_O: m_showInstructionOpcode = !m_showInstructionOpcode; @@ -610,7 +608,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event) void DisassemblyWidget::customMenuRequested(QPoint pos) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; QMenu* contextMenu = new QMenu(this); @@ -623,7 +621,7 @@ void DisassemblyWidget::customMenuRequested(QPoint pos) contextMenu->addAction(action = new QAction(tr("&Copy Instruction Text"), this)); action->setShortcut(QKeySequence(Qt::Key_C)); connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText); - if (m_cpu->GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart)) + if (cpu().GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart)) { contextMenu->addAction(action = new QAction(tr("Copy Function Name"), this)); connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyFunctionName); @@ -695,18 +693,18 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon { DisassemblyLineInfo line; - if (!m_cpu->isValidAddress(address)) + if (!cpu().isValidAddress(address)) return tr("%1 NOT VALID ADDRESS").arg(address, 8, 16, QChar('0')).toUpper(); // Todo? support non symbol view? m_disassemblyManager.getLine(address, true, line); - const bool isConditional = line.info.isConditional && m_cpu->getPC() == address; + const bool isConditional = line.info.isConditional && cpu().getPC() == address; const bool isConditionalMet = line.info.conditionMet; - const bool isCurrentPC = m_cpu->getPC() == address; + const bool isCurrentPC = cpu().getPC() == address; - FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(address); - SymbolInfo symbol = m_cpu->GetSymbolGuardian().SymbolStartingAtAddress(address); - const bool showOpcode = m_showInstructionOpcode && m_cpu->isAlive(); + FunctionInfo function = cpu().GetSymbolGuardian().FunctionStartingAtAddress(address); + SymbolInfo symbol = cpu().GetSymbolGuardian().SymbolStartingAtAddress(address); + const bool showOpcode = m_showInstructionOpcode && cpu().isAlive(); QString lineString; if (showOpcode) @@ -739,7 +737,7 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon if (showOpcode) { - const u32 opcode = m_cpu->read32(address); + const u32 opcode = cpu().read32(address); lineString = lineString.arg(QtUtils::FilledQStringFromValue(opcode, 16)); } @@ -789,7 +787,7 @@ QColor DisassemblyWidget::GetAddressFunctionColor(u32 address) // Use the address to pick the colour since the value of the handle may // change from run to run. ccc::Address function_address = - m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address).address; + cpu().GetSymbolGuardian().FunctionOverlappingAddress(address).address; if (!function_address.valid()) return palette().text().color(); @@ -817,7 +815,7 @@ QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo) } else // INSTRUCTIONHEX { - infoBlock += FilledQStringFromValue(m_cpu->read32(i), 16); + infoBlock += FilledQStringFromValue(cpu().read32(i), 16); } } return infoBlock; @@ -831,7 +829,7 @@ void DisassemblyWidget::gotoAddressAndSetFocus(u32 address) void DisassemblyWidget::gotoProgramCounterOnPause() { if (m_goToProgramCounterOnPause) - gotoAddress(m_cpu->getPC(), false); + gotoAddress(cpu().getPC(), false); } void DisassemblyWidget::gotoAddress(u32 address, bool should_set_focus) @@ -861,7 +859,7 @@ bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end) bool DisassemblyWidget::FunctionCanRestore(u32 address) { - FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address); + FunctionInfo function = cpu().GetSymbolGuardian().FunctionOverlappingAddress(address); if (function.address.valid()) address = function.address.value; diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.h b/pcsx2-qt/Debugger/DisassemblyWidget.h index 9d2670c487..85a51f692e 100644 --- a/pcsx2-qt/Debugger/DisassemblyWidget.h +++ b/pcsx2-qt/Debugger/DisassemblyWidget.h @@ -5,24 +5,22 @@ #include "ui_DisassemblyWidget.h" +#include "DebuggerWidget.h" + #include "pcsx2/DebugTools/DebugInterface.h" #include "pcsx2/DebugTools/DisassemblyManager.h" -#include <QtWidgets/QWidget> #include <QtWidgets/QMenu> #include <QtGui/QPainter> -class DisassemblyWidget final : public QWidget +class DisassemblyWidget final : public DebuggerWidget { Q_OBJECT public: - DisassemblyWidget(QWidget* parent); + DisassemblyWidget(DebugInterface& cpu, QWidget* parent = nullptr); ~DisassemblyWidget(); - // Required because our constructor needs to take no extra arguments. - void SetCpu(DebugInterface* cpu); - // Required for the breakpoint list (ugh wtf) QString GetLineDisasm(u32 address); @@ -69,9 +67,8 @@ signals: void VMUpdate(); private: - Ui::DisassemblyWidget ui; + Ui::DisassemblyWidget m_ui; - DebugInterface* m_cpu; u32 m_visibleStart = 0x00336318; // The address of the first opcode shown(row 0) u32 m_visibleRows; u32 m_selectedAddressStart = 0; diff --git a/pcsx2-qt/Debugger/DockManager.cpp b/pcsx2-qt/Debugger/DockManager.cpp new file mode 100644 index 0000000000..409c3cece8 --- /dev/null +++ b/pcsx2-qt/Debugger/DockManager.cpp @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "DockManager.h" + +#include "DebuggerWindow.h" +#include "DisassemblyWidget.h" +#include "RegisterWidget.h" +#include "StackWidget.h" +#include "ThreadWidget.h" +#include "Breakpoints/BreakpointWidget.h" +#include "Memory/MemorySearchWidget.h" +#include "Memory/MemoryViewWidget.h" +#include "Memory/SavedAddressesWidget.h" +#include "SymbolTree/SymbolTreeWidgets.h" + +#include <kddockwidgets/Config.h> + +#include <QtCore/QTimer> +#include <QtCore/QtTranslation> + +#define FOR_EACH_DEBUGGER_DOCK_WIDGET \ + /* Top right. */ \ + X(DisassemblyWidget, QT_TRANSLATE_NOOP("DockWidget", "Disassembly"), OnRight, Root) \ + /* Bottom. */ \ + X(MemoryViewWidget, QT_TRANSLATE_NOOP("DockWidget", "Memory"), OnBottom, DisassemblyWidget) \ + X(BreakpointWidget, QT_TRANSLATE_NOOP("DockWidget", "Breakpoints"), None, MemoryViewWidget) \ + X(ThreadWidget, QT_TRANSLATE_NOOP("DockWidget", "Threads"), None, MemoryViewWidget) \ + X(StackWidget, QT_TRANSLATE_NOOP("DockWidget", "Stack"), None, MemoryViewWidget) \ + X(SavedAddressesWidget, QT_TRANSLATE_NOOP("DockWidget", "Saved Addresses"), None, MemoryViewWidget) \ + X(GlobalVariableTreeWidget, QT_TRANSLATE_NOOP("DockWidget", "Globals"), None, MemoryViewWidget) \ + X(LocalVariableTreeWidget, QT_TRANSLATE_NOOP("DockWidget", "Locals"), None, MemoryViewWidget) \ + X(ParameterVariableTreeWidget, QT_TRANSLATE_NOOP("DockWidget", "Parameters"), None, MemoryViewWidget) \ + /* Top left. */ \ + X(RegisterWidget, QT_TRANSLATE_NOOP("DockWidget", "Registers"), OnLeft, DisassemblyWidget) \ + X(FunctionTreeWidget, QT_TRANSLATE_NOOP("DockWidget", "Functions"), None, RegisterWidget) \ + X(MemorySearchWidget, QT_TRANSLATE_NOOP("DockWidget", "Memory Search"), None, RegisterWidget) + +DockManager::DockManager(DebuggerWindow* window) + : m_window(window) +{ + createDefaultLayout("R5900", r5900Debug); + //createDefaultLayout("R3000", r3000Debug); + loadLayouts(); +} + +void DockManager::configure_docking_system() +{ + KDDockWidgets::Config::self().setFlags( + KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | + KDDockWidgets::Config::Flag_AlwaysShowTabs | + KDDockWidgets::Config::Flag_AllowReorderTabs | + KDDockWidgets::Config::Flag_TabsHaveCloseButton | + KDDockWidgets::Config::Flag_TitleBarIsFocusable); +} + +const std::vector<DockManager::Layout>& DockManager::layouts() +{ + return m_layouts; +} + +void DockManager::switchToLayout(size_t layout) +{ + //m_layouts.at(m_current_layout).dock_manager->setParent(nullptr); + //m_window->setCentralWidget(m_layouts.at(layout).dock_manager); + //m_current_layout = layout; +} + +size_t DockManager::cloneLayout(size_t existing_layout, std::string new_name) +{ + return 0; +} + +bool DockManager::deleteLayout(size_t layout) +{ + return false; +} + +void DockManager::loadLayouts() +{ +} + +void DockManager::saveLayouts() +{ +} + +size_t DockManager::createDefaultLayout(const char* name, DebugInterface& cpu) +{ + size_t index = m_layouts.size(); + + Layout& layout = m_layouts.emplace_back(); + layout.name = name; + layout.cpu = cpu.getCpuType(); + layout.user_defined = false; + + KDDockWidgets::QtWidgets::DockWidget* dock_Root = nullptr; +#define X(Type, title, Location, Parent) \ + KDDockWidgets::QtWidgets::DockWidget* dock_##Type = new KDDockWidgets::QtWidgets::DockWidget(title); \ + dock_##Type->setWidget(new Type(cpu)); \ + if (KDDockWidgets::Location_##Location != KDDockWidgets::Location_None) \ + m_window->addDockWidget(dock_##Type, KDDockWidgets::Location_##Location, dock_##Parent); \ + else \ + dock_##Parent->addDockWidgetAsTab(dock_##Type); + FOR_EACH_DEBUGGER_DOCK_WIDGET +#undef X + + return index; +} diff --git a/pcsx2-qt/Debugger/DockManager.h b/pcsx2-qt/Debugger/DockManager.h new file mode 100644 index 0000000000..430908a8a1 --- /dev/null +++ b/pcsx2-qt/Debugger/DockManager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "DebugTools/DebugInterface.h" + +#include <kddockwidgets/MainWindow.h> +#include <kddockwidgets/DockWidget.h> + +class DebuggerWindow; + +class DockManager +{ +public: + struct Layout + { + std::string name; + BreakPointCpu cpu; + bool user_defined = false; + }; + + DockManager(DebuggerWindow* window); + + static void configure_docking_system(); + + const std::vector<Layout>& layouts(); + void switchToLayout(size_t layout); + size_t cloneLayout(size_t existing_layout, std::string new_name); + bool deleteLayout(size_t layout); + + void loadLayouts(); + void saveLayouts(); + +protected: + size_t createDefaultLayout(const char* name, DebugInterface& cpu); + + KDDockWidgets::QtWidgets::MainWindow* m_window; + + std::vector<Layout> m_layouts; + size_t m_current_layout = 0; +}; diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.cpp b/pcsx2-qt/Debugger/Memory/MemorySearchWidget.cpp similarity index 95% rename from pcsx2-qt/Debugger/MemorySearchWidget.cpp rename to pcsx2-qt/Debugger/Memory/MemorySearchWidget.cpp index a3f1cc419c..cf629f2cca 100644 --- a/pcsx2-qt/Debugger/MemorySearchWidget.cpp +++ b/pcsx2-qt/Debugger/Memory/MemorySearchWidget.cpp @@ -23,8 +23,8 @@ using SearchResult = MemorySearchWidget::SearchResult; using namespace QtUtils; -MemorySearchWidget::MemorySearchWidget(QWidget* parent) - : QWidget(parent) +MemorySearchWidget::MemorySearchWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) { m_ui.setupUi(this); this->repaint(); @@ -32,8 +32,7 @@ MemorySearchWidget::MemorySearchWidget(QWidget* parent) m_ui.listSearchResults->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_ui.btnSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked); connect(m_ui.btnFilterSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked); - connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) - { + connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) { emit switchToMemoryViewTab(); emit goToAddressInMemoryView(item->text().toUInt(nullptr, 16)); }); @@ -48,11 +47,6 @@ MemorySearchWidget::MemorySearchWidget(QWidget* parent) connect(&m_resultsLoadTimer, &QTimer::timeout, this, &MemorySearchWidget::loadSearchResults); } -void MemorySearchWidget::setCpu(DebugInterface* cpu) -{ - m_cpu = cpu; -} - void MemorySearchWidget::contextSearchResultGoToDisassembly() { const QItemSelectionModel* selModel = m_ui.listSearchResults->selectionModel(); @@ -121,15 +115,15 @@ void MemorySearchWidget::onListSearchResultsContextMenu(QPoint pos) contextMenu->popup(m_ui.listSearchResults->viewport()->mapToGlobal(pos)); } -template<typename T> +template <typename T> T readValueAtAddress(DebugInterface* cpu, u32 addr); -template<> +template <> float readValueAtAddress<float>(DebugInterface* cpu, u32 addr) { return std::bit_cast<float>(cpu->read32(addr)); } -template<> +template <> double readValueAtAddress<double>(DebugInterface* cpu, u32 addr) { return std::bit_cast<double>(cpu->read64(addr)); @@ -230,7 +224,7 @@ template <typename T> bool handleSearchComparison(SearchComparison searchComparison, u32 searchAddress, const SearchResult* priorResult, T searchValue, T readValue) { const bool isNotOperator = searchComparison == SearchComparison::NotEquals || searchComparison == SearchComparison::NotChanged; - switch (searchComparison) + switch (searchComparison) { case SearchComparison::Equals: case SearchComparison::NotEquals: @@ -302,7 +296,7 @@ void searchWorker(DebugInterface* cpu, std::vector<SearchResult>& searchResults, { if (!cpu->isValidAddress(addr)) continue; - + T readValue = readValueAtAddress<T>(cpu, addr); if (handleSearchComparison(searchComparison, addr, nullptr, searchValue, readValue)) { @@ -315,7 +309,7 @@ void searchWorker(DebugInterface* cpu, std::vector<SearchResult>& searchResults, auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [cpu, searchType, searchComparison, searchValue](SearchResult& searchResult) -> bool { const u32 addr = searchResult.getAddress(); if (!cpu->isValidAddress(addr)) - return true; + return true; const auto readValue = readValueAtAddress<T>(cpu, addr); @@ -415,7 +409,7 @@ static void searchWorkerByteArray(DebugInterface* cpu, SearchType searchType, Se } else { - auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [ searchComparison, searchType, searchValue, cpu ](SearchResult& searchResult) -> bool { + auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [searchComparison, searchType, searchValue, cpu](SearchResult& searchResult) -> bool { const u32 addr = searchResult.getAddress(); if (!cpu->isValidAddress(addr)) return true; @@ -476,7 +470,7 @@ std::vector<SearchResult> startWorker(DebugInterface* cpu, const SearchType type void MemorySearchWidget::onSearchButtonClicked() { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; const SearchType searchType = getCurrentSearchType(); @@ -575,6 +569,12 @@ void MemorySearchWidget::onSearchButtonClicked() } } + if (!isFilterSearch && (searchComparison == SearchComparison::Changed || searchComparison == SearchComparison::ChangedBy || searchComparison == SearchComparison::Decreased || searchComparison == SearchComparison::DecreasedBy || searchComparison == SearchComparison::Increased || searchComparison == SearchComparison::IncreasedBy || searchComparison == SearchComparison::NotChanged)) + { + QMessageBox::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches.")); + return; + } + QFutureWatcher<std::vector<SearchResult>>* workerWatcher = new QFutureWatcher<std::vector<SearchResult>>(); auto onSearchFinished = [this, workerWatcher] { m_ui.btnSearch->setDisabled(false); @@ -597,7 +597,7 @@ void MemorySearchWidget::onSearchButtonClicked() m_searchResults.clear(); } - QFuture<std::vector<SearchResult>> workerFuture = QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, std::move(m_searchResults), searchStart, searchEnd, searchValue, searchHex ? 16 : 10); + QFuture<std::vector<SearchResult>> workerFuture = QtConcurrent::run(startWorker, &cpu(), searchType, searchComparison, std::move(m_searchResults), searchStart, searchEnd, searchValue, searchHex ? 16 : 10); workerWatcher->setFuture(workerFuture); connect(workerWatcher, &QFutureWatcher<std::vector<SearchResult>>::finished, onSearchFinished); m_searchResults.clear(); @@ -708,7 +708,7 @@ void MemorySearchWidget::updateSearchComparisonSelections() std::vector<SearchComparison> MemorySearchWidget::getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult>& existingResults) { const bool hasResults = existingResults.size() > 0; - std::vector<SearchComparison> comparisons = { SearchComparison::Equals }; + std::vector<SearchComparison> comparisons = {SearchComparison::Equals}; if (type == SearchType::ArrayType || type == SearchType::StringType) { diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.h b/pcsx2-qt/Debugger/Memory/MemorySearchWidget.h similarity index 90% rename from pcsx2-qt/Debugger/MemorySearchWidget.h rename to pcsx2-qt/Debugger/Memory/MemorySearchWidget.h index 50f44e3286..38bf6ba6db 100644 --- a/pcsx2-qt/Debugger/MemorySearchWidget.h +++ b/pcsx2-qt/Debugger/Memory/MemorySearchWidget.h @@ -5,22 +5,23 @@ #include "ui_MemorySearchWidget.h" +#include "Debugger/DebuggerWidget.h" + #include "DebugTools/DebugInterface.h" #include <QtWidgets/QWidget> #include <QtCore/QTimer> #include <QtCore/QMap> -class MemorySearchWidget final : public QWidget +class MemorySearchWidget final : public DebuggerWidget { - Q_OBJECT + Q_OBJECT public: - MemorySearchWidget(QWidget* parent); - ~MemorySearchWidget() = default; - void setCpu(DebugInterface* cpu); + MemorySearchWidget(DebugInterface& cpu, QWidget* parent = nullptr); + ~MemorySearchWidget() = default; - enum class SearchType + enum class SearchType { ByteType, Int16Type, @@ -77,9 +78,11 @@ public: { return labelToEnumMap.value(comparisonLabel, SearchComparison::Invalid); } - QString enumToLabel(SearchComparison comparison) { + QString enumToLabel(SearchComparison comparison) + { return enumToLabelMap.value(comparison, ""); } + private: QMap<SearchComparison, QString> enumToLabelMap; QMap<QString, SearchComparison> labelToEnumMap; @@ -100,7 +103,9 @@ public: public: SearchResult() {} SearchResult(u32 address, const QVariant& value, SearchType type) - : address(address), value(value), type(type) + : address(address) + , value(value) + , type(type) { } bool isIntegerValue() const { return type == SearchType::ByteType || type == SearchType::Int16Type || type == SearchType::Int32Type || type == SearchType::Int64Type; } @@ -111,7 +116,7 @@ public: SearchType getType() const { return type; } QByteArray getArrayValue() const { return isArrayValue() ? value.toByteArray() : QByteArray(); } - template<typename T> + template <typename T> T getValue() const { return value.value<T>(); @@ -139,14 +144,13 @@ private: std::vector<SearchResult> m_searchResults; SearchComparisonLabelMap m_searchComparisonLabelMap; Ui::MemorySearchWidget m_ui; - DebugInterface* m_cpu; QTimer m_resultsLoadTimer; u32 m_initialResultsLoadLimit = 20000; u32 m_numResultsAddedPerLoad = 10000; void updateSearchComparisonSelections(); - std::vector<SearchComparison> getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult> &existingResults); + std::vector<SearchComparison> getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult>& existingResults); SearchType getCurrentSearchType(); SearchComparison getCurrentSearchComparison(); bool doesSearchComparisonTakeInput(SearchComparison comparison); diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.ui b/pcsx2-qt/Debugger/Memory/MemorySearchWidget.ui similarity index 100% rename from pcsx2-qt/Debugger/MemorySearchWidget.ui rename to pcsx2-qt/Debugger/Memory/MemorySearchWidget.ui diff --git a/pcsx2-qt/Debugger/MemoryViewWidget.cpp b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.cpp similarity index 96% rename from pcsx2-qt/Debugger/MemoryViewWidget.cpp rename to pcsx2-qt/Debugger/Memory/MemoryViewWidget.cpp index 5753bcce5d..bbb25a1ec8 100644 --- a/pcsx2-qt/Debugger/MemoryViewWidget.cpp +++ b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.cpp @@ -173,6 +173,10 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32 void MemoryViewTable::SelectAt(QPoint pos) { + // Check if SelectAt was called before DrawTable. + if (rowHeight == 0) + return; + const u32 selectedRow = (pos.y() - 2) / (rowHeight); const s32 x = pos.x(); const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0]; @@ -447,37 +451,37 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar) /* MemoryViewWidget */ -MemoryViewWidget::MemoryViewWidget(QWidget* parent) - : QWidget(parent) +MemoryViewWidget::MemoryViewWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu) , m_table(this) { ui.setupUi(this); this->setFocusPolicy(Qt::FocusPolicy::ClickFocus); connect(this, &MemoryViewWidget::customContextMenuRequested, this, &MemoryViewWidget::customMenuRequested); + + m_table.SetCpu(&cpu); + m_table.UpdateStartAddress(0x480000); + + applyMonospaceFont(); } MemoryViewWidget::~MemoryViewWidget() = default; -void MemoryViewWidget::SetCpu(DebugInterface* cpu) -{ - m_cpu = cpu; - m_table.SetCpu(cpu); - m_table.UpdateStartAddress(0x480000); -} - void MemoryViewWidget::paintEvent(QPaintEvent* event) { - if (!m_cpu->isAlive()) - return; - QPainter painter(this); + painter.fillRect(rect(), palette().window()); + + if (!cpu().isAlive()) + return; + m_table.DrawTable(painter, this->palette(), this->height()); } void MemoryViewWidget::mousePressEvent(QMouseEvent* event) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; m_table.SelectAt(event->pos()); @@ -486,7 +490,7 @@ void MemoryViewWidget::mousePressEvent(QMouseEvent* event) void MemoryViewWidget::customMenuRequested(QPoint pos) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; if (!m_contextMenu) @@ -571,7 +575,7 @@ void MemoryViewWidget::customMenuRequested(QPoint pos) void MemoryViewWidget::contextCopyByte() { - QApplication::clipboard()->setText(QString::number(m_cpu->read8(m_table.selectedAddress), 16).toUpper()); + QApplication::clipboard()->setText(QString::number(cpu().read8(m_table.selectedAddress), 16).toUpper()); } void MemoryViewWidget::contextCopySegment() @@ -581,7 +585,7 @@ void MemoryViewWidget::contextCopySegment() void MemoryViewWidget::contextCopyCharacter() { - QApplication::clipboard()->setText(QChar::fromLatin1(m_cpu->read8(m_table.selectedAddress)).toUpper()); + QApplication::clipboard()->setText(QChar::fromLatin1(cpu().read8(m_table.selectedAddress)).toUpper()); } void MemoryViewWidget::contextPaste() @@ -600,7 +604,7 @@ void MemoryViewWidget::contextGoToAddress() u64 address = 0; std::string error; - if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error)) + if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error)) { QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error)); return; diff --git a/pcsx2-qt/Debugger/MemoryViewWidget.h b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.h similarity index 93% rename from pcsx2-qt/Debugger/MemoryViewWidget.h rename to pcsx2-qt/Debugger/Memory/MemoryViewWidget.h index 03bc5baac8..e410e799a2 100644 --- a/pcsx2-qt/Debugger/MemoryViewWidget.h +++ b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.h @@ -3,7 +3,9 @@ #pragma once -#include "ui_RegisterWidget.h" +#include "ui_MemoryViewWidget.h" + +#include "Debugger/DebuggerWidget.h" #include "DebugTools/DebugInterface.h" #include "DebugTools/DisassemblyManager.h" @@ -105,16 +107,14 @@ public: }; -class MemoryViewWidget final : public QWidget +class MemoryViewWidget final : public DebuggerWidget { Q_OBJECT public: - MemoryViewWidget(QWidget* parent); + MemoryViewWidget(DebugInterface& cpu, QWidget* parent = nullptr); ~MemoryViewWidget(); - void SetCpu(DebugInterface* cpu); - protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); @@ -138,7 +138,7 @@ signals: void VMUpdate(); private: - Ui::RegisterWidget ui; + Ui::MemoryViewWidget ui; QMenu* m_contextMenu = 0x0; QAction* m_actionLittleEndian; @@ -147,6 +147,5 @@ private: QAction* m_actionWORD; QAction* m_actionDWORD; - DebugInterface* m_cpu; MemoryViewTable m_table; }; diff --git a/pcsx2-qt/Debugger/MemoryViewWidget.ui b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.ui similarity index 81% rename from pcsx2-qt/Debugger/MemoryViewWidget.ui rename to pcsx2-qt/Debugger/Memory/MemoryViewWidget.ui index c9d13501bb..54103700aa 100644 --- a/pcsx2-qt/Debugger/MemoryViewWidget.ui +++ b/pcsx2-qt/Debugger/Memory/MemoryViewWidget.ui @@ -10,6 +10,9 @@ <height>300</height> </rect> </property> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> <property name="windowTitle"> <string>Memory</string> </property> diff --git a/pcsx2-qt/Debugger/Models/SavedAddressesModel.cpp b/pcsx2-qt/Debugger/Memory/SavedAddressesModel.cpp similarity index 100% rename from pcsx2-qt/Debugger/Models/SavedAddressesModel.cpp rename to pcsx2-qt/Debugger/Memory/SavedAddressesModel.cpp diff --git a/pcsx2-qt/Debugger/Models/SavedAddressesModel.h b/pcsx2-qt/Debugger/Memory/SavedAddressesModel.h similarity index 100% rename from pcsx2-qt/Debugger/Models/SavedAddressesModel.h rename to pcsx2-qt/Debugger/Memory/SavedAddressesModel.h diff --git a/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.cpp b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.cpp new file mode 100644 index 0000000000..356c53f5c3 --- /dev/null +++ b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.cpp @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "SavedAddressesWidget.h" + +#include "QtUtils.h" +#include "Debugger/DebuggerSettingsManager.h" + +#include <QClipboard> +#include <QMenu> + +SavedAddressesWidget::SavedAddressesWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) + , m_model(cpu) +{ + //m_ui.savedAddressesList->setModel(&m_model); + //m_ui.savedAddressesList->setContextMenuPolicy(Qt::CustomContextMenu); + //connect(m_ui.savedAddressesList, &QTableView::customContextMenuRequested, this, &CpuWidget::onSavedAddressesListContextMenu); + //for (std::size_t i = 0; auto mode : SavedAddressesModel::HeaderResizeModes) + //{ + // m_ui.savedAddressesList->horizontalHeader()->setSectionResizeMode(i++, mode); + //} + //QTableView* savedAddressesTableView = m_ui.savedAddressesList; + //connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) { + // savedAddressesTableView->resizeColumnToContents(topLeft.column()); + //}); +} + +void SavedAddressesWidget::onContextMenu(QPoint pos) +{ + QMenu* contextMenu = new QMenu("Saved Addresses List Context Menu", m_ui.savedAddressesList); + + QAction* newAction = new QAction(tr("New"), m_ui.savedAddressesList); + connect(newAction, &QAction::triggered, this, &SavedAddressesWidget::contextNew); + contextMenu->addAction(newAction); + + const QModelIndex indexAtPos = m_ui.savedAddressesList->indexAt(pos); + const bool isIndexValid = indexAtPos.isValid(); + + if (isIndexValid) + { + if (cpu().isAlive()) + { + QAction* goToAddressMemViewAction = new QAction(tr("Go to in Memory View"), m_ui.savedAddressesList); + connect(goToAddressMemViewAction, &QAction::triggered, this, [this, indexAtPos]() { + const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex()); + //m_ui.memoryviewWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt()); + //m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); + not_yet_implemented(); + }); + contextMenu->addAction(goToAddressMemViewAction); + + QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList); + connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() { + const QModelIndex rowAddressIndex = + m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex()); + //m_ui.disassemblyWidget->gotoAddressAndSetFocus( + // m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt()); + not_yet_implemented(); + }); + contextMenu->addAction(goToAddressDisassemblyAction); + } + + QAction* copyAction = new QAction(indexAtPos.column() == 0 ? tr("Copy Address") : tr("Copy Text"), m_ui.savedAddressesList); + connect(copyAction, &QAction::triggered, [this, indexAtPos]() { + QGuiApplication::clipboard()->setText( + m_ui.savedAddressesList->model()->data(indexAtPos, Qt::DisplayRole).toString()); + }); + contextMenu->addAction(copyAction); + } + + if (m_ui.savedAddressesList->model()->rowCount() > 0) + { + QAction* actionExportCSV = new QAction(tr("Copy all as CSV"), m_ui.savedAddressesList); + connect(actionExportCSV, &QAction::triggered, [this]() { + QGuiApplication::clipboard()->setText( + QtUtils::AbstractItemModelToCSV(m_ui.savedAddressesList->model(), Qt::DisplayRole, true)); + }); + contextMenu->addAction(actionExportCSV); + } + + QAction* actionImportCSV = new QAction(tr("Paste from CSV"), m_ui.savedAddressesList); + connect(actionImportCSV, &QAction::triggered, this, &SavedAddressesWidget::contextPasteCSV); + contextMenu->addAction(actionImportCSV); + + if (cpu().isAlive()) + { + QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.savedAddressesList); + connect(actionLoad, &QAction::triggered, [this]() { + m_model.clear(); + DebuggerSettingsManager::loadGameSettings(&m_model); + }); + contextMenu->addAction(actionLoad); + + QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.savedAddressesList); + connect(actionSave, &QAction::triggered, this, &SavedAddressesWidget::saveToDebuggerSettings); + contextMenu->addAction(actionSave); + } + + if (isIndexValid) + { + QAction* deleteAction = new QAction(tr("Delete"), m_ui.savedAddressesList); + connect(deleteAction, &QAction::triggered, this, [this, indexAtPos]() { + m_ui.savedAddressesList->model()->removeRows(indexAtPos.row(), 1); + }); + contextMenu->addAction(deleteAction); + } + + contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos)); +} + +void SavedAddressesWidget::contextPasteCSV() +{ + QString csv = QGuiApplication::clipboard()->text(); + // Skip header + csv = csv.mid(csv.indexOf('\n') + 1); + + for (const QString& line : csv.split('\n')) + { + QStringList fields; + // In order to handle text with commas in them we must wrap values in quotes to mark + // where a value starts and end so that text commas aren't identified as delimiters. + // So matches each quote pair, parse it out, and removes the quotes to get the value. + QRegularExpression eachQuotePair(R"("([^"]|\\.)*")"); + QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line); + while (it.hasNext()) + { + QRegularExpressionMatch match = it.next(); + QString matchedValue = match.captured(0); + fields << matchedValue.mid(1, matchedValue.length() - 2); + } + + m_model.loadSavedAddressFromFieldList(fields); + } +} + +void SavedAddressesWidget::contextNew() +{ + qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow(); + const u32 rowCount = m_ui.savedAddressesList->model()->rowCount(); + m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 0)); +} + +void SavedAddressesWidget::addAddress(u32 address) +{ + qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow(); + const u32 rowCount = m_ui.savedAddressesList->model()->rowCount(); + const QModelIndex addressIndex = m_ui.savedAddressesList->model()->index(rowCount - 1, 0); + //m_ui.tabWidget->setCurrentWidget(m_ui.tab_savedaddresses); + not_yet_implemented(); + m_ui.savedAddressesList->model()->setData(addressIndex, address, Qt::UserRole); + m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1)); +} + +void SavedAddressesWidget::saveToDebuggerSettings() +{ + DebuggerSettingsManager::saveGameSettings(&m_model); +} diff --git a/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.h b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.h new file mode 100644 index 0000000000..ed73046c65 --- /dev/null +++ b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "ui_SavedAddressesWidget.h" + +#include "SavedAddressesModel.h" + +#include "Debugger/DebuggerWidget.h" + +class SavedAddressesWidget : public DebuggerWidget +{ + Q_OBJECT + +public: + SavedAddressesWidget(DebugInterface& cpu, QWidget* parent = nullptr); + + void onContextMenu(QPoint pos); + void contextPasteCSV(); + void contextNew(); + void addAddress(u32 address); + void saveToDebuggerSettings(); + +private: + Ui::SavedAddressesWidget m_ui; + + SavedAddressesModel m_model; +}; diff --git a/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.ui b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.ui new file mode 100644 index 0000000000..a8d756f623 --- /dev/null +++ b/pcsx2-qt/Debugger/Memory/SavedAddressesWidget.ui @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SavedAddressesWidget</class> + <widget class="QWidget" name="SavedAddressesWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Saved Addresses</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTableWidget" name="savedAddressesList"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/pcsx2-qt/Debugger/RegisterWidget.cpp b/pcsx2-qt/Debugger/RegisterWidget.cpp index b0c80a0bd0..49e4fbf65c 100644 --- a/pcsx2-qt/Debugger/RegisterWidget.cpp +++ b/pcsx2-qt/Debugger/RegisterWidget.cpp @@ -20,8 +20,8 @@ using namespace QtUtils; -RegisterWidget::RegisterWidget(QWidget* parent) - : QWidget(parent) +RegisterWidget::RegisterWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu) { this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); @@ -30,21 +30,19 @@ RegisterWidget::RegisterWidget(QWidget* parent) connect(this, &RegisterWidget::customContextMenuRequested, this, &RegisterWidget::customMenuRequested); connect(ui.registerTabs, &QTabBar::currentChanged, this, &RegisterWidget::tabCurrentChanged); -}; -RegisterWidget::~RegisterWidget() -{ -} - -void RegisterWidget::SetCpu(DebugInterface* cpu) -{ - m_cpu = cpu; - for (int i = 0; i < m_cpu->getRegisterCategoryCount(); i++) + for (int i = 0; i < cpu.getRegisterCategoryCount(); i++) { - ui.registerTabs->addTab(m_cpu->getRegisterCategoryName(i)); + ui.registerTabs->addTab(cpu.getRegisterCategoryName(i)); } connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); }); + + applyMonospaceFont(); +} + +RegisterWidget::~RegisterWidget() +{ } void RegisterWidget::tabCurrentChanged(int cur) @@ -54,9 +52,6 @@ void RegisterWidget::tabCurrentChanged(int cur) void RegisterWidget::paintEvent(QPaintEvent* event) { - if (!m_cpu) - return; - QPainter painter(this); painter.setPen(this->palette().text().color()); m_renderStart = QPoint(0, ui.registerTabs->pos().y() + ui.registerTabs->size().height()); @@ -94,9 +89,9 @@ void RegisterWidget::paintEvent(QPaintEvent* event) // off of that. // Can probably constexpr the loop out as register names are known during runtime int safeValueStartX = 0; - for (int i = 0; i < m_cpu->getRegisterCount(categoryIndex); i++) + for (int i = 0; i < cpu().getRegisterCount(categoryIndex); i++) { - const int registerNameWidth = strlen(m_cpu->getRegisterName(categoryIndex, i)); + const int registerNameWidth = strlen(cpu().getRegisterName(categoryIndex, i)); if (safeValueStartX < registerNameWidth) { safeValueStartX = registerNameWidth; @@ -110,7 +105,7 @@ void RegisterWidget::paintEvent(QPaintEvent* event) // Make it relative to where we start rendering safeValueStartX += m_renderStart.x(); - for (s32 i = 0; i < m_cpu->getRegisterCount(categoryIndex) - m_rowStart; i++) + for (s32 i = 0; i < cpu().getRegisterCount(categoryIndex) - m_rowStart; i++) { const s32 registerIndex = i + m_rowStart; const int yStart = (i * m_rowHeight) + m_renderStart.y(); @@ -120,11 +115,11 @@ void RegisterWidget::paintEvent(QPaintEvent* event) // Draw register name painter.setPen(this->palette().text().color()); - painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, m_cpu->getRegisterName(categoryIndex, registerIndex)); + painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, cpu().getRegisterName(categoryIndex, registerIndex)); - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) { - const u128 curRegister = m_cpu->getRegister(categoryIndex, registerIndex); + const u128 curRegister = cpu().getRegister(categoryIndex, registerIndex); int regIndex = 3; for (int j = 0; j < 4; j++) @@ -136,7 +131,7 @@ void RegisterWidget::paintEvent(QPaintEvent* event) if (categoryIndex == EECAT_VU0F && m_showVU0FFloat) painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight, Qt::AlignLeft, - painter.fontMetrics().elidedText(QString::number(std::bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth())); + painter.fontMetrics().elidedText(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth())); else painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight, Qt::AlignLeft, FilledQStringFromValue(curRegister._u32[regIndex], 16)); @@ -153,13 +148,13 @@ void RegisterWidget::paintEvent(QPaintEvent* event) if (categoryIndex == EECAT_FPR && m_showFPRFloat) painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, - QString("%1").arg(QString::number(std::bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper()); - else if (m_cpu->getRegisterSize(categoryIndex) == 64) + QString("%1").arg(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper()); + else if (cpu().getRegisterSize(categoryIndex) == 64) painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, - FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex).lo, 16)); + FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex).lo, 16)); else painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, - FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0], 16)); + FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex)._u32[0], 16)); } } painter.end(); @@ -171,7 +166,7 @@ void RegisterWidget::mousePressEvent(QMouseEvent* event) m_selectedRow = static_cast<int>(((event->position().y() - m_renderStart.y()) / m_rowHeight)) + m_rowStart; // For 128 bit types, support selecting segments - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) { constexpr auto inRange = [](u32 low, u32 high, u32 val) { return (low <= val && val <= high); @@ -190,7 +185,7 @@ void RegisterWidget::mousePressEvent(QMouseEvent* event) void RegisterWidget::wheelEvent(QWheelEvent* event) { - if (event->angleDelta().y() < 0 && m_rowEnd < m_cpu->getRegisterCount(ui.registerTabs->currentIndex())) + if (event->angleDelta().y() < 0 && m_rowEnd < cpu().getRegisterCount(ui.registerTabs->currentIndex())) { m_rowStart += 1; } @@ -204,12 +199,12 @@ void RegisterWidget::wheelEvent(QWheelEvent* event) void RegisterWidget::mouseDoubleClickEvent(QMouseEvent* event) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative) return; const int categoryIndex = ui.registerTabs->currentIndex(); - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) contextChangeSegment(); else contextChangeValue(); @@ -217,7 +212,7 @@ void RegisterWidget::mouseDoubleClickEvent(QMouseEvent* event) void RegisterWidget::customMenuRequested(QPoint pos) { - if (!m_cpu->isAlive()) + if (!cpu().isAlive()) return; if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative) @@ -248,7 +243,7 @@ void RegisterWidget::customMenuRequested(QPoint pos) m_contextMenu->addSeparator(); } - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) { m_contextMenu->addAction(action = new QAction(tr("Copy Top Half"), this)); connect(action, &QAction::triggered, this, &RegisterWidget::contextCopyTop); @@ -265,7 +260,7 @@ void RegisterWidget::customMenuRequested(QPoint pos) m_contextMenu->addSeparator(); - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) { m_contextMenu->addAction(action = new QAction(tr("Change Top Half"), this)); connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeTop); @@ -295,7 +290,7 @@ void RegisterWidget::customMenuRequested(QPoint pos) void RegisterWidget::contextCopyValue() { const int categoryIndex = ui.registerTabs->currentIndex(); - const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow); + const u128 val = cpu().getRegister(categoryIndex, m_selectedRow); if (CAT_SHOW_FLOAT) QApplication::clipboard()->setText(QString("%1").arg(QString::number(std::bit_cast<float>(val._u32[0])).toUpper(), 16)); else @@ -305,21 +300,21 @@ void RegisterWidget::contextCopyValue() void RegisterWidget::contextCopyTop() { const int categoryIndex = ui.registerTabs->currentIndex(); - const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow); + const u128 val = cpu().getRegister(categoryIndex, m_selectedRow); QApplication::clipboard()->setText(FilledQStringFromValue(val.hi, 16)); } void RegisterWidget::contextCopyBottom() { const int categoryIndex = ui.registerTabs->currentIndex(); - const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow); + const u128 val = cpu().getRegister(categoryIndex, m_selectedRow); QApplication::clipboard()->setText(FilledQStringFromValue(val.lo, 16)); } void RegisterWidget::contextCopySegment() { const int categoryIndex = ui.registerTabs->currentIndex(); - const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow); + const u128 val = cpu().getRegister(categoryIndex, m_selectedRow); if (CAT_SHOW_FLOAT) QApplication::clipboard()->setText(FilledQStringFromValue(std::bit_cast<float>(val._u32[3 - m_selected128Field]), 10)); else @@ -330,7 +325,7 @@ bool RegisterWidget::contextFetchNewValue(u64& out, u64 currentValue, bool segme { const int categoryIndex = ui.registerTabs->currentIndex(); const bool floatingPoint = CAT_SHOW_FLOAT && segment; - const int regSize = m_cpu->getRegisterSize(categoryIndex); + const int regSize = cpu().getRegisterSize(categoryIndex); bool ok = false; QString existingValue("%1"); @@ -341,7 +336,7 @@ bool RegisterWidget::contextFetchNewValue(u64& out, u64 currentValue, bool segme existingValue = existingValue.arg(std::bit_cast<float>((u32)currentValue)); //: Changing the value in a CPU register (e.g. "Change t0") - QString input = QInputDialog::getText(this, tr("Change %1").arg(m_cpu->getRegisterName(categoryIndex, m_selectedRow)), "", + QString input = QInputDialog::getText(this, tr("Change %1").arg(cpu().getRegisterName(categoryIndex, m_selectedRow)), "", QLineEdit::Normal, existingValue, &ok); if (!ok) @@ -373,9 +368,9 @@ void RegisterWidget::contextChangeValue() { const int categoryIndex = ui.registerTabs->currentIndex(); u64 newVal; - if (contextFetchNewValue(newVal, m_cpu->getRegister(categoryIndex, m_selectedRow).lo)) + if (contextFetchNewValue(newVal, cpu().getRegister(categoryIndex, m_selectedRow).lo)) { - m_cpu->setRegister(categoryIndex, m_selectedRow, u128::From64(newVal)); + cpu().setRegister(categoryIndex, m_selectedRow, u128::From64(newVal)); VMUpdate(); } } @@ -383,11 +378,11 @@ void RegisterWidget::contextChangeValue() void RegisterWidget::contextChangeTop() { u64 newVal; - u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow); + u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow); if (contextFetchNewValue(newVal, oldVal.hi)) { oldVal.hi = newVal; - m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); + cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); VMUpdate(); } } @@ -395,11 +390,11 @@ void RegisterWidget::contextChangeTop() void RegisterWidget::contextChangeBottom() { u64 newVal; - u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow); + u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow); if (contextFetchNewValue(newVal, oldVal.lo)) { oldVal.lo = newVal; - m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); + cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); VMUpdate(); } } @@ -407,11 +402,11 @@ void RegisterWidget::contextChangeBottom() void RegisterWidget::contextChangeSegment() { u64 newVal; - u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow); + u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow); if (contextFetchNewValue(newVal, oldVal._u32[3 - m_selected128Field], true)) { oldVal._u32[3 - m_selected128Field] = (u32)newVal; - m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); + cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal); VMUpdate(); } } @@ -419,15 +414,15 @@ void RegisterWidget::contextChangeSegment() void RegisterWidget::contextGotoDisasm() { const int categoryIndex = ui.registerTabs->currentIndex(); - u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow); + u128 regVal = cpu().getRegister(categoryIndex, m_selectedRow); u32 addr = 0; - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) addr = regVal._u32[3 - m_selected128Field]; else addr = regVal._u32[0]; - if (m_cpu->isValidAddress(addr)) + if (cpu().isValidAddress(addr)) gotoInDisasm(addr); else QMessageBox::warning(this, tr("Invalid target address"), ("This register holds an invalid address.")); @@ -436,10 +431,10 @@ void RegisterWidget::contextGotoDisasm() void RegisterWidget::contextGotoMemory() { const int categoryIndex = ui.registerTabs->currentIndex(); - u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow); + u128 regVal = cpu().getRegister(categoryIndex, m_selectedRow); u32 addr = 0; - if (m_cpu->getRegisterSize(categoryIndex) == 128) + if (cpu().getRegisterSize(categoryIndex) == 128) addr = regVal._u32[3 - m_selected128Field]; else addr = regVal._u32[0]; diff --git a/pcsx2-qt/Debugger/RegisterWidget.h b/pcsx2-qt/Debugger/RegisterWidget.h index cfe9cb4799..f114cca55e 100644 --- a/pcsx2-qt/Debugger/RegisterWidget.h +++ b/pcsx2-qt/Debugger/RegisterWidget.h @@ -5,24 +5,23 @@ #include "ui_RegisterWidget.h" +#include "DebuggerWidget.h" + #include "DebugTools/DebugInterface.h" #include "DebugTools/DisassemblyManager.h" -#include <QtWidgets/QWidget> #include <QtWidgets/QMenu> #include <QtWidgets/QTabBar> #include <QtGui/QPainter> -class RegisterWidget final : public QWidget +class RegisterWidget final : public DebuggerWidget { Q_OBJECT public: - RegisterWidget(QWidget* parent); + RegisterWidget(DebugInterface& cpu, QWidget* parent = nullptr); ~RegisterWidget(); - void SetCpu(DebugInterface* cpu); - protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); @@ -58,8 +57,6 @@ private: // Returns true on success bool contextFetchNewValue(u64& out, u64 currentValue, bool segment = false); - DebugInterface* m_cpu; - // Used for the height offset the tab bar creates // because we share a widget QPoint m_renderStart; diff --git a/pcsx2-qt/Debugger/RegisterWidget.ui b/pcsx2-qt/Debugger/RegisterWidget.ui index 63388d9f9d..fb5f5dd971 100644 --- a/pcsx2-qt/Debugger/RegisterWidget.ui +++ b/pcsx2-qt/Debugger/RegisterWidget.ui @@ -11,9 +11,9 @@ </rect> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> - <horstretch>1</horstretch> - <verstretch>1</verstretch> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> diff --git a/pcsx2-qt/Debugger/Models/StackModel.cpp b/pcsx2-qt/Debugger/StackModel.cpp similarity index 100% rename from pcsx2-qt/Debugger/Models/StackModel.cpp rename to pcsx2-qt/Debugger/StackModel.cpp diff --git a/pcsx2-qt/Debugger/Models/StackModel.h b/pcsx2-qt/Debugger/StackModel.h similarity index 100% rename from pcsx2-qt/Debugger/Models/StackModel.h rename to pcsx2-qt/Debugger/StackModel.h diff --git a/pcsx2-qt/Debugger/StackWidget.cpp b/pcsx2-qt/Debugger/StackWidget.cpp new file mode 100644 index 0000000000..805376ead7 --- /dev/null +++ b/pcsx2-qt/Debugger/StackWidget.cpp @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "StackWidget.h" + +#include "QtUtils.h" + +#include <QClipboard> +#include <QMenu> + +StackWidget::StackWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) + , m_model(cpu) +{ + m_ui.setupUi(this); + + connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &StackWidget::onContextMenu); + connect(m_ui.stackList, &QTableView::doubleClicked, this, &StackWidget::onDoubleClick); + + m_ui.stackList->setModel(&m_model); + for (std::size_t i = 0; auto mode : StackModel::HeaderResizeModes) + { + m_ui.stackList->horizontalHeader()->setSectionResizeMode(i, mode); + i++; + } +} + +void StackWidget::onContextMenu(QPoint pos) +{ + if (!m_ui.stackList->selectionModel()->hasSelection()) + return; + + QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList); + + QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList); + connect(actionCopy, &QAction::triggered, [this]() { + const auto* selModel = m_ui.stackList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString()); + }); + contextMenu->addAction(actionCopy); + + contextMenu->addSeparator(); + + QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.stackList); + connect(actionExport, &QAction::triggered, [this]() { + QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.stackList->model())); + }); + contextMenu->addAction(actionExport); + + contextMenu->popup(m_ui.stackList->viewport()->mapToGlobal(pos)); +} + +void StackWidget::onDoubleClick(const QModelIndex& index) +{ + switch (index.column()) + { + case StackModel::StackModel::ENTRY: + case StackModel::StackModel::ENTRY_LABEL: + //m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt()); + not_yet_implemented(); + break; + case StackModel::StackModel::SP: + //m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt()); + //m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); + not_yet_implemented(); + break; + default: // Default to PC + //m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt()); + not_yet_implemented(); + break; + } +} diff --git a/pcsx2-qt/Debugger/StackWidget.h b/pcsx2-qt/Debugger/StackWidget.h new file mode 100644 index 0000000000..32b5907da2 --- /dev/null +++ b/pcsx2-qt/Debugger/StackWidget.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "ui_StackWidget.h" + +#include "StackModel.h" + +#include "DebuggerWidget.h" + +class StackWidget final : public DebuggerWidget +{ + Q_OBJECT + +public: + StackWidget(DebugInterface& cpu, QWidget* parent = nullptr); + + void onContextMenu(QPoint pos); + void onDoubleClick(const QModelIndex& index); + +private: + Ui::StackWidget m_ui; + + StackModel m_model; +}; diff --git a/pcsx2-qt/Debugger/StackWidget.ui b/pcsx2-qt/Debugger/StackWidget.ui new file mode 100644 index 0000000000..44830715af --- /dev/null +++ b/pcsx2-qt/Debugger/StackWidget.ui @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>StackWidget</class> + <widget class="QWidget" name="StackWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Stack</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTableView" name="stackList"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidget.ui b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidget.ui index 4fb07245f4..de979d7e08 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidget.ui +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidget.ui @@ -6,12 +6,12 @@ <rect> <x>0</x> <y>0</y> - <width>419</width> + <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> - <string>Form</string> + <string>Symbol Tree</string> </property> <layout class="QVBoxLayout" name="vertical_layout"> <property name="spacing"> diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.cpp b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.cpp index a8b8ade04f..e86d8af394 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.cpp +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.cpp @@ -14,8 +14,12 @@ static bool testName(const QString& name, const QString& filter); -SymbolTreeWidget::SymbolTreeWidget(u32 flags, s32 symbol_address_alignment, DebugInterface& cpu, QWidget* parent) - : QWidget(parent) +SymbolTreeWidget::SymbolTreeWidget( + u32 flags, + s32 symbol_address_alignment, + DebugInterface& cpu, + QWidget* parent) + : DebuggerWidget(&cpu, parent) , m_cpu(cpu) , m_flags(flags) , m_symbol_address_alignment(symbol_address_alignment) @@ -661,7 +665,11 @@ SymbolTreeNode* SymbolTreeWidget::currentNode() // ***************************************************************************** FunctionTreeWidget::FunctionTreeWidget(DebugInterface& cpu, QWidget* parent) - : SymbolTreeWidget(ALLOW_GROUPING | ALLOW_MANGLED_NAME_ACTIONS, 4, cpu, parent) + : SymbolTreeWidget( + ALLOW_GROUPING | ALLOW_MANGLED_NAME_ACTIONS, + 4, + cpu, + parent) { } @@ -745,7 +753,11 @@ void FunctionTreeWidget::onNewButtonPressed() // ***************************************************************************** GlobalVariableTreeWidget::GlobalVariableTreeWidget(DebugInterface& cpu, QWidget* parent) - : SymbolTreeWidget(ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS | ALLOW_MANGLED_NAME_ACTIONS, 1, cpu, parent) + : SymbolTreeWidget( + ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS | ALLOW_MANGLED_NAME_ACTIONS, + 1, + cpu, + parent) { } @@ -884,7 +896,11 @@ void GlobalVariableTreeWidget::onNewButtonPressed() // ***************************************************************************** LocalVariableTreeWidget::LocalVariableTreeWidget(DebugInterface& cpu, QWidget* parent) - : SymbolTreeWidget(ALLOW_TYPE_ACTIONS, 1, cpu, parent) + : SymbolTreeWidget( + ALLOW_TYPE_ACTIONS, + 1, + cpu, + parent) { } @@ -1009,7 +1025,11 @@ void LocalVariableTreeWidget::onNewButtonPressed() // ***************************************************************************** ParameterVariableTreeWidget::ParameterVariableTreeWidget(DebugInterface& cpu, QWidget* parent) - : SymbolTreeWidget(ALLOW_TYPE_ACTIONS, 1, cpu, parent) + : SymbolTreeWidget( + ALLOW_TYPE_ACTIONS, + 1, + cpu, + parent) { } diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.h b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.h index b74138bbc9..0186b46dbf 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.h +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeWidgets.h @@ -3,7 +3,7 @@ #pragma once -#include <QtWidgets/QWidget> +#include "Debugger/DebuggerWidget.h" #include "SymbolTreeModel.h" #include "ui_SymbolTreeWidget.h" @@ -12,7 +12,7 @@ struct SymbolFilters; // A symbol tree widget with its associated refresh button, filter box and // right-click menu. Supports grouping, sorting and various other settings. -class SymbolTreeWidget : public QWidget +class SymbolTreeWidget : public DebuggerWidget { Q_OBJECT @@ -41,7 +41,11 @@ protected: const ccc::SourceFile* source_file = nullptr; }; - SymbolTreeWidget(u32 flags, s32 symbol_address_alignment, DebugInterface& cpu, QWidget* parent = nullptr); + SymbolTreeWidget( + u32 flags, + s32 symbol_address_alignment, + DebugInterface& cpu, + QWidget* parent = nullptr); void resizeEvent(QResizeEvent* event) override; diff --git a/pcsx2-qt/Debugger/Models/ThreadModel.cpp b/pcsx2-qt/Debugger/ThreadModel.cpp similarity index 100% rename from pcsx2-qt/Debugger/Models/ThreadModel.cpp rename to pcsx2-qt/Debugger/ThreadModel.cpp diff --git a/pcsx2-qt/Debugger/Models/ThreadModel.h b/pcsx2-qt/Debugger/ThreadModel.h similarity index 100% rename from pcsx2-qt/Debugger/Models/ThreadModel.h rename to pcsx2-qt/Debugger/ThreadModel.h diff --git a/pcsx2-qt/Debugger/ThreadWidget.cpp b/pcsx2-qt/Debugger/ThreadWidget.cpp new file mode 100644 index 0000000000..73401586ad --- /dev/null +++ b/pcsx2-qt/Debugger/ThreadWidget.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "ThreadWidget.h" + +#include "QtUtils.h" + +#include <QtGui/QClipboard> +#include <QtWidgets/QMenu> + +ThreadWidget::ThreadWidget(DebugInterface& cpu, QWidget* parent) + : DebuggerWidget(&cpu, parent) + , m_model(cpu) +{ + m_ui.setupUi(this); + + connect(m_ui.threadList, &QTableView::customContextMenuRequested, this, &ThreadWidget::onContextMenu); + connect(m_ui.threadList, &QTableView::doubleClicked, this, &ThreadWidget::onDoubleClick); + + m_proxy_model.setSourceModel(&m_model); + m_proxy_model.setSortRole(Qt::UserRole); + m_ui.threadList->setModel(&m_proxy_model); + m_ui.threadList->setSortingEnabled(true); + m_ui.threadList->sortByColumn(ThreadModel::ThreadColumns::ID, Qt::SortOrder::AscendingOrder); + for (std::size_t i = 0; auto mode : ThreadModel::HeaderResizeModes) + { + m_ui.threadList->horizontalHeader()->setSectionResizeMode(i, mode); + i++; + } +} + +void ThreadWidget::onContextMenu(QPoint pos) +{ + if (!m_ui.threadList->selectionModel()->hasSelection()) + return; + + QMenu* contextMenu = new QMenu(tr("Thread List Context Menu"), m_ui.threadList); + + QAction* actionCopy = new QAction(tr("Copy"), m_ui.threadList); + connect(actionCopy, &QAction::triggered, [this]() { + const auto* selModel = m_ui.threadList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + QGuiApplication::clipboard()->setText(m_ui.threadList->model()->data(selModel->currentIndex()).toString()); + }); + contextMenu->addAction(actionCopy); + + contextMenu->addSeparator(); + + QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.threadList); + connect(actionExport, &QAction::triggered, [this]() { + QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.threadList->model())); + }); + contextMenu->addAction(actionExport); + + contextMenu->popup(m_ui.threadList->viewport()->mapToGlobal(pos)); +} + +void ThreadWidget::onDoubleClick(const QModelIndex& index) +{ + switch (index.column()) + { + case ThreadModel::ThreadColumns::ENTRY: + not_yet_implemented(); + //m_ui.memoryviewWidget->gotoAddress(m_ui.threadList->model()->data(index, Qt::UserRole).toUInt()); + //m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); + break; + default: // Default to PC + not_yet_implemented(); + //m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt()); + break; + } +} diff --git a/pcsx2-qt/Debugger/ThreadWidget.h b/pcsx2-qt/Debugger/ThreadWidget.h new file mode 100644 index 0000000000..f0d71064d7 --- /dev/null +++ b/pcsx2-qt/Debugger/ThreadWidget.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#pragma once + +#include "ui_ThreadWidget.h" + +#include "DebuggerWidget.h" +#include "ThreadModel.h" + +#include <QSortFilterProxyModel> + +class ThreadWidget final : public DebuggerWidget +{ + Q_OBJECT + +public: + ThreadWidget(DebugInterface& cpu, QWidget* parent = nullptr); + + void onContextMenu(QPoint pos); + void onDoubleClick(const QModelIndex& index); + +private: + Ui::ThreadWidget m_ui; + + ThreadModel m_model; + QSortFilterProxyModel m_proxy_model; +}; diff --git a/pcsx2-qt/Debugger/ThreadWidget.ui b/pcsx2-qt/Debugger/ThreadWidget.ui new file mode 100644 index 0000000000..4d498c2146 --- /dev/null +++ b/pcsx2-qt/Debugger/ThreadWidget.ui @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ThreadWidget</class> + <widget class="QWidget" name="ThreadWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Threads</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTableView" name="threadList"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index bdac5bcce8..5c12a1b923 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -2782,8 +2782,13 @@ void MainWindow::doSettings(const char* category /* = nullptr */) DebuggerWindow* MainWindow::getDebuggerWindow() { if (!m_debugger_window) + { + // Setup KDDockWidgets. + DockManager::configure_docking_system(); + // Don't pass us (this) as the parent, otherwise the window is always on top of the mainwindow (on windows at least) m_debugger_window = new DebuggerWindow(nullptr); + } return m_debugger_window; } diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index 1b89dc42e0..d15b8528a7 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -114,7 +114,7 @@ public Q_SLOTS: void endCapture(); void setAudioOutputVolume(int volume, int fast_forward_volume); void setAudioOutputMuted(bool muted); - + Q_SIGNALS: bool messageConfirmed(const QString& title, const QString& message); void statusMessage(const QString& message); diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index 11d29cddeb..082edce0e4 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -49,7 +49,7 @@ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\ccc\src</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)pcsx2</AdditionalIncludeDirectories> <!-- Needed for moc pch --> - <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger;$(ProjectDir)\Debugger\Models;$(ProjectDir)\Debugger\SymbolTree</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\Tools\InputRecording;$(ProjectDir)\Debugger;$(ProjectDir)\Debugger\Breakpoints;$(ProjectDir)\Debugger\Memory;$(ProjectDir)\Debugger\SymbolTree</AdditionalIncludeDirectories> <PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile> <ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles> @@ -110,18 +110,23 @@ <ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp" /> <ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" /> <ClCompile Include="Debugger\AnalysisOptionsDialog.cpp" /> - <ClCompile Include="Debugger\CpuWidget.cpp" /> + <ClCompile Include="Debugger\DebuggerWidget.cpp" /> <ClCompile Include="Debugger\DebuggerWindow.cpp" /> <ClCompile Include="Debugger\DisassemblyWidget.cpp" /> - <ClCompile Include="Debugger\MemorySearchWidget.cpp" /> - <ClCompile Include="Debugger\MemoryViewWidget.cpp" /> + <ClCompile Include="Debugger\DockManager.cpp" /> <ClCompile Include="Debugger\RegisterWidget.cpp" /> - <ClCompile Include="Debugger\BreakpointDialog.cpp" /> <ClCompile Include="Debugger\DebuggerSettingsManager.cpp" /> - <ClCompile Include="Debugger\Models\BreakpointModel.cpp" /> - <ClCompile Include="Debugger\Models\ThreadModel.cpp" /> - <ClCompile Include="Debugger\Models\StackModel.cpp" /> - <ClCompile Include="Debugger\Models\SavedAddressesModel.cpp" /> + <ClCompile Include="Debugger\StackModel.cpp" /> + <ClCompile Include="Debugger\StackWidget.cpp" /> + <ClCompile Include="Debugger\ThreadModel.cpp" /> + <ClCompile Include="Debugger\ThreadWidget.cpp" /> + <ClCompile Include="Debugger\Breakpoints\BreakpointDialog.cpp" /> + <ClCompile Include="Debugger\Breakpoints\BreakpointModel.cpp" /> + <ClCompile Include="Debugger\Breakpoints\BreakpointWidget.cpp" /> + <ClCompile Include="Debugger\Memory\MemorySearchWidget.cpp" /> + <ClCompile Include="Debugger\Memory\MemoryViewWidget.cpp" /> + <ClCompile Include="Debugger\Memory\SavedAddressesModel.cpp" /> + <ClCompile Include="Debugger\Memory\SavedAddressesWidget.cpp" /> <ClCompile Include="Settings\BIOSSettingsWidget.cpp" /> <ClCompile Include="Settings\ControllerBindingWidget.cpp" /> <ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" /> @@ -212,18 +217,23 @@ <QtMoc Include="Tools\InputRecording\InputRecordingViewer.h" /> <ClInclude Include="QtUtils.h" /> <QtMoc Include="Debugger\AnalysisOptionsDialog.h" /> - <QtMoc Include="Debugger\CpuWidget.h" /> + <QtMoc Include="Debugger\DebuggerWidget.h" /> <QtMoc Include="Debugger\DebuggerWindow.h" /> <QtMoc Include="Debugger\DisassemblyWidget.h" /> - <QtMoc Include="Debugger\MemorySearchWidget.h" /> - <QtMoc Include="Debugger\MemoryViewWidget.h" /> + <QtMoc Include="Debugger\DockManager.h" /> <QtMoc Include="Debugger\RegisterWidget.h" /> - <QtMoc Include="Debugger\BreakpointDialog.h" /> + <QtMoc Include="Debugger\StackModel.h" /> + <QtMoc Include="Debugger\StackWidget.h" /> + <QtMoc Include="Debugger\ThreadModel.h" /> + <QtMoc Include="Debugger\ThreadWidget.h" /> <ClInclude Include="Debugger\DebuggerSettingsManager.h" /> - <QtMoc Include="Debugger\Models\BreakpointModel.h" /> - <QtMoc Include="Debugger\Models\ThreadModel.h" /> - <QtMoc Include="Debugger\Models\StackModel.h" /> - <QtMoc Include="Debugger\Models\SavedAddressesModel.h" /> + <QtMoc Include="Debugger\Breakpoints\BreakpointDialog.h" /> + <QtMoc Include="Debugger\Breakpoints\BreakpointModel.h" /> + <QtMoc Include="Debugger\Breakpoints\BreakpointWidget.h" /> + <QtMoc Include="Debugger\Memory\MemorySearchWidget.h" /> + <QtMoc Include="Debugger\Memory\MemoryViewWidget.h" /> + <QtMoc Include="Debugger\Memory\SavedAddressesModel.h" /> + <QtMoc Include="Debugger\Memory\SavedAddressesWidget.h" /> <QtMoc Include="Settings\ControllerBindingWidget.h" /> <QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" /> <ClInclude Include="Settings\MemoryCardConvertWorker.h" /> @@ -272,17 +282,22 @@ <ClCompile Include="$(IntDir)Settings\moc_DebugAnalysisSettingsWidget.cpp" /> <ClCompile Include="$(IntDir)Settings\moc_DebugSettingsWidget.cpp" /> <ClCompile Include="$(IntDir)Debugger\moc_AnalysisOptionsDialog.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_DebuggerWidget.cpp" /> <ClCompile Include="$(IntDir)Debugger\moc_DebuggerWindow.cpp" /> - <ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp" /> <ClCompile Include="$(IntDir)Debugger\moc_DisassemblyWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_DockManager.cpp" /> <ClCompile Include="$(IntDir)Debugger\moc_RegisterWidget.cpp" /> - <ClCompile Include="$(IntDir)Debugger\moc_MemorySearchWidget.cpp" /> - <ClCompile Include="$(IntDir)Debugger\moc_MemoryViewWidget.cpp" /> - <ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp" /> - <ClCompile Include="$(IntDir)Debugger\Models\moc_BreakpointModel.cpp" /> - <ClCompile Include="$(IntDir)Debugger\Models\moc_ThreadModel.cpp" /> - <ClCompile Include="$(IntDir)Debugger\Models\moc_StackModel.cpp" /> - <ClCompile Include="$(IntDir)Debugger\Models\moc_SavedAddressesModel.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_StackModel.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_StackWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_ThreadModel.cpp" /> + <ClCompile Include="$(IntDir)Debugger\moc_ThreadWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointDialog.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointModel.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_MemorySearchWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_MemoryViewWidget.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_SavedAddressesModel.cpp" /> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_SavedAddressesWidget.cpp" /> <ClCompile Include="$(IntDir)Debugger\SymbolTree\moc_NewSymbolDialogs.cpp" /> <ClCompile Include="$(IntDir)Debugger\SymbolTree\moc_SymbolTreeDelegates.cpp" /> <ClCompile Include="$(IntDir)Debugger\SymbolTree\moc_SymbolTreeLocation.cpp" /> @@ -409,19 +424,22 @@ <QtUi Include="Debugger\DisassemblyWidget.ui"> <FileType>Document</FileType> </QtUi> - <QtUi Include="Debugger\CpuWidget.ui"> - <FileType>Document</FileType> - </QtUi> <QtUi Include="Debugger\RegisterWidget.ui"> <FileType>Document</FileType> </QtUi> - <QtUi Include="Debugger\MemorySearchWidget.ui"> + <QtUi Include="Debugger\Breakpoints\BreakpointDialog.ui"> <FileType>Document</FileType> </QtUi> - <QtUi Include="Debugger\MemoryViewWidget.ui"> + <QtUi Include="Debugger\Breakpoints\BreakpointWidget.ui"> <FileType>Document</FileType> </QtUi> - <QtUi Include="Debugger\BreakpointDialog.ui"> + <QtUi Include="Debugger\Memory\MemorySearchWidget.ui"> + <FileType>Document</FileType> + </QtUi> + <QtUi Include="Debugger\Memory\MemoryViewWidget.ui"> + <FileType>Document</FileType> + </QtUi> + <QtUi Include="Debugger\Memory\SavedAddressesWidget.ui"> <FileType>Document</FileType> </QtUi> </ItemGroup> diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index b583ecaaf3..5963e150f7 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -272,43 +272,58 @@ <ClCompile Include="Debugger\AnalysisOptionsDialog.cpp"> <Filter>Debugger</Filter> </ClCompile> + <ClCompile Include="Debugger\DebuggerWidget.cpp"> + <Filter>Debugger</Filter> + </ClCompile> <ClCompile Include="Debugger\DebuggerWindow.cpp"> <Filter>Debugger</Filter> </ClCompile> <ClCompile Include="Debugger\DisassemblyWidget.cpp"> <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\CpuWidget.cpp"> + <ClCompile Include="Debugger\DockManager.cpp"> <Filter>Debugger</Filter> </ClCompile> <ClCompile Include="Debugger\RegisterWidget.cpp"> <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\MemorySearchWidget.cpp"> + <ClCompile Include="Debugger\StackModel.cpp"> <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\MemoryViewWidget.cpp"> + <ClCompile Include="Debugger\StackWidget.cpp"> <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\BreakpointDialog.cpp"> + <ClCompile Include="Debugger\ThreadModel.cpp"> <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\Models\BreakpointModel.cpp"> - <Filter>Debugger\Models</Filter> + <ClCompile Include="Debugger\ThreadWidget.cpp"> + <Filter>Debugger</Filter> </ClCompile> - <ClCompile Include="Debugger\Models\ThreadModel.cpp"> - <Filter>Debugger\Models</Filter> + <ClCompile Include="Debugger\Breakpoints\BreakpointDialog.cpp"> + <Filter>Debugger\Breakpoints</Filter> </ClCompile> - <ClCompile Include="Debugger\Models\StackModel.cpp"> - <Filter>Debugger\Models</Filter> + <ClCompile Include="Debugger\Breakpoints\BreakpointModel.cpp"> + <Filter>Debugger\Breakpoints</Filter> </ClCompile> - <ClCompile Include="Debugger\Models\SavedAddressesModel.cpp"> - <Filter>Debugger\Models</Filter> + <ClCompile Include="Debugger\Breakpoints\BreakpointWidget.cpp"> + <Filter>Debugger\Breakpoints</Filter> + </ClCompile> + <ClCompile Include="Debugger\Memory\MemorySearchWidget.cpp"> + <Filter>Debugger\Memory</Filter> + </ClCompile> + <ClCompile Include="Debugger\Memory\MemoryViewWidget.cpp"> + <Filter>Debugger\Memory</Filter> + </ClCompile> + <ClCompile Include="Debugger\Memory\SavedAddressesModel.cpp"> + <Filter>Debugger\Memory</Filter> + </ClCompile> + <ClCompile Include="Debugger\Memory\SavedAddressesWidget.cpp"> + <Filter>Debugger\Memory</Filter> </ClCompile> <ClCompile Include="$(IntDir)Debugger\moc_AnalysisOptionsDialog.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp"> + <ClCompile Include="$(IntDir)Debugger\moc_DebuggerWidget.cpp"> <Filter>moc</Filter> </ClCompile> <ClCompile Include="$(IntDir)Debugger\moc_DebuggerWindow.cpp"> @@ -317,28 +332,43 @@ <ClCompile Include="$(IntDir)Debugger\moc_DisassemblyWidget.cpp"> <Filter>moc</Filter> </ClCompile> + <ClCompile Include="$(IntDir)Debugger\moc_DockManager.cpp"> + <Filter>moc</Filter> + </ClCompile> <ClCompile Include="$(IntDir)Debugger\moc_RegisterWidget.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\moc_MemorySearchWidget.cpp"> + <ClCompile Include="$(IntDir)Debugger\moc_StackModel.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\moc_MemoryViewWidget.cpp"> + <ClCompile Include="$(IntDir)Debugger\moc_StackWidget.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\moc_BreakpointDialog.cpp"> + <ClCompile Include="$(IntDir)Debugger\moc_ThreadModel.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\Models\moc_BreakpointModel.cpp"> + <ClCompile Include="$(IntDir)Debugger\moc_ThreadWidget.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\Models\moc_ThreadModel.cpp"> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointDialog.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\Models\moc_StackModel.cpp"> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointModel.cpp"> <Filter>moc</Filter> </ClCompile> - <ClCompile Include="$(IntDir)Debugger\Models\moc_SavedAddressesModel.cpp"> + <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointWidget.cpp"> + <Filter>moc</Filter> + </ClCompile> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_MemorySearchWidget.cpp"> + <Filter>moc</Filter> + </ClCompile> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_MemoryViewWidget.cpp"> + <Filter>moc</Filter> + </ClCompile> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_SavedAddressesModel.cpp"> + <Filter>moc</Filter> + </ClCompile> + <ClCompile Include="$(IntDir)Debugger\Memory\moc_SavedAddressesWidget.cpp"> <Filter>moc</Filter> </ClCompile> <ClCompile Include="ColorPickerButton.cpp" /> @@ -541,38 +571,53 @@ <QtMoc Include="Debugger\AnalysisOptionsDialog.h"> <Filter>Debugger</Filter> </QtMoc> + <QtMoc Include="Debugger\DebuggerWidget.h"> + <Filter>Debugger</Filter> + </QtMoc> <QtMoc Include="Debugger\DebuggerWindow.h"> <Filter>Debugger</Filter> </QtMoc> <QtMoc Include="Debugger\DisassemblyWidget.h"> <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\CpuWidget.h"> + <QtMoc Include="Debugger\DockManager.h"> <Filter>Debugger</Filter> </QtMoc> <QtMoc Include="Debugger\RegisterWidget.h"> <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\MemorySearchWidget.h"> + <QtMoc Include="Debugger\StackModel.h"> <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\MemoryViewWidget.h"> + <QtMoc Include="Debugger\StackWidget.h"> <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\BreakpointDialog.h"> + <QtMoc Include="Debugger\ThreadModel.h"> <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\Models\BreakpointModel.h"> - <Filter>Debugger\Models</Filter> + <QtMoc Include="Debugger\ThreadWidget.h"> + <Filter>Debugger</Filter> </QtMoc> - <QtMoc Include="Debugger\Models\ThreadModel.h"> - <Filter>Debugger\Models</Filter> + <QtMoc Include="Debugger\Breakpoints\BreakpointDialog.h"> + <Filter>Debugger\Breakpoints</Filter> </QtMoc> - <QtMoc Include="Debugger\Models\StackModel.h"> - <Filter>Debugger\Models</Filter> + <QtMoc Include="Debugger\Breakpoints\BreakpointModel.h"> + <Filter>Debugger\Breakpoints</Filter> </QtMoc> - <QtMoc Include="Debugger\Models\SavedAddressesModel.h"> - <Filter>Debugger\Models</Filter> + <QtMoc Include="Debugger\Breakpoints\BreakpointWidget.h"> + <Filter>Debugger\Breakpoints</Filter> + </QtMoc> + <QtMoc Include="Debugger\Memory\MemorySearchWidget.h"> + <Filter>Debugger\Memory</Filter> + </QtMoc> + <QtMoc Include="Debugger\Memory\MemoryViewWidget.h"> + <Filter>Debugger\Memory</Filter> + </QtMoc> + <QtMoc Include="Debugger\Memory\SavedAddressesModel.h"> + <Filter>Debugger\Memory</Filter> + </QtMoc> + <QtMoc Include="Debugger\Memory\SavedAddressesWidget.h"> + <Filter>Debugger\Memory</Filter> </QtMoc> <QtMoc Include="ColorPickerButton.h" /> <QtMoc Include="SetupWizardDialog.h" /> @@ -698,20 +743,23 @@ <QtUi Include="Debugger\DisassemblyWidget.ui"> <Filter>Debugger</Filter> </QtUi> - <QtUi Include="Debugger\CpuWidget.ui"> - <Filter>Debugger</Filter> - </QtUi> <QtUi Include="Debugger\RegisterWidget.ui"> <Filter>Debugger</Filter> </QtUi> - <QtUi Include="Debugger\MemorySearchWidget.ui"> - <Filter>Debugger</Filter> + <QtUi Include="Debugger\Breakpoints\BreakpointDialog.ui"> + <Filter>Debugger\Breakpoints</Filter> </QtUi> - <QtUi Include="Debugger\MemoryViewWidget.ui"> - <Filter>Debugger</Filter> + <QtUi Include="Debugger\Breakpoints\BreakpointWidget.ui"> + <Filter>Debugger\Breakpoints</Filter> </QtUi> - <QtUi Include="Debugger\BreakpointDialog.ui"> - <Filter>Debugger</Filter> + <QtUi Include="Debugger\Memory\MemorySearchWidget.ui"> + <Filter>Debugger\Memory</Filter> + </QtUi> + <QtUi Include="Debugger\Memory\MemoryViewWidget.ui"> + <Filter>Debugger\Memory</Filter> + </QtUi> + <QtUi Include="Debugger\Memory\SavedAddressesWidget.ui"> + <Filter>Debugger\Memory</Filter> </QtUi> <QtUi Include="Settings\ControllerLEDSettingsDialog.ui"> <Filter>Settings</Filter>