diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index 3200b3f7ae..3ed0ffb1c8 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/BreakpointWidget.cpp + Debugger/NewBreakpointDialog.cpp Debugger/RegisterColumn.cpp Debugger/RegisterWidget.cpp Debugger/WatchWidget.cpp diff --git a/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp new file mode 100644 index 0000000000..80ac714607 --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp @@ -0,0 +1,284 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Debugger/BreakpointWidget.h" + +#include +#include +#include +#include +#include + +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/PowerPC/BreakPoints.h" +#include "Core/PowerPC/PPCSymbolDB.h" +#include "Core/PowerPC/PowerPC.h" +#include "DolphinQt2/Debugger/NewBreakpointDialog.h" +#include "DolphinQt2/QtUtils/ActionHelper.h" +#include "DolphinQt2/Settings.h" + +BreakpointWidget::BreakpointWidget(QWidget* parent) : QDockWidget(parent) +{ + setWindowTitle(tr("Breakpoints")); + setAllowedAreas(Qt::AllDockWidgetAreas); + + QSettings settings; + + restoreGeometry(settings.value(QStringLiteral("breakpointwidget/geometry")).toByteArray()); + setFloating(settings.value(QStringLiteral("breakpointwidget/floating")).toBool()); + + CreateWidgets(); + + connect(&Settings::Instance(), &Settings::EmulationStateChanged, [this](Core::State state) { + if (!Settings::Instance().IsDebugModeEnabled()) + return; + + m_load->setEnabled(Core::IsRunning()); + m_save->setEnabled(Core::IsRunning()); + }); + + connect(&Settings::Instance(), &Settings::BreakpointsVisibilityChanged, + [this](bool visible) { setHidden(!visible); }); + + connect(&Settings::Instance(), &Settings::DebugModeToggled, [this](bool enabled) { + setHidden(!enabled || !Settings::Instance().IsBreakpointsVisible()); + }); + + setHidden(!Settings::Instance().IsBreakpointsVisible() || + !Settings::Instance().IsDebugModeEnabled()); + + Update(); +} + +BreakpointWidget::~BreakpointWidget() +{ + QSettings settings; + + settings.setValue(QStringLiteral("breakpointwidget/geometry"), saveGeometry()); + settings.setValue(QStringLiteral("breakpointwidget/floating"), isFloating()); +} + +void BreakpointWidget::CreateWidgets() +{ + m_toolbar = new QToolBar; + m_table = new QTableWidget; + m_table->setColumnCount(5); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->verticalHeader()->hide(); + + auto* layout = new QVBoxLayout; + + layout->addWidget(m_toolbar); + layout->addWidget(m_table); + + AddAction(m_toolbar, tr("New"), this, &BreakpointWidget::OnNewBreakpoint); + AddAction(m_toolbar, tr("Delete"), this, &BreakpointWidget::OnDelete); + AddAction(m_toolbar, tr("Clear"), this, &BreakpointWidget::OnClear); + + m_load = AddAction(m_toolbar, tr("Load"), this, &BreakpointWidget::OnLoad); + m_save = AddAction(m_toolbar, tr("Save"), this, &BreakpointWidget::OnSave); + + m_load->setEnabled(false); + m_save->setEnabled(false); + + QWidget* widget = new QWidget; + widget->setLayout(layout); + + setWidget(widget); +} + +void BreakpointWidget::closeEvent(QCloseEvent*) +{ + Settings::Instance().SetBreakpointsVisible(false); +} + +void BreakpointWidget::Update() +{ + m_table->clear(); + + m_table->setHorizontalHeaderLabels( + {tr("Active"), tr("Type"), tr("Function"), tr("Address"), tr("Flags")}); + + int i = 0; + + auto create_item = [this](const QString string = QStringLiteral("")) { + QTableWidgetItem* item = new QTableWidgetItem(string); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + return item; + }; + + // Breakpoints + for (const auto& bp : PowerPC::breakpoints.GetBreakPoints()) + { + m_table->setRowCount(i + 1); + + auto* active = create_item(bp.is_enabled ? tr("on") : QString()); + + active->setData(Qt::UserRole, bp.address); + + m_table->setItem(i, 0, active); + m_table->setItem(i, 1, create_item(QStringLiteral("BP"))); + + if (g_symbolDB.GetSymbolFromAddr(bp.address)) + { + m_table->setItem(i, 2, + create_item(QString::fromStdString(g_symbolDB.GetDescription(bp.address)))); + } + + m_table->setItem(i, 3, + create_item(QStringLiteral("%1").arg(bp.address, 8, 16, QLatin1Char('0')))); + + m_table->setItem(i, 4, create_item()); + + i++; + } + + // Memory Breakpoints + for (const auto& mbp : PowerPC::memchecks.GetMemChecks()) + { + m_table->setRowCount(i + 1); + m_table->setItem(i, 0, create_item(mbp.break_on_hit || mbp.log_on_hit ? tr("on") : QString())); + m_table->setItem(i, 1, create_item(QStringLiteral("MBP"))); + + if (g_symbolDB.GetSymbolFromAddr(mbp.start_address)) + { + m_table->setItem( + i, 2, create_item(QString::fromStdString(g_symbolDB.GetDescription(mbp.start_address)))); + } + + if (mbp.is_ranged) + { + m_table->setItem(i, 3, create_item(QStringLiteral("%1 - %2") + .arg(mbp.start_address, 8, 16, QLatin1Char('0')) + .arg(mbp.end_address, 8, 16, QLatin1Char('0')))); + } + else + { + m_table->setItem( + i, 3, create_item(QStringLiteral("%1").arg(mbp.start_address, 8, 16, QLatin1Char('0')))); + } + + QString flags; + + if (mbp.is_break_on_read) + flags.append(QStringLiteral("r")); + + if (mbp.is_break_on_write) + flags.append(QStringLiteral("w")); + + m_table->setItem(i, 4, create_item(flags)); + + i++; + } +} + +void BreakpointWidget::OnDelete() +{ + if (m_table->selectedItems().size() == 0) + return; + + auto address = m_table->selectedItems()[0]->data(Qt::UserRole).toUInt(); + + PowerPC::breakpoints.Remove(address); + PowerPC::memchecks.Remove(address); + + Update(); +} + +void BreakpointWidget::OnClear() +{ + PowerPC::debug_interface.ClearAllBreakpoints(); + PowerPC::debug_interface.ClearAllMemChecks(); + + m_table->setRowCount(0); + Update(); +} + +void BreakpointWidget::OnNewBreakpoint() +{ + NewBreakpointDialog* dialog = new NewBreakpointDialog(this); + dialog->exec(); +} + +void BreakpointWidget::OnLoad() +{ + IniFile ini; + BreakPoints::TBreakPointsStr newbps; + MemChecks::TMemChecksStr newmcs; + + if (!ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + SConfig::GetInstance().GetGameID() + ".ini", + false)) + { + return; + } + + if (ini.GetLines("BreakPoints", &newbps, false)) + { + PowerPC::breakpoints.Clear(); + PowerPC::breakpoints.AddFromStrings(newbps); + } + + if (ini.GetLines("MemoryBreakPoints", &newmcs, false)) + { + PowerPC::memchecks.Clear(); + PowerPC::memchecks.AddFromStrings(newmcs); + } + + Update(); +} + +void BreakpointWidget::OnSave() +{ + IniFile ini; + ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + SConfig::GetInstance().GetGameID() + ".ini", + false); + ini.SetLines("BreakPoints", PowerPC::breakpoints.GetStrings()); + ini.SetLines("MemoryBreakPoints", PowerPC::memchecks.GetStrings()); + ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + SConfig::GetInstance().GetGameID() + ".ini"); +} + +void BreakpointWidget::AddBP(u32 addr) +{ + PowerPC::breakpoints.Add(addr); + + Update(); +} + +void BreakpointWidget::AddAddressMBP(u32 addr, bool on_read, bool on_write, bool do_log, + bool do_break) +{ + TMemCheck check; + + check.start_address = addr; + check.is_break_on_read = on_read; + check.is_break_on_write = on_write; + check.log_on_hit = do_log; + check.break_on_hit = do_break; + + PowerPC::memchecks.Add(check); + + Update(); +} + +void BreakpointWidget::AddRangedMBP(u32 from, u32 to, bool on_read, bool on_write, bool do_log, + bool do_break) +{ + TMemCheck check; + + check.start_address = from; + check.end_address = to; + check.is_ranged = true; + check.is_break_on_read = on_read; + check.is_break_on_write = on_write; + check.log_on_hit = do_log; + check.break_on_hit = do_break; + + PowerPC::memchecks.Add(check); + + Update(); +} diff --git a/Source/Core/DolphinQt2/Debugger/BreakpointWidget.h b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.h new file mode 100644 index 0000000000..0a2761f0a5 --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.h @@ -0,0 +1,47 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +class QAction; +class QTableWidget; +class QToolBar; +class QCloseEvent; + +class BreakpointWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit BreakpointWidget(QWidget* parent = nullptr); + ~BreakpointWidget(); + + void AddBP(u32 addr); + void AddAddressMBP(u32 addr, bool on_read = true, bool on_write = true, bool do_log = true, + bool do_break = true); + void AddRangedMBP(u32 from, u32 to, bool do_read = true, bool do_write = true, bool do_log = true, + bool do_break = true); + +protected: + void closeEvent(QCloseEvent*) override; + +private: + void CreateWidgets(); + + void OnDelete(); + void OnClear(); + void OnNewBreakpoint(); + void OnLoad(); + void OnSave(); + + void Update(); + + QToolBar* m_toolbar; + QTableWidget* m_table; + QAction* m_load; + QAction* m_save; +}; diff --git a/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.cpp b/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.cpp new file mode 100644 index 0000000000..723ea5d6c9 --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.cpp @@ -0,0 +1,182 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Debugger/NewBreakpointDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DolphinQt2/Debugger/BreakpointWidget.h" + +NewBreakpointDialog::NewBreakpointDialog(BreakpointWidget* parent) + : QDialog(parent), m_parent(parent) +{ + setWindowTitle(tr("New Breakpoint")); + CreateWidgets(); + ConnectWidgets(); + + OnBPTypeChanged(); + OnAddressTypeChanged(); +} + +void NewBreakpointDialog::CreateWidgets() +{ + m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + // Instruction BP + m_instruction_bp = new QRadioButton(tr("Instruction Breakpoint")); + m_instruction_bp->setChecked(true); + m_instruction_box = new QGroupBox; + m_instruction_address = new QLineEdit; + + auto* instruction_layout = new QHBoxLayout; + m_instruction_box->setLayout(instruction_layout); + instruction_layout->addWidget(new QLabel(tr("Address:"))); + instruction_layout->addWidget(m_instruction_address); + + // Memory BP + m_memory_bp = new QRadioButton(tr("Memory Breakpoint")); + m_memory_box = new QGroupBox; + m_memory_use_address = new QRadioButton(tr("Address")); + m_memory_use_address->setChecked(true); + m_memory_use_range = new QRadioButton(tr("Range")); + m_memory_address_from = new QLineEdit; + m_memory_address_to = new QLineEdit; + m_memory_address_from_label = new QLabel; // Set by OnAddressTypeChanged + m_memory_address_to_label = new QLabel(tr("To:")); + m_memory_on_read = new QRadioButton(tr("Read")); + m_memory_on_write = new QRadioButton(tr("Write")); + m_memory_on_read_and_write = new QRadioButton(tr("Read or Write")); + m_memory_on_write->setChecked(true); + m_memory_do_log = new QRadioButton(tr("Log")); + m_memory_do_break = new QRadioButton(tr("Break")); + m_memory_do_log_and_break = new QRadioButton(tr("Log and Break")); + m_memory_do_log_and_break->setChecked(true); + + auto* memory_layout = new QGridLayout; + m_memory_box->setLayout(memory_layout); + memory_layout->addWidget(m_memory_use_address, 0, 0); + memory_layout->addWidget(m_memory_use_range, 0, 3); + memory_layout->addWidget(m_memory_address_from_label, 1, 0); + memory_layout->addWidget(m_memory_address_from, 1, 1); + memory_layout->addWidget(m_memory_address_to_label, 1, 2); + memory_layout->addWidget(m_memory_address_to, 1, 3); + memory_layout->addWidget(new QLabel(tr("On...")), 2, 0); + memory_layout->addWidget(m_memory_on_read, 2, 1); + memory_layout->addWidget(m_memory_on_write, 2, 2); + memory_layout->addWidget(m_memory_on_read_and_write, 2, 3); + memory_layout->addWidget(new QLabel(tr("Do...")), 3, 0); + memory_layout->addWidget(m_memory_do_log, 3, 1); + memory_layout->addWidget(m_memory_do_break, 3, 2); + memory_layout->addWidget(m_memory_do_log_and_break, 3, 3); + + auto* layout = new QVBoxLayout; + + layout->addWidget(m_instruction_bp); + layout->addWidget(m_instruction_box); + layout->addWidget(m_memory_bp); + layout->addWidget(m_memory_box); + layout->addWidget(m_buttons); + + setLayout(layout); +} + +void NewBreakpointDialog::ConnectWidgets() +{ + connect(m_buttons, &QDialogButtonBox::accepted, this, &NewBreakpointDialog::accept); + connect(m_buttons, &QDialogButtonBox::rejected, this, &NewBreakpointDialog::reject); + + connect(m_instruction_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged); + connect(m_memory_bp, &QRadioButton::toggled, this, &NewBreakpointDialog::OnBPTypeChanged); + + connect(m_memory_use_address, &QRadioButton::toggled, this, + &NewBreakpointDialog::OnAddressTypeChanged); + connect(m_memory_use_range, &QRadioButton::toggled, this, + &NewBreakpointDialog::OnAddressTypeChanged); +} + +void NewBreakpointDialog::OnBPTypeChanged() +{ + m_instruction_box->setEnabled(m_instruction_bp->isChecked()); + m_memory_box->setEnabled(m_memory_bp->isChecked()); +} + +void NewBreakpointDialog::OnAddressTypeChanged() +{ + bool ranged = m_memory_use_range->isChecked(); + + m_memory_address_to->setHidden(!ranged); + m_memory_address_to_label->setHidden(!ranged); + + m_memory_address_from_label->setText(ranged ? tr("From:") : tr("Address:")); +} + +void NewBreakpointDialog::accept() +{ + auto invalid_input = [this](QString field) { + QMessageBox::critical(this, tr("Error"), tr("Bad input provided for %1 field").arg(field)); + }; + + bool instruction = m_instruction_bp->isChecked(); + bool ranged = m_memory_use_range->isChecked(); + + // Triggers + bool on_read = m_memory_on_read->isChecked() || m_memory_on_read_and_write->isChecked(); + bool on_write = m_memory_on_write->isChecked() || m_memory_on_read_and_write->isChecked(); + + // Actions + bool do_log = m_memory_do_log->isChecked() || m_memory_do_log_and_break->isChecked(); + bool do_break = m_memory_do_break->isChecked() || m_memory_do_log_and_break->isChecked(); + + bool good; + + if (instruction) + { + u32 address = m_instruction_address->text().toUInt(&good, 16); + + if (!good) + { + invalid_input(tr("address")); + return; + } + + m_parent->AddBP(address); + } + else + { + u32 from = m_memory_address_from->text().toUInt(&good, 16); + + if (!good) + { + invalid_input(ranged ? tr("from") : tr("address")); + return; + } + + if (ranged) + { + u32 to = m_memory_address_to->text().toUInt(&good, 16); + if (!good) + { + invalid_input(tr("to")); + return; + } + + m_parent->AddRangedMBP(from, to, on_read, on_write, do_log, do_break); + } + else + { + m_parent->AddAddressMBP(from, on_read, on_write, do_log, do_break); + } + } + + QDialog::accept(); +} diff --git a/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.h b/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.h new file mode 100644 index 0000000000..7dd1d34837 --- /dev/null +++ b/Source/Core/DolphinQt2/Debugger/NewBreakpointDialog.h @@ -0,0 +1,57 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +class BreakpointWidget; +class QCheckBox; +class QDialogButtonBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QRadioButton; + +class NewBreakpointDialog : public QDialog +{ + Q_OBJECT +public: + explicit NewBreakpointDialog(BreakpointWidget* parent); + + void accept(); + +private: + void CreateWidgets(); + void ConnectWidgets(); + + void OnBPTypeChanged(); + void OnAddressTypeChanged(); + + // Instruction BPs + QRadioButton* m_instruction_bp; + QGroupBox* m_instruction_box; + QLineEdit* m_instruction_address; + + // Memory BPs + QRadioButton* m_memory_bp; + QRadioButton* m_memory_use_address; + QRadioButton* m_memory_use_range; + QGroupBox* m_memory_box; + QLabel* m_memory_address_from_label; + QLineEdit* m_memory_address_from; + QLabel* m_memory_address_to_label; + QLineEdit* m_memory_address_to; + QRadioButton* m_memory_on_read; + QRadioButton* m_memory_on_read_and_write; + QRadioButton* m_memory_on_write; + QRadioButton* m_memory_do_log; + QRadioButton* m_memory_do_break; + QRadioButton* m_memory_do_log_and_break; + + QDialogButtonBox* m_buttons; + BreakpointWidget* m_parent; +}; diff --git a/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp b/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp index 957388be8d..efdb94d466 100644 --- a/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp +++ b/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp @@ -173,6 +173,7 @@ void WatchWidget::closeEvent(QCloseEvent*) { Settings::Instance().SetWatchVisible(false); } + void WatchWidget::OnLoad() { IniFile ini; diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index d43f142cae..800825824b 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -84,6 +84,8 @@ + + @@ -118,6 +120,7 @@ + @@ -155,6 +158,7 @@ + @@ -205,6 +209,8 @@ + + diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 4d460f9735..6e20544770 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -46,6 +46,8 @@ #include "DolphinQt2/Config/LogWidget.h" #include "DolphinQt2/Config/Mapping/MappingWindow.h" #include "DolphinQt2/Config/SettingsWindow.h" +#include "DolphinQt2/FIFOPlayerWindow.h" +#include "DolphinQt2/Debugger/BreakpointWidget.h" #include "DolphinQt2/Debugger/RegisterWidget.h" #include "DolphinQt2/Debugger/WatchWidget.h" #include "DolphinQt2/Host.h" @@ -171,6 +173,7 @@ void MainWindow::CreateComponents() [this](const QString& path) { StartGame(path); }); m_register_widget = new RegisterWidget(this); m_watch_widget = new WatchWidget(this); + m_breakpoint_widget = new BreakpointWidget(this); #if defined(HAVE_XRANDR) && HAVE_XRANDR m_graphics_window = new GraphicsWindow( @@ -321,10 +324,12 @@ void MainWindow::ConnectStack() addDockWidget(Qt::RightDockWidgetArea, m_log_config_widget); addDockWidget(Qt::RightDockWidgetArea, m_register_widget); addDockWidget(Qt::RightDockWidgetArea, m_watch_widget); + addDockWidget(Qt::RightDockWidgetArea, m_breakpoint_widget); tabifyDockWidget(m_log_widget, m_log_config_widget); tabifyDockWidget(m_log_widget, m_register_widget); tabifyDockWidget(m_log_widget, m_watch_widget); + tabifyDockWidget(m_log_widget, m_breakpoint_widget); } void MainWindow::Open() diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index 8cbf8108b9..6584f4e2d1 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -17,6 +17,7 @@ #include "DolphinQt2/RenderWidget.h" #include "DolphinQt2/ToolBar.h" +class BreakpointWidget; struct BootParameters; class FIFOPlayerWindow; class HotkeyScheduler; @@ -141,6 +142,7 @@ private: NetPlaySetupDialog* m_netplay_setup_dialog; GraphicsWindow* m_graphics_window; + BreakpointWidget* m_breakpoint_widget; LogWidget* m_log_widget; LogConfigWidget* m_log_config_widget; FIFOPlayerWindow* m_fifo_window; diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index 0ef13e32e6..326d35a1b4 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -92,6 +92,7 @@ void MenuBar::OnDebugModeToggled(bool enabled) { m_show_registers->setVisible(enabled); m_show_watch->setVisible(enabled); + m_show_breakpoints->setVisible(enabled); } void MenuBar::AddFileMenu() @@ -281,6 +282,15 @@ void MenuBar::AddViewMenu() connect(&Settings::Instance(), &Settings::WatchVisibilityChanged, m_show_watch, &QAction::setChecked); + m_show_breakpoints = view_menu->addAction(tr("&Breakpoints")); + m_show_breakpoints->setCheckable(true); + m_show_breakpoints->setChecked(Settings::Instance().IsBreakpointsVisible()); + + connect(m_show_breakpoints, &QAction::toggled, &Settings::Instance(), + &Settings::SetBreakpointsVisible); + connect(&Settings::Instance(), &Settings::BreakpointsVisibilityChanged, m_show_breakpoints, + &QAction::setChecked); + view_menu->addSeparator(); AddGameListTypeSection(view_menu); diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index 385c8c3ca4..ef73d8623f 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -162,4 +162,5 @@ private: // View QAction* m_show_registers; QAction* m_show_watch; + QAction* m_show_breakpoints; }; diff --git a/Source/Core/DolphinQt2/Settings.cpp b/Source/Core/DolphinQt2/Settings.cpp index d737f0893b..433dd3ab95 100644 --- a/Source/Core/DolphinQt2/Settings.cpp +++ b/Source/Core/DolphinQt2/Settings.cpp @@ -240,3 +240,18 @@ bool Settings::IsWatchVisible() const { return QSettings().value(QStringLiteral("debugger/showwatch")).toBool(); } + +void Settings::SetBreakpointsVisible(bool enabled) +{ + if (IsBreakpointsVisible() != enabled) + { + QSettings().setValue(QStringLiteral("debugger/showbreakpoints"), enabled); + + emit BreakpointsVisibilityChanged(enabled); + } +} + +bool Settings::IsBreakpointsVisible() const +{ + return QSettings().value(QStringLiteral("debugger/showbreakpoints")).toBool(); +} diff --git a/Source/Core/DolphinQt2/Settings.h b/Source/Core/DolphinQt2/Settings.h index a654c21222..7ae4340f7b 100644 --- a/Source/Core/DolphinQt2/Settings.h +++ b/Source/Core/DolphinQt2/Settings.h @@ -85,6 +85,8 @@ public: bool IsRegistersVisible() const; void SetWatchVisible(bool enabled); bool IsWatchVisible() const; + void SetBreakpointsVisible(bool enabled); + bool IsBreakpointsVisible() const; // Other GameListModel* GetGameListModel() const; @@ -102,6 +104,7 @@ signals: void LogConfigVisibilityChanged(bool visible); void EnableCheatsChanged(bool enabled); void WatchVisibilityChanged(bool visible); + void BreakpointsVisibilityChanged(bool visible); void DebugModeToggled(bool enabled); private: