diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoints.cpp | 253 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoints.hxx | 78 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 6 |
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 | |||
| 8 | debugger/callstack.cpp | 8 | debugger/callstack.cpp |
| 9 | debugger/disassembler.cpp | 9 | debugger/disassembler.cpp |
| 10 | debugger/graphics.cpp | 10 | debugger/graphics.cpp |
| 11 | debugger/graphics_breakpoints.cpp | ||
| 11 | debugger/graphics_cmdlists.cpp | 12 | debugger/graphics_cmdlists.cpp |
| 12 | debugger/ramview.cpp | 13 | debugger/ramview.cpp |
| 13 | debugger/registers.cpp | 14 | debugger/registers.cpp |
| @@ -24,6 +25,7 @@ set(HEADERS | |||
| 24 | debugger/callstack.hxx | 25 | debugger/callstack.hxx |
| 25 | debugger/disassembler.hxx | 26 | debugger/disassembler.hxx |
| 26 | debugger/graphics.hxx | 27 | debugger/graphics.hxx |
| 28 | debugger/graphics_breakpoints.hxx | ||
| 27 | debugger/graphics_cmdlists.hxx | 29 | debugger/graphics_cmdlists.hxx |
| 28 | debugger/ramview.hxx | 30 | debugger/ramview.hxx |
| 29 | debugger/registers.hxx | 31 | 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 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QMetaType> | ||
| 6 | #include <QPushButton> | ||
| 7 | #include <QTreeWidget> | ||
| 8 | #include <QVBoxLayout> | ||
| 9 | #include <QLabel> | ||
| 10 | |||
| 11 | #include "graphics_breakpoints.hxx" | ||
| 12 | |||
| 13 | BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||
| 14 | : QAbstractListModel(parent), context_weak(debug_context), | ||
| 15 | at_breakpoint(debug_context->at_breakpoint), | ||
| 16 | active_breakpoint(debug_context->active_breakpoint) | ||
| 17 | { | ||
| 18 | |||
| 19 | } | ||
| 20 | |||
| 21 | int BreakPointModel::columnCount(const QModelIndex& parent) const | ||
| 22 | { | ||
| 23 | return 2; | ||
| 24 | } | ||
| 25 | |||
| 26 | int BreakPointModel::rowCount(const QModelIndex& parent) const | ||
| 27 | { | ||
| 28 | return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||
| 29 | } | ||
| 30 | |||
| 31 | QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||
| 32 | { | ||
| 33 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 34 | |||
| 35 | switch (role) { | ||
| 36 | case Qt::DisplayRole: | ||
| 37 | { | ||
| 38 | if (index.column() == 0) { | ||
| 39 | std::map<Pica::DebugContext::Event, QString> map; | ||
| 40 | map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); | ||
| 41 | map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); | ||
| 42 | map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incomming primitive batch")}); | ||
| 43 | map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); | ||
| 44 | |||
| 45 | _dbg_assert_(GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||
| 46 | |||
| 47 | return map[event]; | ||
| 48 | } else if (index.column() == 1) { | ||
| 49 | return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | ||
| 50 | } | ||
| 51 | |||
| 52 | break; | ||
| 53 | } | ||
| 54 | |||
| 55 | case Qt::BackgroundRole: | ||
| 56 | { | ||
| 57 | if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||
| 58 | return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||
| 59 | } | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | case Role_IsEnabled: | ||
| 64 | { | ||
| 65 | auto context = context_weak.lock(); | ||
| 66 | return context && context->breakpoints[event].enabled; | ||
| 67 | } | ||
| 68 | |||
| 69 | default: | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | return QVariant(); | ||
| 73 | } | ||
| 74 | |||
| 75 | QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
| 76 | { | ||
| 77 | switch(role) { | ||
| 78 | case Qt::DisplayRole: | ||
| 79 | { | ||
| 80 | if (section == 0) { | ||
| 81 | return tr("Event"); | ||
| 82 | } else if (section == 1) { | ||
| 83 | return tr("Status"); | ||
| 84 | } | ||
| 85 | |||
| 86 | break; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | return QVariant(); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||
| 94 | { | ||
| 95 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 96 | |||
| 97 | switch (role) { | ||
| 98 | case Role_IsEnabled: | ||
| 99 | { | ||
| 100 | auto context = context_weak.lock(); | ||
| 101 | if (!context) | ||
| 102 | return false; | ||
| 103 | |||
| 104 | context->breakpoints[event].enabled = value.toBool(); | ||
| 105 | QModelIndex changed_index = createIndex(index.row(), 1); | ||
| 106 | emit dataChanged(changed_index, changed_index); | ||
| 107 | return true; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | return false; | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||
| 116 | { | ||
| 117 | auto context = context_weak.lock(); | ||
| 118 | if (!context) | ||
| 119 | return; | ||
| 120 | |||
| 121 | active_breakpoint = context->active_breakpoint; | ||
| 122 | at_breakpoint = context->at_breakpoint; | ||
| 123 | emit dataChanged(createIndex(static_cast<int>(event), 0), | ||
| 124 | createIndex(static_cast<int>(event), 1)); | ||
| 125 | } | ||
| 126 | |||
| 127 | void BreakPointModel::OnResumed() | ||
| 128 | { | ||
| 129 | auto context = context_weak.lock(); | ||
| 130 | if (!context) | ||
| 131 | return; | ||
| 132 | |||
| 133 | at_breakpoint = context->at_breakpoint; | ||
| 134 | emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | ||
| 135 | createIndex(static_cast<int>(active_breakpoint), 1)); | ||
| 136 | active_breakpoint = context->active_breakpoint; | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 141 | QWidget* parent) | ||
| 142 | : QDockWidget(tr("Pica Breakpoints"), parent), | ||
| 143 | Pica::DebugContext::BreakPointObserver(debug_context) | ||
| 144 | { | ||
| 145 | setObjectName("PicaBreakPointsWidget"); | ||
| 146 | |||
| 147 | status_text = new QLabel(tr("Emulation running")); | ||
| 148 | resume_button = new QPushButton(tr("Resume")); | ||
| 149 | resume_button->setEnabled(false); | ||
| 150 | |||
| 151 | breakpoint_model = new BreakPointModel(debug_context, this); | ||
| 152 | breakpoint_list = new QTreeView; | ||
| 153 | breakpoint_list->setModel(breakpoint_model); | ||
| 154 | |||
| 155 | toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||
| 156 | toggle_breakpoint_button->setEnabled(false); | ||
| 157 | |||
| 158 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 159 | |||
| 160 | connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||
| 161 | |||
| 162 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 163 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 164 | Qt::BlockingQueuedConnection); | ||
| 165 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 166 | |||
| 167 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 168 | breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), | ||
| 169 | Qt::BlockingQueuedConnection); | ||
| 170 | connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||
| 171 | |||
| 172 | connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | ||
| 173 | breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | ||
| 174 | |||
| 175 | connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||
| 176 | this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||
| 177 | |||
| 178 | connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||
| 179 | |||
| 180 | QWidget* main_widget = new QWidget; | ||
| 181 | auto main_layout = new QVBoxLayout; | ||
| 182 | { | ||
| 183 | auto sub_layout = new QHBoxLayout; | ||
| 184 | sub_layout->addWidget(status_text); | ||
| 185 | sub_layout->addWidget(resume_button); | ||
| 186 | main_layout->addLayout(sub_layout); | ||
| 187 | } | ||
| 188 | main_layout->addWidget(breakpoint_list); | ||
| 189 | main_layout->addWidget(toggle_breakpoint_button); | ||
| 190 | main_widget->setLayout(main_layout); | ||
| 191 | |||
| 192 | setWidget(main_widget); | ||
| 193 | } | ||
| 194 | |||
| 195 | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||
| 196 | { | ||
| 197 | // Process in GUI thread | ||
| 198 | emit BreakPointHit(event, data); | ||
| 199 | } | ||
| 200 | |||
| 201 | void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 202 | { | ||
| 203 | status_text->setText(tr("Emulation halted at breakpoint")); | ||
| 204 | resume_button->setEnabled(true); | ||
| 205 | } | ||
| 206 | |||
| 207 | void GraphicsBreakPointsWidget::OnPicaResume() | ||
| 208 | { | ||
| 209 | // Process in GUI thread | ||
| 210 | emit Resumed(); | ||
| 211 | } | ||
| 212 | |||
| 213 | void GraphicsBreakPointsWidget::OnResumed() | ||
| 214 | { | ||
| 215 | status_text->setText(tr("Emulation running")); | ||
| 216 | resume_button->setEnabled(false); | ||
| 217 | } | ||
| 218 | |||
| 219 | void GraphicsBreakPointsWidget::OnResumeRequested() | ||
| 220 | { | ||
| 221 | if (auto context = context_weak.lock()) | ||
| 222 | context->Resume(); | ||
| 223 | } | ||
| 224 | |||
| 225 | void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | ||
| 226 | { | ||
| 227 | if (!index.isValid()) { | ||
| 228 | toggle_breakpoint_button->setEnabled(false); | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | toggle_breakpoint_button->setEnabled(true); | ||
| 233 | UpdateToggleBreakpointButton(index); | ||
| 234 | } | ||
| 235 | |||
| 236 | void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | ||
| 237 | { | ||
| 238 | QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||
| 239 | bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||
| 240 | |||
| 241 | breakpoint_model->setData(index, new_state, | ||
| 242 | BreakPointModel::Role_IsEnabled); | ||
| 243 | UpdateToggleBreakpointButton(index); | ||
| 244 | } | ||
| 245 | |||
| 246 | void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||
| 247 | { | ||
| 248 | if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||
| 249 | toggle_breakpoint_button->setText(tr("Disable")); | ||
| 250 | } else { | ||
| 251 | toggle_breakpoint_button->setText(tr("Enable")); | ||
| 252 | } | ||
| 253 | } | ||
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 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include <QAbstractListModel> | ||
| 10 | #include <QDockWidget> | ||
| 11 | |||
| 12 | #include "video_core/debug_utils/debug_utils.h" | ||
| 13 | |||
| 14 | class QLabel; | ||
| 15 | class QPushButton; | ||
| 16 | class QTreeView; | ||
| 17 | |||
| 18 | class BreakPointModel : public QAbstractListModel { | ||
| 19 | Q_OBJECT | ||
| 20 | |||
| 21 | public: | ||
| 22 | enum { | ||
| 23 | Role_IsEnabled = Qt::UserRole, | ||
| 24 | }; | ||
| 25 | |||
| 26 | BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent); | ||
| 27 | |||
| 28 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||
| 29 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||
| 30 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||
| 31 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||
| 32 | |||
| 33 | bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); | ||
| 34 | |||
| 35 | public slots: | ||
| 36 | void OnBreakPointHit(Pica::DebugContext::Event event); | ||
| 37 | void OnResumed(); | ||
| 38 | |||
| 39 | private: | ||
| 40 | bool at_breakpoint; | ||
| 41 | Pica::DebugContext::Event active_breakpoint; | ||
| 42 | std::weak_ptr<Pica::DebugContext> context_weak; | ||
| 43 | }; | ||
| 44 | |||
| 45 | class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 46 | Q_OBJECT | ||
| 47 | |||
| 48 | using Event = Pica::DebugContext::Event; | ||
| 49 | |||
| 50 | public: | ||
| 51 | GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 52 | QWidget* parent = nullptr); | ||
| 53 | |||
| 54 | void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 55 | void OnPicaResume() override; | ||
| 56 | |||
| 57 | public slots: | ||
| 58 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 59 | void OnResumeRequested(); | ||
| 60 | void OnResumed(); | ||
| 61 | void OnBreakpointSelectionChanged(const QModelIndex&); | ||
| 62 | void OnToggleBreakpointEnabled(); | ||
| 63 | |||
| 64 | signals: | ||
| 65 | void Resumed(); | ||
| 66 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 67 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||
| 68 | |||
| 69 | private: | ||
| 70 | void UpdateToggleBreakpointButton(const QModelIndex& index); | ||
| 71 | |||
| 72 | QLabel* status_text; | ||
| 73 | QPushButton* resume_button; | ||
| 74 | QPushButton* toggle_breakpoint_button; | ||
| 75 | |||
| 76 | BreakPointModel* breakpoint_model; | ||
| 77 | QTreeView* breakpoint_list; | ||
| 78 | }; | ||
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 @@ | |||
| 20 | #include "debugger/callstack.hxx" | 20 | #include "debugger/callstack.hxx" |
| 21 | #include "debugger/ramview.hxx" | 21 | #include "debugger/ramview.hxx" |
| 22 | #include "debugger/graphics.hxx" | 22 | #include "debugger/graphics.hxx" |
| 23 | #include "debugger/graphics_breakpoints.hxx" | ||
| 23 | #include "debugger/graphics_cmdlists.hxx" | 24 | #include "debugger/graphics_cmdlists.hxx" |
| 24 | 25 | ||
| 25 | #include "core/settings.h" | 26 | #include "core/settings.h" |
| @@ -69,12 +70,17 @@ GMainWindow::GMainWindow() | |||
| 69 | addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | 70 | addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); |
| 70 | graphicsCommandsWidget->hide(); | 71 | graphicsCommandsWidget->hide(); |
| 71 | 72 | ||
| 73 | auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); | ||
| 74 | addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); | ||
| 75 | graphicsBreakpointsWidget->hide(); | ||
| 76 | |||
| 72 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 77 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 73 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 78 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| 74 | debug_menu->addAction(registersWidget->toggleViewAction()); | 79 | debug_menu->addAction(registersWidget->toggleViewAction()); |
| 75 | debug_menu->addAction(callstackWidget->toggleViewAction()); | 80 | debug_menu->addAction(callstackWidget->toggleViewAction()); |
| 76 | debug_menu->addAction(graphicsWidget->toggleViewAction()); | 81 | debug_menu->addAction(graphicsWidget->toggleViewAction()); |
| 77 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | 82 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); |
| 83 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||
| 78 | 84 | ||
| 79 | // Set default UI state | 85 | // Set default UI state |
| 80 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half | 86 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half |