| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | // Copyright 2014 Citra Emulator Project
 | 
					
						
							| 
									
										
										
										
											2014-12-16 21:38:14 -08:00
										 |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-21 00:21:23 +09:00
										 |  |  | #include "citra_qt/debugger/graphics_breakpoints.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-11 00:23:00 -04:00
										 |  |  | #include <QLabel>
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | #include <QMetaType>
 | 
					
						
							|  |  |  | #include <QPushButton>
 | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  | #include <QTreeView>
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | #include <QVBoxLayout>
 | 
					
						
							| 
									
										
										
										
											2015-09-11 00:23:00 -04:00
										 |  |  | #include "citra_qt/debugger/graphics_breakpoints_p.h"
 | 
					
						
							|  |  |  | #include "common/assert.h"
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | 
					
						
							|  |  |  |     : QAbstractListModel(parent), context_weak(debug_context), | 
					
						
							|  |  |  |       at_breakpoint(debug_context->at_breakpoint), | 
					
						
							| 
									
										
										
										
											2016-09-18 18:01:46 -07:00
										 |  |  |       active_breakpoint(debug_context->active_breakpoint) {} | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | int BreakPointModel::columnCount(const QModelIndex& parent) const { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     return 1; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | int BreakPointModel::rowCount(const QModelIndex& parent) const { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     return static_cast<int>(Pica::DebugContext::Event::NumEvents); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (role) { | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     case Qt::DisplayRole: { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |         if (index.column() == 0) { | 
					
						
							| 
									
										
										
										
											2014-12-19 18:37:14 +01:00
										 |  |  |             static const std::map<Pica::DebugContext::Event, QString> map = { | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |                 {Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::IncomingDisplayTransfer, | 
					
						
							|  |  |  |                  tr("Incoming display transfer")}, | 
					
						
							|  |  |  |                 {Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed")}, | 
					
						
							| 
									
										
										
										
											2016-09-18 18:01:46 -07:00
										 |  |  |                 {Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped")}, | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |             DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | 
					
						
							| 
									
										
										
										
											2014-12-19 18:37:14 +01:00
										 |  |  |             return (map.find(event) != map.end()) ? map.at(event) : QString(); | 
					
						
							| 
									
										
										
										
											2014-12-04 21:00:06 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     case Qt::CheckStateRole: { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |         if (index.column() == 0) | 
					
						
							|  |  |  |             return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     case Qt::BackgroundRole: { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | 
					
						
							|  |  |  |             return QBrush(QColor(0xE0, 0xE0, 0x10)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     case Role_IsEnabled: { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         auto context = context_weak.lock(); | 
					
						
							| 
									
										
										
										
											2016-04-24 14:19:49 +02:00
										 |  |  |         return context && context->breakpoints[(int)event].enabled; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return QVariant(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     if (!index.isValid()) | 
					
						
							|  |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     Qt::ItemFlags flags = Qt::ItemIsEnabled; | 
					
						
							|  |  |  |     if (index.column() == 0) | 
					
						
							|  |  |  |         flags |= Qt::ItemIsUserCheckable; | 
					
						
							|  |  |  |     return flags; | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (role) { | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     case Qt::CheckStateRole: { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |         if (index.column() != 0) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         auto context = context_weak.lock(); | 
					
						
							|  |  |  |         if (!context) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-24 14:19:49 +02:00
										 |  |  |         context->breakpoints[(int)event].enabled = value == Qt::Checked; | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |         QModelIndex changed_index = createIndex(index.row(), 0); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         emit dataChanged(changed_index, changed_index); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     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), | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |                      createIndex(static_cast<int>(event), 0)); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void BreakPointModel::OnResumed() { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     auto context = context_weak.lock(); | 
					
						
							|  |  |  |     if (!context) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     at_breakpoint = context->at_breakpoint; | 
					
						
							|  |  |  |     emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |                      createIndex(static_cast<int>(active_breakpoint), 0)); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     active_breakpoint = context->active_breakpoint; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( | 
					
						
							|  |  |  |     std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent) | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     : QDockWidget(tr("Pica Breakpoints"), parent), | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |       Pica::DebugContext::BreakPointObserver(debug_context) { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     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; | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     breakpoint_list->setRootIsDecorated(false); | 
					
						
							|  |  |  |     breakpoint_list->setHeaderHidden(true); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     breakpoint_list->setModel(breakpoint_model); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this, | 
					
						
							|  |  |  |             SLOT(OnItemDoubleClicked(const QModelIndex&))); | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), this, | 
					
						
							|  |  |  |             SLOT(OnBreakPointHit(Pica::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event, void*)), breakpoint_model, | 
					
						
							|  |  |  |             SLOT(OnBreakPointHit(Pica::DebugContext::Event)), Qt::BlockingQueuedConnection); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  |     connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)), | 
					
						
							|  |  |  |             breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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_widget->setLayout(main_layout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setWidget(main_widget); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     // Process in GUI thread
 | 
					
						
							|  |  |  |     emit BreakPointHit(event, data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     status_text->setText(tr("Emulation halted at breakpoint")); | 
					
						
							|  |  |  |     resume_button->setEnabled(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnPicaResume() { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     // Process in GUI thread
 | 
					
						
							|  |  |  |     emit Resumed(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnResumed() { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     status_text->setText(tr("Emulation running")); | 
					
						
							|  |  |  |     resume_button->setEnabled(false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnResumeRequested() { | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |     if (auto context = context_weak.lock()) | 
					
						
							|  |  |  |         context->Resume(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-18 09:38:01 +09:00
										 |  |  | void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) { | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     if (!index.isValid()) | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-22 20:42:59 -05:00
										 |  |  |     QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0); | 
					
						
							|  |  |  |     QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole); | 
					
						
							|  |  |  |     QVariant new_state = Qt::Unchecked; | 
					
						
							|  |  |  |     if (enabled == Qt::Unchecked) | 
					
						
							|  |  |  |         new_state = Qt::Checked; | 
					
						
							|  |  |  |     breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole); | 
					
						
							| 
									
										
										
										
											2014-10-25 20:28:24 +02:00
										 |  |  | } |