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