diff options
Diffstat (limited to 'src/citra_qt/debugger/graphics_breakpoints.cpp')
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoints.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp new file mode 100644 index 000000000..9486f06cc --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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 | #include "graphics_breakpoints_p.hxx" | ||
| 13 | |||
| 14 | BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||
| 15 | : QAbstractListModel(parent), context_weak(debug_context), | ||
| 16 | at_breakpoint(debug_context->at_breakpoint), | ||
| 17 | active_breakpoint(debug_context->active_breakpoint) | ||
| 18 | { | ||
| 19 | |||
| 20 | } | ||
| 21 | |||
| 22 | int BreakPointModel::columnCount(const QModelIndex& parent) const | ||
| 23 | { | ||
| 24 | return 2; | ||
| 25 | } | ||
| 26 | |||
| 27 | int BreakPointModel::rowCount(const QModelIndex& parent) const | ||
| 28 | { | ||
| 29 | return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||
| 30 | } | ||
| 31 | |||
| 32 | QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||
| 33 | { | ||
| 34 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 35 | |||
| 36 | switch (role) { | ||
| 37 | case Qt::DisplayRole: | ||
| 38 | { | ||
| 39 | switch (index.column()) { | ||
| 40 | case 0: | ||
| 41 | { | ||
| 42 | static const std::map<Pica::DebugContext::Event, QString> map = { | ||
| 43 | { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, | ||
| 44 | { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, | ||
| 45 | { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, | ||
| 46 | { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, | ||
| 47 | { Pica::DebugContext::Event::VertexLoaded, tr("Vertex Loaded") } | ||
| 48 | }; | ||
| 49 | |||
| 50 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||
| 51 | |||
| 52 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | ||
| 53 | } | ||
| 54 | |||
| 55 | case 1: | ||
| 56 | return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | ||
| 57 | |||
| 58 | default: | ||
| 59 | break; | ||
| 60 | } | ||
| 61 | |||
| 62 | break; | ||
| 63 | } | ||
| 64 | |||
| 65 | case Qt::BackgroundRole: | ||
| 66 | { | ||
| 67 | if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||
| 68 | return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||
| 69 | } | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | |||
| 73 | case Role_IsEnabled: | ||
| 74 | { | ||
| 75 | auto context = context_weak.lock(); | ||
| 76 | return context && context->breakpoints[event].enabled; | ||
| 77 | } | ||
| 78 | |||
| 79 | default: | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | return QVariant(); | ||
| 83 | } | ||
| 84 | |||
| 85 | QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
| 86 | { | ||
| 87 | switch(role) { | ||
| 88 | case Qt::DisplayRole: | ||
| 89 | { | ||
| 90 | if (section == 0) { | ||
| 91 | return tr("Event"); | ||
| 92 | } else if (section == 1) { | ||
| 93 | return tr("Status"); | ||
| 94 | } | ||
| 95 | |||
| 96 | break; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | return QVariant(); | ||
| 101 | } | ||
| 102 | |||
| 103 | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||
| 104 | { | ||
| 105 | const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||
| 106 | |||
| 107 | switch (role) { | ||
| 108 | case Role_IsEnabled: | ||
| 109 | { | ||
| 110 | auto context = context_weak.lock(); | ||
| 111 | if (!context) | ||
| 112 | return false; | ||
| 113 | |||
| 114 | context->breakpoints[event].enabled = value.toBool(); | ||
| 115 | QModelIndex changed_index = createIndex(index.row(), 1); | ||
| 116 | emit dataChanged(changed_index, changed_index); | ||
| 117 | return true; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | return false; | ||
| 122 | } | ||
| 123 | |||
| 124 | |||
| 125 | void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||
| 126 | { | ||
| 127 | auto context = context_weak.lock(); | ||
| 128 | if (!context) | ||
| 129 | return; | ||
| 130 | |||
| 131 | active_breakpoint = context->active_breakpoint; | ||
| 132 | at_breakpoint = context->at_breakpoint; | ||
| 133 | emit dataChanged(createIndex(static_cast<int>(event), 0), | ||
| 134 | createIndex(static_cast<int>(event), 1)); | ||
| 135 | } | ||
| 136 | |||
| 137 | void BreakPointModel::OnResumed() | ||
| 138 | { | ||
| 139 | auto context = context_weak.lock(); | ||
| 140 | if (!context) | ||
| 141 | return; | ||
| 142 | |||
| 143 | at_breakpoint = context->at_breakpoint; | ||
| 144 | emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | ||
| 145 | createIndex(static_cast<int>(active_breakpoint), 1)); | ||
| 146 | active_breakpoint = context->active_breakpoint; | ||
| 147 | } | ||
| 148 | |||
| 149 | |||
| 150 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 151 | QWidget* parent) | ||
| 152 | : QDockWidget(tr("Pica Breakpoints"), parent), | ||
| 153 | Pica::DebugContext::BreakPointObserver(debug_context) | ||
| 154 | { | ||
| 155 | setObjectName("PicaBreakPointsWidget"); | ||
| 156 | |||
| 157 | status_text = new QLabel(tr("Emulation running")); | ||
| 158 | resume_button = new QPushButton(tr("Resume")); | ||
| 159 | resume_button->setEnabled(false); | ||
| 160 | |||
| 161 | breakpoint_model = new BreakPointModel(debug_context, this); | ||
| 162 | breakpoint_list = new QTreeView; | ||
| 163 | breakpoint_list->setModel(breakpoint_model); | ||
| 164 | |||
| 165 | toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||
| 166 | toggle_breakpoint_button->setEnabled(false); | ||
| 167 | |||
| 168 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 169 | |||
| 170 | connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||
| 171 | |||
| 172 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 173 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 174 | Qt::BlockingQueuedConnection); | ||
| 175 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 176 | |||
| 177 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 178 | breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), | ||
| 179 | Qt::BlockingQueuedConnection); | ||
| 180 | connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||
| 181 | |||
| 182 | connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | ||
| 183 | breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | ||
| 184 | |||
| 185 | connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||
| 186 | this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||
| 187 | |||
| 188 | connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||
| 189 | |||
| 190 | QWidget* main_widget = new QWidget; | ||
| 191 | auto main_layout = new QVBoxLayout; | ||
| 192 | { | ||
| 193 | auto sub_layout = new QHBoxLayout; | ||
| 194 | sub_layout->addWidget(status_text); | ||
| 195 | sub_layout->addWidget(resume_button); | ||
| 196 | main_layout->addLayout(sub_layout); | ||
| 197 | } | ||
| 198 | main_layout->addWidget(breakpoint_list); | ||
| 199 | main_layout->addWidget(toggle_breakpoint_button); | ||
| 200 | main_widget->setLayout(main_layout); | ||
| 201 | |||
| 202 | setWidget(main_widget); | ||
| 203 | } | ||
| 204 | |||
| 205 | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||
| 206 | { | ||
| 207 | // Process in GUI thread | ||
| 208 | emit BreakPointHit(event, data); | ||
| 209 | } | ||
| 210 | |||
| 211 | void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 212 | { | ||
| 213 | status_text->setText(tr("Emulation halted at breakpoint")); | ||
| 214 | resume_button->setEnabled(true); | ||
| 215 | } | ||
| 216 | |||
| 217 | void GraphicsBreakPointsWidget::OnPicaResume() | ||
| 218 | { | ||
| 219 | // Process in GUI thread | ||
| 220 | emit Resumed(); | ||
| 221 | } | ||
| 222 | |||
| 223 | void GraphicsBreakPointsWidget::OnResumed() | ||
| 224 | { | ||
| 225 | status_text->setText(tr("Emulation running")); | ||
| 226 | resume_button->setEnabled(false); | ||
| 227 | } | ||
| 228 | |||
| 229 | void GraphicsBreakPointsWidget::OnResumeRequested() | ||
| 230 | { | ||
| 231 | if (auto context = context_weak.lock()) | ||
| 232 | context->Resume(); | ||
| 233 | } | ||
| 234 | |||
| 235 | void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | ||
| 236 | { | ||
| 237 | if (!index.isValid()) { | ||
| 238 | toggle_breakpoint_button->setEnabled(false); | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | |||
| 242 | toggle_breakpoint_button->setEnabled(true); | ||
| 243 | UpdateToggleBreakpointButton(index); | ||
| 244 | } | ||
| 245 | |||
| 246 | void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | ||
| 247 | { | ||
| 248 | QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||
| 249 | bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||
| 250 | |||
| 251 | breakpoint_model->setData(index, new_state, | ||
| 252 | BreakPointModel::Role_IsEnabled); | ||
| 253 | UpdateToggleBreakpointButton(index); | ||
| 254 | } | ||
| 255 | |||
| 256 | void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||
| 257 | { | ||
| 258 | if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||
| 259 | toggle_breakpoint_button->setText(tr("Disable")); | ||
| 260 | } else { | ||
| 261 | toggle_breakpoint_button->setText(tr("Enable")); | ||
| 262 | } | ||
| 263 | } | ||