4 changed files with 339 additions and 0 deletions
-
2src/citra_qt/CMakeLists.txt
-
253src/citra_qt/debugger/graphics_breakpoints.cpp
-
78src/citra_qt/debugger/graphics_breakpoints.hxx
-
6src/citra_qt/main.cpp
@ -0,0 +1,253 @@ |
|||||
|
// Copyright 2014 Citra Emulator Project
|
||||
|
// Licensed under GPLv2
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <QMetaType>
|
||||
|
#include <QPushButton>
|
||||
|
#include <QTreeWidget>
|
||||
|
#include <QVBoxLayout>
|
||||
|
#include <QLabel>
|
||||
|
|
||||
|
#include "graphics_breakpoints.hxx"
|
||||
|
|
||||
|
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) |
||||
|
: QAbstractListModel(parent), context_weak(debug_context), |
||||
|
at_breakpoint(debug_context->at_breakpoint), |
||||
|
active_breakpoint(debug_context->active_breakpoint) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
int BreakPointModel::columnCount(const QModelIndex& parent) const |
||||
|
{ |
||||
|
return 2; |
||||
|
} |
||||
|
|
||||
|
int BreakPointModel::rowCount(const QModelIndex& parent) const |
||||
|
{ |
||||
|
return static_cast<int>(Pica::DebugContext::Event::NumEvents); |
||||
|
} |
||||
|
|
||||
|
QVariant BreakPointModel::data(const QModelIndex& index, int role) const |
||||
|
{ |
||||
|
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); |
||||
|
|
||||
|
switch (role) { |
||||
|
case Qt::DisplayRole: |
||||
|
{ |
||||
|
if (index.column() == 0) { |
||||
|
std::map<Pica::DebugContext::Event, QString> map; |
||||
|
map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); |
||||
|
map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); |
||||
|
map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incomming primitive batch")}); |
||||
|
map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); |
||||
|
|
||||
|
_dbg_assert_(GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); |
||||
|
|
||||
|
return map[event]; |
||||
|
} else if (index.column() == 1) { |
||||
|
return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
case Qt::BackgroundRole: |
||||
|
{ |
||||
|
if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { |
||||
|
return QBrush(QColor(0xE0, 0xE0, 0x10)); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
case Role_IsEnabled: |
||||
|
{ |
||||
|
auto context = context_weak.lock(); |
||||
|
return context && context->breakpoints[event].enabled; |
||||
|
} |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
return QVariant(); |
||||
|
} |
||||
|
|
||||
|
QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const |
||||
|
{ |
||||
|
switch(role) { |
||||
|
case Qt::DisplayRole: |
||||
|
{ |
||||
|
if (section == 0) { |
||||
|
return tr("Event"); |
||||
|
} else if (section == 1) { |
||||
|
return tr("Status"); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return QVariant(); |
||||
|
} |
||||
|
|
||||
|
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) |
||||
|
{ |
||||
|
const auto event = static_cast<Pica::DebugContext::Event>(index.row()); |
||||
|
|
||||
|
switch (role) { |
||||
|
case Role_IsEnabled: |
||||
|
{ |
||||
|
auto context = context_weak.lock(); |
||||
|
if (!context) |
||||
|
return false; |
||||
|
|
||||
|
context->breakpoints[event].enabled = value.toBool(); |
||||
|
QModelIndex changed_index = createIndex(index.row(), 1); |
||||
|
emit dataChanged(changed_index, changed_index); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) |
||||
|
{ |
||||
|
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), |
||||
|
createIndex(static_cast<int>(event), 1)); |
||||
|
} |
||||
|
|
||||
|
void BreakPointModel::OnResumed() |
||||
|
{ |
||||
|
auto context = context_weak.lock(); |
||||
|
if (!context) |
||||
|
return; |
||||
|
|
||||
|
at_breakpoint = context->at_breakpoint; |
||||
|
emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), |
||||
|
createIndex(static_cast<int>(active_breakpoint), 1)); |
||||
|
active_breakpoint = context->active_breakpoint; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, |
||||
|
QWidget* parent) |
||||
|
: QDockWidget(tr("Pica Breakpoints"), parent), |
||||
|
Pica::DebugContext::BreakPointObserver(debug_context) |
||||
|
{ |
||||
|
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; |
||||
|
breakpoint_list->setModel(breakpoint_model); |
||||
|
|
||||
|
toggle_breakpoint_button = new QPushButton(tr("Enable")); |
||||
|
toggle_breakpoint_button->setEnabled(false); |
||||
|
|
||||
|
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); |
||||
|
|
||||
|
connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); |
||||
|
|
||||
|
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |
||||
|
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), |
||||
|
Qt::BlockingQueuedConnection); |
||||
|
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); |
||||
|
|
||||
|
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), |
||||
|
breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), |
||||
|
Qt::BlockingQueuedConnection); |
||||
|
connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); |
||||
|
|
||||
|
connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), |
||||
|
breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); |
||||
|
|
||||
|
connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
||||
|
this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); |
||||
|
|
||||
|
connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); |
||||
|
|
||||
|
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_layout->addWidget(toggle_breakpoint_button); |
||||
|
main_widget->setLayout(main_layout); |
||||
|
|
||||
|
setWidget(main_widget); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) |
||||
|
{ |
||||
|
// Process in GUI thread
|
||||
|
emit BreakPointHit(event, data); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) |
||||
|
{ |
||||
|
status_text->setText(tr("Emulation halted at breakpoint")); |
||||
|
resume_button->setEnabled(true); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnPicaResume() |
||||
|
{ |
||||
|
// Process in GUI thread
|
||||
|
emit Resumed(); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnResumed() |
||||
|
{ |
||||
|
status_text->setText(tr("Emulation running")); |
||||
|
resume_button->setEnabled(false); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnResumeRequested() |
||||
|
{ |
||||
|
if (auto context = context_weak.lock()) |
||||
|
context->Resume(); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) |
||||
|
{ |
||||
|
if (!index.isValid()) { |
||||
|
toggle_breakpoint_button->setEnabled(false); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
toggle_breakpoint_button->setEnabled(true); |
||||
|
UpdateToggleBreakpointButton(index); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() |
||||
|
{ |
||||
|
QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); |
||||
|
bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); |
||||
|
|
||||
|
breakpoint_model->setData(index, new_state, |
||||
|
BreakPointModel::Role_IsEnabled); |
||||
|
UpdateToggleBreakpointButton(index); |
||||
|
} |
||||
|
|
||||
|
void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) |
||||
|
{ |
||||
|
if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { |
||||
|
toggle_breakpoint_button->setText(tr("Disable")); |
||||
|
} else { |
||||
|
toggle_breakpoint_button->setText(tr("Enable")); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
// Copyright 2014 Citra Emulator Project
|
||||
|
// Licensed under GPLv2
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#pragma once
|
||||
|
|
||||
|
#include <memory>
|
||||
|
|
||||
|
#include <QAbstractListModel>
|
||||
|
#include <QDockWidget>
|
||||
|
|
||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||
|
|
||||
|
class QLabel; |
||||
|
class QPushButton; |
||||
|
class QTreeView; |
||||
|
|
||||
|
class BreakPointModel : public QAbstractListModel { |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
enum { |
||||
|
Role_IsEnabled = Qt::UserRole, |
||||
|
}; |
||||
|
|
||||
|
BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent); |
||||
|
|
||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override; |
||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |
||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; |
||||
|
|
||||
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); |
||||
|
|
||||
|
public slots: |
||||
|
void OnBreakPointHit(Pica::DebugContext::Event event); |
||||
|
void OnResumed(); |
||||
|
|
||||
|
private: |
||||
|
bool at_breakpoint; |
||||
|
Pica::DebugContext::Event active_breakpoint; |
||||
|
std::weak_ptr<Pica::DebugContext> context_weak; |
||||
|
}; |
||||
|
|
||||
|
class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { |
||||
|
Q_OBJECT |
||||
|
|
||||
|
using Event = Pica::DebugContext::Event; |
||||
|
|
||||
|
public: |
||||
|
GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, |
||||
|
QWidget* parent = nullptr); |
||||
|
|
||||
|
void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; |
||||
|
void OnPicaResume() override; |
||||
|
|
||||
|
public slots: |
||||
|
void OnBreakPointHit(Pica::DebugContext::Event event, void* data); |
||||
|
void OnResumeRequested(); |
||||
|
void OnResumed(); |
||||
|
void OnBreakpointSelectionChanged(const QModelIndex&); |
||||
|
void OnToggleBreakpointEnabled(); |
||||
|
|
||||
|
signals: |
||||
|
void Resumed(); |
||||
|
void BreakPointHit(Pica::DebugContext::Event event, void* data); |
||||
|
void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); |
||||
|
|
||||
|
private: |
||||
|
void UpdateToggleBreakpointButton(const QModelIndex& index); |
||||
|
|
||||
|
QLabel* status_text; |
||||
|
QPushButton* resume_button; |
||||
|
QPushButton* toggle_breakpoint_button; |
||||
|
|
||||
|
BreakPointModel* breakpoint_model; |
||||
|
QTreeView* breakpoint_list; |
||||
|
}; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue