diff options
Diffstat (limited to 'src/citra_qt/debugger')
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoints.cpp | 253 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoints.hxx | 78 | 
2 files changed, 331 insertions, 0 deletions
| diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp new file mode 100644 index 000000000..2f41d5f77 --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints.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")); +    } +} diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx new file mode 100644 index 000000000..edc41283e --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoints.hxx @@ -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; +}; | 
