graphics_breakpoints.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <QLabel>
  5. #include <QMetaType>
  6. #include <QPushButton>
  7. #include <QTreeView>
  8. #include <QVBoxLayout>
  9. #include "citra_qt/debugger/graphics_breakpoints.h"
  10. #include "citra_qt/debugger/graphics_breakpoints_p.h"
  11. #include "common/assert.h"
  12. BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
  13. : QAbstractListModel(parent), context_weak(debug_context),
  14. at_breakpoint(debug_context->at_breakpoint),
  15. active_breakpoint(debug_context->active_breakpoint)
  16. {
  17. }
  18. int BreakPointModel::columnCount(const QModelIndex& parent) const
  19. {
  20. return 1;
  21. }
  22. int BreakPointModel::rowCount(const QModelIndex& parent) const
  23. {
  24. return static_cast<int>(Pica::DebugContext::Event::NumEvents);
  25. }
  26. QVariant BreakPointModel::data(const QModelIndex& index, int role) const
  27. {
  28. const auto event = static_cast<Pica::DebugContext::Event>(index.row());
  29. switch (role) {
  30. case Qt::DisplayRole:
  31. {
  32. if (index.column() == 0) {
  33. static const std::map<Pica::DebugContext::Event, QString> map = {
  34. { Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") },
  35. { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") },
  36. { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
  37. { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
  38. { Pica::DebugContext::Event::VertexShaderInvocation, tr("Vertex shader invocation") },
  39. { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") },
  40. { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") },
  41. { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") }
  42. };
  43. DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
  44. return (map.find(event) != map.end()) ? map.at(event) : QString();
  45. }
  46. break;
  47. }
  48. case Qt::CheckStateRole:
  49. {
  50. if (index.column() == 0)
  51. return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked;
  52. break;
  53. }
  54. case Qt::BackgroundRole:
  55. {
  56. if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
  57. return QBrush(QColor(0xE0, 0xE0, 0x10));
  58. }
  59. break;
  60. }
  61. case Role_IsEnabled:
  62. {
  63. auto context = context_weak.lock();
  64. return context && context->breakpoints[(int)event].enabled;
  65. }
  66. default:
  67. break;
  68. }
  69. return QVariant();
  70. }
  71. Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const
  72. {
  73. if (!index.isValid())
  74. return 0;
  75. Qt::ItemFlags flags = Qt::ItemIsEnabled;
  76. if (index.column() == 0)
  77. flags |= Qt::ItemIsUserCheckable;
  78. return flags;
  79. }
  80. bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
  81. {
  82. const auto event = static_cast<Pica::DebugContext::Event>(index.row());
  83. switch (role) {
  84. case Qt::CheckStateRole:
  85. {
  86. if (index.column() != 0)
  87. return false;
  88. auto context = context_weak.lock();
  89. if (!context)
  90. return false;
  91. context->breakpoints[(int)event].enabled = value == Qt::Checked;
  92. QModelIndex changed_index = createIndex(index.row(), 0);
  93. emit dataChanged(changed_index, changed_index);
  94. return true;
  95. }
  96. }
  97. return false;
  98. }
  99. void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
  100. {
  101. auto context = context_weak.lock();
  102. if (!context)
  103. return;
  104. active_breakpoint = context->active_breakpoint;
  105. at_breakpoint = context->at_breakpoint;
  106. emit dataChanged(createIndex(static_cast<int>(event), 0),
  107. createIndex(static_cast<int>(event), 0));
  108. }
  109. void BreakPointModel::OnResumed()
  110. {
  111. auto context = context_weak.lock();
  112. if (!context)
  113. return;
  114. at_breakpoint = context->at_breakpoint;
  115. emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
  116. createIndex(static_cast<int>(active_breakpoint), 0));
  117. active_breakpoint = context->active_breakpoint;
  118. }
  119. GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
  120. QWidget* parent)
  121. : QDockWidget(tr("Pica Breakpoints"), parent),
  122. Pica::DebugContext::BreakPointObserver(debug_context)
  123. {
  124. setObjectName("PicaBreakPointsWidget");
  125. status_text = new QLabel(tr("Emulation running"));
  126. resume_button = new QPushButton(tr("Resume"));
  127. resume_button->setEnabled(false);
  128. breakpoint_model = new BreakPointModel(debug_context, this);
  129. breakpoint_list = new QTreeView;
  130. breakpoint_list->setRootIsDecorated(false);
  131. breakpoint_list->setHeaderHidden(true);
  132. breakpoint_list->setModel(breakpoint_model);
  133. qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
  134. connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)),
  135. this, SLOT(OnItemDoubleClicked(const QModelIndex&)));
  136. connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
  137. connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
  138. this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
  139. Qt::BlockingQueuedConnection);
  140. connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
  141. connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
  142. breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
  143. Qt::BlockingQueuedConnection);
  144. connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
  145. connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
  146. breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
  147. QWidget* main_widget = new QWidget;
  148. auto main_layout = new QVBoxLayout;
  149. {
  150. auto sub_layout = new QHBoxLayout;
  151. sub_layout->addWidget(status_text);
  152. sub_layout->addWidget(resume_button);
  153. main_layout->addLayout(sub_layout);
  154. }
  155. main_layout->addWidget(breakpoint_list);
  156. main_widget->setLayout(main_layout);
  157. setWidget(main_widget);
  158. }
  159. void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
  160. {
  161. // Process in GUI thread
  162. emit BreakPointHit(event, data);
  163. }
  164. void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
  165. {
  166. status_text->setText(tr("Emulation halted at breakpoint"));
  167. resume_button->setEnabled(true);
  168. }
  169. void GraphicsBreakPointsWidget::OnPicaResume()
  170. {
  171. // Process in GUI thread
  172. emit Resumed();
  173. }
  174. void GraphicsBreakPointsWidget::OnResumed()
  175. {
  176. status_text->setText(tr("Emulation running"));
  177. resume_button->setEnabled(false);
  178. }
  179. void GraphicsBreakPointsWidget::OnResumeRequested()
  180. {
  181. if (auto context = context_weak.lock())
  182. context->Resume();
  183. }
  184. void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index)
  185. {
  186. if (!index.isValid())
  187. return;
  188. QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0);
  189. QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole);
  190. QVariant new_state = Qt::Unchecked;
  191. if (enabled == Qt::Unchecked)
  192. new_state = Qt::Checked;
  193. breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole);
  194. }