From ec37ce093f5349a785d056958a187f5391322b4c Mon Sep 17 00:00:00 2001 From: spycrab Date: Wed, 13 Sep 2017 19:33:45 +0200 Subject: [PATCH] Qt/Debugger: Implement "Registers" window --- Source/Core/DolphinQt2/CMakeLists.txt | 2 + .../DolphinQt2/Debugger/RegisterColumn.cpp | 123 +++++++ .../Core/DolphinQt2/Debugger/RegisterColumn.h | 68 ++++ .../DolphinQt2/Debugger/RegisterWidget.cpp | 337 ++++++++++++++++++ .../Core/DolphinQt2/Debugger/RegisterWidget.h | 46 +++ Source/Core/DolphinQt2/DolphinQt2.vcxproj | 8 +- Source/Core/DolphinQt2/MainWindow.cpp | 6 + Source/Core/DolphinQt2/MainWindow.h | 3 + Source/Core/DolphinQt2/MenuBar.cpp | 19 + Source/Core/DolphinQt2/MenuBar.h | 4 + Source/Core/DolphinQt2/Settings.cpp | 15 + Source/Core/DolphinQt2/Settings.h | 4 +- 12 files changed, 633 insertions(+), 2 deletions(-) create mode 100644 Source/Core/DolphinQt2/Debugger/RegisterColumn.cpp create mode 100644 Source/Core/DolphinQt2/Debugger/RegisterColumn.h create mode 100644 Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp create mode 100644 Source/Core/DolphinQt2/Debugger/RegisterWidget.h diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index ea8b9be430..793bda50cd 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -70,6 +70,8 @@ set(SRCS Config/Mapping/WiimoteEmuMotionControl.cpp Config/PropertiesDialog.cpp Config/SettingsWindow.cpp + Debugger/RegisterColumn.cpp + Debugger/RegisterWidget.cpp GameList/GameFileCache.cpp GameList/GameFile.cpp GameList/GameList.cpp diff --git a/Source/Core/DolphinQt2/Debugger/RegisterColumn.cpp b/Source/Core/DolphinQt2/Debugger/RegisterColumn.cpp new file mode 100644 index 0000000000..e93f6aee9e --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/RegisterColumn.cpp @@ -0,0 +1,123 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Debugger/RegisterColumn.h" + +#include +#include + +#include + +RegisterColumn::RegisterColumn(RegisterType type, std::function get, + std::function set) + : m_type(type), m_get_register(std::move(get)), m_set_register(std::move(set)) +{ + RefreshValue(); + Update(); + + setFlags(set == nullptr ? flags() ^ Qt::ItemIsEditable : flags()); + setData(DATA_TYPE, static_cast(type)); +} + +RegisterDisplay RegisterColumn::GetDisplay() const +{ + return m_display; +} + +void RegisterColumn::SetDisplay(RegisterDisplay display) +{ + m_display = display; + Update(); +} + +void RegisterColumn::RefreshValue() +{ + QBrush brush = QPalette().brush(QPalette::Text); + + if (m_value != m_get_register()) + { + m_value = m_get_register(); + brush.setColor(Qt::red); + } + + setForeground(brush); + + Update(); +} + +u64 RegisterColumn::GetValue() const +{ + return m_value; +} + +void RegisterColumn::SetValue() +{ + u64 value = 0; + + bool valid = false; + + switch (m_display) + { + case RegisterDisplay::Hex: + value = text().toULongLong(&valid, 16); + break; + case RegisterDisplay::SInt32: + value = text().toInt(&valid); + break; + case RegisterDisplay::UInt32: + value = text().toUInt(&valid); + break; + case RegisterDisplay::Float: + { + float f = text().toFloat(&valid); + std::memcpy(&value, &f, sizeof(u32)); + break; + } + } + + if (!valid) + { + QMessageBox::critical(nullptr, QObject::tr("Invalid input"), + QObject::tr("Bad input for field")); + } + else + { + m_set_register(value); + } + + RefreshValue(); +} + +void RegisterColumn::Update() +{ + QString text; + + switch (m_display) + { + case RegisterDisplay::Hex: + text = QStringLiteral("%1").arg(m_value, + (m_type == RegisterType::ibat || m_type == RegisterType::dbat || + m_type == RegisterType::fpr ? + sizeof(u64) : + sizeof(u32)) * + 2, + 16, QLatin1Char('0')); + break; + case RegisterDisplay::SInt32: + text = QString::number(static_cast(m_value)); + break; + case RegisterDisplay::UInt32: + text = QString::number(static_cast(m_value)); + break; + case RegisterDisplay::Float: + { + float tmp; + std::memcpy(&tmp, &m_value, sizeof(float)); + text = QString::number(tmp); + break; + } + } + + setText(text); +} diff --git a/Source/Core/DolphinQt2/Debugger/RegisterColumn.h b/Source/Core/DolphinQt2/Debugger/RegisterColumn.h new file mode 100644 index 0000000000..ccfaf2b7ca --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/RegisterColumn.h @@ -0,0 +1,68 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include + +#include "Common/CommonTypes.h" + +enum class RegisterType +{ + gpr, // General purpose registers, int (r0-r31) + fpr, // General purpose registers, float (f0-f31) + ibat, // Instruction BATs (IBAT0-IBAT7) + dbat, // Data BATs (DBAT0-DBAT7) + pc, // Program counter + lr, // Link register + ctr, // Decremented and incremented by branch and count instructions + cr, // Condition register + fpscr, // Floating point status and control register + msr, // Machine state register + srr, // Machine status save/restore register (SRR0 - SRR1) + sr, // Segment register (SR0 - SR15) + exceptions, // Keeps track of currently triggered exceptions + int_mask, // ??? + int_cause, // ??? + dsisr, // Defines the cause of data / alignment exceptions + dar, // Data adress register + pt_hashmask // ??? +}; + +enum class RegisterDisplay +{ + Hex, + SInt32, + UInt32, + Float +}; + +constexpr int DATA_TYPE = Qt::UserRole; + +class RegisterColumn : public QTableWidgetItem +{ +public: + explicit RegisterColumn(RegisterType type, std::function get, + std::function set); + + void RefreshValue(); + + RegisterDisplay GetDisplay() const; + void SetDisplay(RegisterDisplay display); + u64 GetValue() const; + void SetValue(); + +private: + void Update(); + + RegisterType m_type; + + std::function m_get_register; + std::function m_set_register; + + u64 m_value; + RegisterDisplay m_display = RegisterDisplay::Hex; +}; diff --git a/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp b/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp new file mode 100644 index 0000000000..e9817b84ff --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp @@ -0,0 +1,337 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Debugger/RegisterWidget.h" + +#include "Core/Core.h" +#include "Core/HW/ProcessorInterface.h" +#include "Core/PowerPC/PowerPC.h" +#include "DolphinQt2/QtUtils/ActionHelper.h" +#include "DolphinQt2/Settings.h" + +#include +#include +#include +#include +#include + +RegisterWidget::RegisterWidget(QWidget* parent) : QDockWidget(parent) +{ + setWindowTitle(tr("Registers")); + setAllowedAreas(Qt::AllDockWidgetAreas); + + QSettings settings; + + restoreGeometry(settings.value(QStringLiteral("registerwidget/geometry")).toByteArray()); + setFloating(settings.value(QStringLiteral("registerwidget/floating")).toBool()); + + CreateWidgets(); + PopulateTable(); + ConnectWidgets(); + + connect(&Settings::Instance(), &Settings::EmulationStateChanged, [this](Core::State state) { + if (Settings::Instance().IsDebugModeEnabled() && Core::GetState() == Core::State::Paused) + emit RequestTableUpdate(); + }); + + connect(this, &RegisterWidget::RequestTableUpdate, [this] { + m_updating = true; + emit UpdateTable(); + m_updating = false; + }); + + connect(&Settings::Instance(), &Settings::RegistersVisibilityChanged, + [this](bool visible) { setHidden(!visible); }); + + connect(&Settings::Instance(), &Settings::DebugModeToggled, [this](bool enabled) { + setHidden(!enabled || !Settings::Instance().IsRegistersVisible()); + }); + + setHidden(!Settings::Instance().IsRegistersVisible() || + !Settings::Instance().IsDebugModeEnabled()); +} + +RegisterWidget::~RegisterWidget() +{ + QSettings settings; + + settings.setValue(QStringLiteral("registerwidget/geometry"), saveGeometry()); + settings.setValue(QStringLiteral("registerwidget/floating"), isFloating()); +} + +void RegisterWidget::closeEvent(QCloseEvent*) +{ + Settings::Instance().SetRegistersVisible(false); +} + +void RegisterWidget::CreateWidgets() +{ + m_table = new QTableWidget; + + m_table->setColumnCount(9); + + m_table->verticalHeader()->setVisible(false); + m_table->setContextMenuPolicy(Qt::CustomContextMenu); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + + QStringList empty_list; + + for (auto i = 0; i < 9; i++) + empty_list << QStringLiteral(""); + + m_table->setHorizontalHeaderLabels(empty_list); + + QWidget* widget = new QWidget; + auto* layout = new QVBoxLayout; + layout->addWidget(m_table); + widget->setLayout(layout); + + setWidget(widget); +} + +void RegisterWidget::ConnectWidgets() +{ + connect(m_table, &QTableWidget::customContextMenuRequested, this, + &RegisterWidget::ShowContextMenu); + connect(m_table, &QTableWidget::itemChanged, this, &RegisterWidget::OnItemChanged); +} + +void RegisterWidget::OnItemChanged(QTableWidgetItem* item) +{ + if (!item->data(DATA_TYPE).isNull() && !m_updating) + static_cast(item)->SetValue(); +} + +void RegisterWidget::ShowContextMenu() +{ + QMenu* menu = new QMenu(this); + + if (m_table->selectedItems().size()) + { + auto variant = m_table->selectedItems()[0]->data(DATA_TYPE); + + if (!variant.isNull()) + { + auto* item = reinterpret_cast(m_table->selectedItems()[0]); + auto type = static_cast(item->data(DATA_TYPE).toInt()); + auto display = item->GetDisplay(); + + menu->addAction(tr("Add to &watch")); + menu->addAction(tr("View &memory")); + menu->addAction(tr("View &code")); + + menu->addSeparator(); + + QActionGroup* group = new QActionGroup(menu); + group->setExclusive(true); + + auto* view_hex = menu->addAction(tr("Hexadecimal")); + auto* view_int = menu->addAction(tr("Signed Integer")); + auto* view_uint = menu->addAction(tr("Unsigned Integer")); + auto* view_float = menu->addAction(tr("Float")); + + for (auto* action : {view_hex, view_int, view_uint, view_float}) + { + action->setCheckable(true); + action->setVisible(false); + action->setActionGroup(group); + } + + switch (display) + { + case RegisterDisplay::Hex: + view_hex->setChecked(true); + break; + case RegisterDisplay::SInt32: + view_int->setChecked(true); + break; + case RegisterDisplay::UInt32: + view_uint->setChecked(true); + break; + case RegisterDisplay::Float: + view_float->setChecked(true); + break; + } + + switch (type) + { + case RegisterType::gpr: + view_hex->setVisible(true); + view_int->setVisible(true); + view_uint->setVisible(true); + view_float->setVisible(true); + break; + case RegisterType::fpr: + view_hex->setVisible(true); + view_float->setVisible(true); + break; + default: + break; + } + + connect(view_hex, &QAction::triggered, [this, item] { + m_updating = true; + item->SetDisplay(RegisterDisplay::Hex); + m_updating = false; + }); + + connect(view_int, &QAction::triggered, [this, item] { + m_updating = true; + item->SetDisplay(RegisterDisplay::SInt32); + m_updating = false; + }); + + connect(view_uint, &QAction::triggered, [this, item] { + m_updating = true; + item->SetDisplay(RegisterDisplay::UInt32); + m_updating = false; + }); + + connect(view_float, &QAction::triggered, [this, item] { + m_updating = true; + item->SetDisplay(RegisterDisplay::Float); + m_updating = false; + }); + + menu->addSeparator(); + } + } + + AddAction(menu, tr("Update"), this, [this] { emit RequestTableUpdate(); }); + + menu->exec(QCursor::pos()); +} + +void RegisterWidget::PopulateTable() +{ + for (int i = 0; i < 32; i++) + { + // General purpose registers (int) + AddRegister(i, 0, RegisterType::gpr, "r" + std::to_string(i), [i] { return GPR(i); }, + [i](u64 value) { GPR(i) = value; }); + + // General purpose registers (float) + AddRegister(i, 2, RegisterType::fpr, "f" + std::to_string(i), [i] { return riPS0(i); }, + [i](u64 value) { riPS0(i) = value; }); + + AddRegister(i, 4, RegisterType::fpr, "", [i] { return riPS1(i); }, + [i](u64 value) { riPS1(i) = value; }); + } + + for (int i = 0; i < 8; i++) + { + // IBAT registers + AddRegister(i, 5, RegisterType::ibat, "IBAT" + std::to_string(i), + [i] { + return (static_cast(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2]) << 32) + + PowerPC::ppcState.spr[SPR_IBAT0L + i * 2]; + }, + nullptr); + // DBAT registers + AddRegister(i + 8, 5, RegisterType::dbat, "DBAT" + std::to_string(i), + [i] { + return (static_cast(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2]) << 32) + + PowerPC::ppcState.spr[SPR_DBAT0L + i * 2]; + }, + nullptr); + } + + for (int i = 0; i < 16; i++) + { + // SR registers + AddRegister(i, 7, RegisterType::sr, "SR" + std::to_string(i), + [i] { return PowerPC::ppcState.sr[i]; }, + [i](u64 value) { PowerPC::ppcState.sr[i] = value; }); + } + + // Special registers + // PC + AddRegister(16, 5, RegisterType::pc, "PC", [] { return PowerPC::ppcState.pc; }, + [](u64 value) { PowerPC::ppcState.pc = value; }); + + // LR + AddRegister(17, 5, RegisterType::fpscr, "LR", [] { return PowerPC::ppcState.spr[SPR_LR]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_LR] = value; }); + + // CTR + AddRegister(18, 5, RegisterType::fpscr, "FPSCR", [] { return PowerPC::ppcState.spr[SPR_CTR]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_CTR] = value; }); + + // CR + AddRegister(19, 5, RegisterType::cr, "CR", [] { return GetCR(); }, + [](u64 value) { SetCR(value); }); + + // FPSCR + AddRegister(20, 5, RegisterType::fpscr, "FPSCR", [] { return PowerPC::ppcState.fpscr; }, + [](u64 value) { PowerPC::ppcState.fpscr = value; }); + + // MSR + AddRegister(21, 5, RegisterType::msr, "MSR", [] { return PowerPC::ppcState.msr; }, + [](u64 value) { PowerPC::ppcState.msr = value; }); + + // SRR 0-1 + AddRegister(22, 5, RegisterType::srr, "SRR0", [] { return PowerPC::ppcState.spr[SPR_SRR0]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_SRR0] = value; }); + AddRegister(23, 5, RegisterType::srr, "SRR1", [] { return PowerPC::ppcState.spr[SPR_SRR1]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_SRR1] = value; }); + + // Exceptions + AddRegister(24, 5, RegisterType::exceptions, "Exceptions", + [] { return PowerPC::ppcState.Exceptions; }, + [](u64 value) { PowerPC::ppcState.Exceptions = value; }); + + // Int Mask + AddRegister(25, 5, RegisterType::int_mask, "Int Mask", + [] { return ProcessorInterface::GetMask(); }, nullptr); + + // Int Cause + AddRegister(26, 5, RegisterType::int_cause, "Int Cause", + [] { return ProcessorInterface::GetCause(); }, nullptr); + + // DSISR + AddRegister(27, 5, RegisterType::dsisr, "DSISR", [] { return PowerPC::ppcState.spr[SPR_DSISR]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_DSISR] = value; }); + // DAR + AddRegister(28, 5, RegisterType::dar, "DAR", [] { return PowerPC::ppcState.spr[SPR_DAR]; }, + [](u64 value) { PowerPC::ppcState.spr[SPR_DAR] = value; }); + + // Hash Mask + AddRegister( + 29, 5, RegisterType::pt_hashmask, "Hash Mask", + [] { return (PowerPC::ppcState.pagetable_hashmask << 6) | PowerPC::ppcState.pagetable_base; }, + nullptr); + + emit RequestTableUpdate(); + m_table->resizeColumnsToContents(); +} + +void RegisterWidget::AddRegister(int row, int column, RegisterType type, std::string register_name, + std::function get_reg, std::function set_reg) +{ + auto* value = new RegisterColumn(type, get_reg, set_reg); + + if (m_table->rowCount() <= row) + m_table->setRowCount(row + 1); + + bool has_label = !register_name.empty(); + + if (has_label) + { + auto* label = new QTableWidgetItem(QString::fromStdString(register_name)); + label->setFlags(Qt::ItemIsEnabled); + + QFont label_font = label->font(); + label_font.setBold(true); + label->setFont(label_font); + + m_table->setItem(row, column, label); + m_table->setItem(row, column + 1, value); + } + else + { + m_table->setItem(row, column, value); + } + + connect(this, &RegisterWidget::UpdateTable, [value] { value->RefreshValue(); }); +} diff --git a/Source/Core/DolphinQt2/Debugger/RegisterWidget.h b/Source/Core/DolphinQt2/Debugger/RegisterWidget.h new file mode 100644 index 0000000000..3793e12e6e --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/RegisterWidget.h @@ -0,0 +1,46 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include + +#include "Common/CommonTypes.h" +#include "DolphinQt2/Debugger/RegisterColumn.h" + +class QTableWidget; +class QCloseEvent; + +class RegisterWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit RegisterWidget(QWidget* parent = nullptr); + ~RegisterWidget(); + +signals: + void RequestTableUpdate(); + void UpdateTable(); + void UpdateValue(QTableWidgetItem* item); + void UpdateValueType(QTableWidgetItem* item); + +protected: + void closeEvent(QCloseEvent*) override; + +private: + void CreateWidgets(); + void ConnectWidgets(); + void PopulateTable(); + + void ShowContextMenu(); + void OnItemChanged(QTableWidgetItem* item); + + void AddRegister(int row, int column, RegisterType type, std::string register_name, + std::function get_reg, std::function set_reg); + + QTableWidget* m_table; + bool m_updating = false; +}; diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index 82d1a653bb..966234241a 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -47,7 +47,7 @@ avrt.lib;iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies) - $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;%(AdditionalIncludeDirectories) + $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Debugger;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;%(AdditionalIncludeDirectories) DolphinQt2.manifest;%(AdditionalManifestFiles) @@ -84,6 +84,7 @@ + @@ -156,6 +157,7 @@ + @@ -201,6 +203,8 @@ + + @@ -255,6 +259,8 @@ + + diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 99e762cdb8..99278149ca 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -46,6 +46,7 @@ #include "DolphinQt2/Config/LogWidget.h" #include "DolphinQt2/Config/Mapping/MappingWindow.h" #include "DolphinQt2/Config/SettingsWindow.h" +#include "DolphinQt2/Debugger/RegisterWidget.h" #include "DolphinQt2/FIFOPlayerWindow.h" #include "DolphinQt2/Host.h" #include "DolphinQt2/HotkeyScheduler.h" @@ -97,6 +98,8 @@ MainWindow::~MainWindow() { m_render_widget->deleteLater(); ShutdownControllers(); + + Config::Save(); } void MainWindow::InitControllers() @@ -166,6 +169,7 @@ void MainWindow::CreateComponents() connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this, [this](const QString& path) { StartGame(path); }); + m_register_widget = new RegisterWidget(this); #if defined(HAVE_XRANDR) && HAVE_XRANDR m_graphics_window = new GraphicsWindow( @@ -314,8 +318,10 @@ void MainWindow::ConnectStack() setTabPosition(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea, QTabWidget::North); addDockWidget(Qt::RightDockWidgetArea, m_log_widget); addDockWidget(Qt::RightDockWidgetArea, m_log_config_widget); + addDockWidget(Qt::RightDockWidgetArea, m_register_widget); tabifyDockWidget(m_log_widget, m_log_config_widget); + tabifyDockWidget(m_log_widget, m_register_widget); } void MainWindow::Open() diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index a230a2322f..1eb3c8c008 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -31,6 +31,7 @@ class SettingsWindow; class ControllersWindow; class DragEnterEvent; class GraphicsWindow; +class RegisterWidget; class MainWindow final : public QMainWindow { @@ -138,7 +139,9 @@ private: NetPlayDialog* m_netplay_dialog; NetPlaySetupDialog* m_netplay_setup_dialog; GraphicsWindow* m_graphics_window; + LogWidget* m_log_widget; LogConfigWidget* m_log_config_widget; FIFOPlayerWindow* m_fifo_window; + RegisterWidget* m_register_widget; }; diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index aaea20cbc7..d0143eb9f9 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -50,6 +50,7 @@ MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent) connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [=](Core::State state) { OnEmulationStateChanged(state); }); OnEmulationStateChanged(Core::GetState()); + connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &MenuBar::OnDebugModeToggled); connect(this, &MenuBar::SelectionChanged, this, &MenuBar::OnSelectionChanged); connect(this, &MenuBar::RecordingStatusChanged, this, &MenuBar::OnRecordingStatusChanged); @@ -83,6 +84,13 @@ void MenuBar::OnEmulationStateChanged(Core::State state) UpdateStateSlotMenu(); UpdateToolsMenu(running); + + OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled()); +} + +void MenuBar::OnDebugModeToggled(bool enabled) +{ + m_show_registers->setVisible(enabled); } void MenuBar::AddFileMenu() @@ -255,6 +263,17 @@ void MenuBar::AddViewMenu() view_menu->addSeparator(); + m_show_registers = view_menu->addAction(tr("&Registers")); + m_show_registers->setCheckable(true); + m_show_registers->setChecked(Settings::Instance().IsRegistersVisible()); + + connect(m_show_registers, &QAction::toggled, &Settings::Instance(), + &Settings::SetRegistersVisible); + connect(&Settings::Instance(), &Settings::RegistersVisibilityChanged, m_show_registers, + &QAction::setChecked); + + view_menu->addSeparator(); + AddGameListTypeSection(view_menu); view_menu->addSeparator(); AddListColumnsMenu(view_menu); diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index 3bbb51345a..c8af98d00b 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -119,6 +119,7 @@ private: void OnSelectionChanged(QSharedPointer game_file); void OnRecordingStatusChanged(bool recording); void OnReadOnlyModeChanged(bool read_only); + void OnDebugModeToggled(bool enabled); // File QAction* m_open_action; @@ -157,4 +158,7 @@ private: QAction* m_recording_start; QAction* m_recording_stop; QAction* m_recording_read_only; + + // View + QAction* m_show_registers; }; diff --git a/Source/Core/DolphinQt2/Settings.cpp b/Source/Core/DolphinQt2/Settings.cpp index 30dbde00d8..2fb765c1aa 100644 --- a/Source/Core/DolphinQt2/Settings.cpp +++ b/Source/Core/DolphinQt2/Settings.cpp @@ -210,3 +210,18 @@ bool Settings::IsDebugModeEnabled() const { return SConfig::GetInstance().bEnableDebugging; } + +void Settings::SetRegistersVisible(bool enabled) +{ + if (IsRegistersVisible() != enabled) + { + QSettings().setValue(QStringLiteral("debugger/showregisters"), enabled); + + emit RegistersVisibilityChanged(enabled); + } +} + +bool Settings::IsRegistersVisible() const +{ + return QSettings().value(QStringLiteral("debugger/showregisters")).toBool(); +} diff --git a/Source/Core/DolphinQt2/Settings.h b/Source/Core/DolphinQt2/Settings.h index 4ab31ad2cc..ebee8cb788 100644 --- a/Source/Core/DolphinQt2/Settings.h +++ b/Source/Core/DolphinQt2/Settings.h @@ -81,6 +81,8 @@ public: // Debug void SetDebugModeEnabled(bool enabled); bool IsDebugModeEnabled() const; + void SetRegistersVisible(bool enabled); + bool IsRegistersVisible() const; // Other GameListModel* GetGameListModel() const; @@ -93,13 +95,13 @@ signals: void HideCursorChanged(); void VolumeChanged(int volume); void NANDRefresh(); + void RegistersVisibilityChanged(bool visible); void LogVisibilityChanged(bool visible); void LogConfigVisibilityChanged(bool visible); void EnableCheatsChanged(bool enabled); void DebugModeToggled(bool enabled); private: - bool m_registers_visible = false; std::unique_ptr m_client; std::unique_ptr m_server; Settings();