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