summaryrefslogtreecommitdiffstats
path: root/src/citra_qt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.cpp253
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.hxx78
-rw-r--r--src/citra_qt/main.cpp6
4 files changed, 339 insertions, 0 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 7bf44197d..999724102 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -8,6 +8,7 @@ set(SRCS
debugger/callstack.cpp
debugger/disassembler.cpp
debugger/graphics.cpp
+ debugger/graphics_breakpoints.cpp
debugger/graphics_cmdlists.cpp
debugger/ramview.cpp
debugger/registers.cpp
@@ -24,6 +25,7 @@ set(HEADERS
debugger/callstack.hxx
debugger/disassembler.hxx
debugger/graphics.hxx
+ debugger/graphics_breakpoints.hxx
debugger/graphics_cmdlists.hxx
debugger/ramview.hxx
debugger/registers.hxx
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp
new file mode 100644
index 000000000..2f41d5f77
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.cpp
@@ -0,0 +1,253 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <QMetaType>
+#include <QPushButton>
+#include <QTreeWidget>
+#include <QVBoxLayout>
+#include <QLabel>
+
+#include "graphics_breakpoints.hxx"
+
+BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
+ : QAbstractListModel(parent), context_weak(debug_context),
+ at_breakpoint(debug_context->at_breakpoint),
+ active_breakpoint(debug_context->active_breakpoint)
+{
+
+}
+
+int BreakPointModel::columnCount(const QModelIndex& parent) const
+{
+ return 2;
+}
+
+int BreakPointModel::rowCount(const QModelIndex& parent) const
+{
+ return static_cast<int>(Pica::DebugContext::Event::NumEvents);
+}
+
+QVariant BreakPointModel::data(const QModelIndex& index, int role) const
+{
+ const auto event = static_cast<Pica::DebugContext::Event>(index.row());
+
+ switch (role) {
+ case Qt::DisplayRole:
+ {
+ if (index.column() == 0) {
+ std::map<Pica::DebugContext::Event, QString> map;
+ map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")});
+ map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")});
+ map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incomming primitive batch")});
+ map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")});
+
+ _dbg_assert_(GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
+
+ return map[event];
+ } else if (index.column() == 1) {
+ return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled");
+ }
+
+ break;
+ }
+
+ case Qt::BackgroundRole:
+ {
+ if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
+ return QBrush(QColor(0xE0, 0xE0, 0x10));
+ }
+ break;
+ }
+
+ case Role_IsEnabled:
+ {
+ auto context = context_weak.lock();
+ return context && context->breakpoints[event].enabled;
+ }
+
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ if (section == 0) {
+ return tr("Event");
+ } else if (section == 1) {
+ return tr("Status");
+ }
+
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+ const auto event = static_cast<Pica::DebugContext::Event>(index.row());
+
+ switch (role) {
+ case Role_IsEnabled:
+ {
+ auto context = context_weak.lock();
+ if (!context)
+ return false;
+
+ context->breakpoints[event].enabled = value.toBool();
+ QModelIndex changed_index = createIndex(index.row(), 1);
+ emit dataChanged(changed_index, changed_index);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
+{
+ auto context = context_weak.lock();
+ if (!context)
+ return;
+
+ active_breakpoint = context->active_breakpoint;
+ at_breakpoint = context->at_breakpoint;
+ emit dataChanged(createIndex(static_cast<int>(event), 0),
+ createIndex(static_cast<int>(event), 1));
+}
+
+void BreakPointModel::OnResumed()
+{
+ auto context = context_weak.lock();
+ if (!context)
+ return;
+
+ at_breakpoint = context->at_breakpoint;
+ emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
+ createIndex(static_cast<int>(active_breakpoint), 1));
+ active_breakpoint = context->active_breakpoint;
+}
+
+
+GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent)
+ : QDockWidget(tr("Pica Breakpoints"), parent),
+ Pica::DebugContext::BreakPointObserver(debug_context)
+{
+ setObjectName("PicaBreakPointsWidget");
+
+ status_text = new QLabel(tr("Emulation running"));
+ resume_button = new QPushButton(tr("Resume"));
+ resume_button->setEnabled(false);
+
+ breakpoint_model = new BreakPointModel(debug_context, this);
+ breakpoint_list = new QTreeView;
+ breakpoint_list->setModel(breakpoint_model);
+
+ toggle_breakpoint_button = new QPushButton(tr("Enable"));
+ toggle_breakpoint_button->setEnabled(false);
+
+ qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
+
+ connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
+
+ connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
+ this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
+ Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
+
+ connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
+ breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
+ Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
+
+ connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
+ breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
+
+ connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(OnBreakpointSelectionChanged(QModelIndex)));
+
+ connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled()));
+
+ QWidget* main_widget = new QWidget;
+ auto main_layout = new QVBoxLayout;
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(status_text);
+ sub_layout->addWidget(resume_button);
+ main_layout->addLayout(sub_layout);
+ }
+ main_layout->addWidget(breakpoint_list);
+ main_layout->addWidget(toggle_breakpoint_button);
+ main_widget->setLayout(main_layout);
+
+ setWidget(main_widget);
+}
+
+void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
+{
+ // Process in GUI thread
+ emit BreakPointHit(event, data);
+}
+
+void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ status_text->setText(tr("Emulation halted at breakpoint"));
+ resume_button->setEnabled(true);
+}
+
+void GraphicsBreakPointsWidget::OnPicaResume()
+{
+ // Process in GUI thread
+ emit Resumed();
+}
+
+void GraphicsBreakPointsWidget::OnResumed()
+{
+ status_text->setText(tr("Emulation running"));
+ resume_button->setEnabled(false);
+}
+
+void GraphicsBreakPointsWidget::OnResumeRequested()
+{
+ if (auto context = context_weak.lock())
+ context->Resume();
+}
+
+void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index)
+{
+ if (!index.isValid()) {
+ toggle_breakpoint_button->setEnabled(false);
+ return;
+ }
+
+ toggle_breakpoint_button->setEnabled(true);
+ UpdateToggleBreakpointButton(index);
+}
+
+void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled()
+{
+ QModelIndex index = breakpoint_list->selectionModel()->currentIndex();
+ bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool());
+
+ breakpoint_model->setData(index, new_state,
+ BreakPointModel::Role_IsEnabled);
+ UpdateToggleBreakpointButton(index);
+}
+
+void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index)
+{
+ if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) {
+ toggle_breakpoint_button->setText(tr("Disable"));
+ } else {
+ toggle_breakpoint_button->setText(tr("Enable"));
+ }
+}
diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx
new file mode 100644
index 000000000..edc41283e
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.hxx
@@ -0,0 +1,78 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include <QAbstractListModel>
+#include <QDockWidget>
+
+#include "video_core/debug_utils/debug_utils.h"
+
+class QLabel;
+class QPushButton;
+class QTreeView;
+
+class BreakPointModel : public QAbstractListModel {
+ Q_OBJECT
+
+public:
+ enum {
+ Role_IsEnabled = Qt::UserRole,
+ };
+
+ BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent);
+
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
+
+public slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event);
+ void OnResumed();
+
+private:
+ bool at_breakpoint;
+ Pica::DebugContext::Event active_breakpoint;
+ std::weak_ptr<Pica::DebugContext> context_weak;
+};
+
+class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver {
+ Q_OBJECT
+
+ using Event = Pica::DebugContext::Event;
+
+public:
+ GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent = nullptr);
+
+ void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnPicaResume() override;
+
+public slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event, void* data);
+ void OnResumeRequested();
+ void OnResumed();
+ void OnBreakpointSelectionChanged(const QModelIndex&);
+ void OnToggleBreakpointEnabled();
+
+signals:
+ void Resumed();
+ void BreakPointHit(Pica::DebugContext::Event event, void* data);
+ void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+private:
+ void UpdateToggleBreakpointButton(const QModelIndex& index);
+
+ QLabel* status_text;
+ QPushButton* resume_button;
+ QPushButton* toggle_breakpoint_button;
+
+ BreakPointModel* breakpoint_model;
+ QTreeView* breakpoint_list;
+};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 869826e61..84afe59d8 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -20,6 +20,7 @@
#include "debugger/callstack.hxx"
#include "debugger/ramview.hxx"
#include "debugger/graphics.hxx"
+#include "debugger/graphics_breakpoints.hxx"
#include "debugger/graphics_cmdlists.hxx"
#include "core/settings.h"
@@ -69,12 +70,17 @@ GMainWindow::GMainWindow()
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide();
+ auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
+ graphicsBreakpointsWidget->hide();
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(disasmWidget->toggleViewAction());
debug_menu->addAction(registersWidget->toggleViewAction());
debug_menu->addAction(callstackWidget->toggleViewAction());
debug_menu->addAction(graphicsWidget->toggleViewAction());
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
+ debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
// Set default UI state
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half