graphics_breakpoints.cpp 7.7 KB

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