citra-qt: Rewrite register widget

This commit is contained in:
Kingcom 2015-01-13 11:24:22 +01:00
parent a46371329d
commit 868e6f2d82
8 changed files with 352 additions and 67 deletions

View File

@ -13,6 +13,8 @@ set(SRCS
debugger/graphics_framebuffer.cpp
debugger/ramview.cpp
debugger/registers.cpp
debugger/register_view.cpp
debugger/util.cpp
util/spinbox.cpp
bootmanager.cpp
hotkeys.cpp
@ -33,6 +35,8 @@ set(HEADERS
debugger/graphics_framebuffer.h
debugger/ramview.h
debugger/registers.h
debugger/register_view.h
debugger/util.h
util/spinbox.h
bootmanager.h
hotkeys.h

View File

@ -0,0 +1,168 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QPainter>
#include <QMouseEvent>
#include <QShortcut>
#include "core/debugger/debug_interface.h"
#include "register_view.h"
RegisterView::RegisterView(QWidget* parent) : QFrame(parent) {
// initialize registers
for (int cat = 0; cat < 2; cat++) {
for (int index = 0; index < g_debug->GetNumRegsInCategory(cat); index++) {
Register reg;
reg.category = cat;
reg.index = index;
reg.oldValue = 0;
reg.changed = false;
registers.append(reg);
}
}
initializeSize();
last_pc = 0;
selection = 0;
QShortcut* up_shortcut = new QShortcut(QKeySequence(Qt::Key_Up), this);
up_shortcut->setContext(Qt::WidgetShortcut);
connect(up_shortcut, SIGNAL(activated()), this, SLOT(previousRegister()));
QShortcut* down_shortcut = new QShortcut(QKeySequence(Qt::Key_Down), this);
down_shortcut->setContext(Qt::WidgetShortcut);
connect(down_shortcut, SIGNAL(activated()), this, SLOT(nextRegister()));
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
}
void RegisterView::initializeSize() {
// calculate max register name length
int max_length = 0;
for (int i = 0; i < registers.size(); i++) {
const char* name = g_debug->GetRegName(registers[i].category,registers[i].index);
max_length = qMax<int>(max_length, strlen(name));
}
// initialize positions
border_gap = 3;
value_offset = border_gap + (max_length + 1) * font.charWidth();
// characters per line = length of register name + space + length of register value
int numChars = max_length + 1 + 8;
// calculate size of widget. borderGap pixels to all sides,
// and as many rows and columns as needed
QSize size = QSize(2 * border_gap + numChars*font.charWidth(),
2 * border_gap + registers.size()*font.charHeight());
// make sure the widget has exactly that size
setMinimumSize(size);
setMaximumSize(size);
}
void RegisterView::refreshChangedRegs() {
// the registers need to be refreshed if the PC of the core has
// changed since the last call
if (g_debug->GetPC() == last_pc)
return;
for (Register& reg: registers) {
unsigned int value = g_debug->GetRegValue(reg.category,reg.index);
reg.changed = (value != reg.oldValue);
reg.oldValue = value;
}
last_pc = g_debug->GetPC();
}
void RegisterView::paintEvent(QPaintEvent* event) {
QPainter p(this);
refreshChangedRegs();
// clear background
p.setBrush(QBrush(QColor(Qt::white)));
p.setPen(QPen(QColor(Qt::white)));
p.drawRect(0, 0, width(), height());
p.setFont(font.font());
QPoint pos;
QString value;
for (int i = 0; i < registers.size(); i++) {
Register& reg = registers[i];
int y = border_gap + i * font.charHeight();
// highlight background of selected register
if (i == selection)
{
QColor col(0xE8, 0xEF, 0xFF);
p.setBrush(col);
p.setPen(col);
p.drawRect(0, y - 1, width(), font.charHeight() - 1);
}
// draw register name
pos = font.calculateDrawPos(border_gap, y);
p.setPen(QColor(0, 0, 0x60));
p.drawText(pos, g_debug->GetRegName(reg.category, reg.index));
// draw register value
pos = font.calculateDrawPos(value_offset, y);
value.sprintf("%08X", g_debug->GetRegValue(reg.category, reg.index));
// draw changed registers in red, rest in black
if (reg.changed)
p.setPen(QColor(Qt::red));
else
p.setPen(QColor(Qt::black));
p.drawText(pos, value);
}
// draw frame last, otherwise it gets overwritten
QFrame::paintEvent(event);
}
int RegisterView::mapPositionToRegister(QPoint pos) const {
int y = pos.y();
if (y < border_gap || y + border_gap >= height())
return -1;
return (y - border_gap) / font.charHeight();
}
void RegisterView::mousePressEvent(QMouseEvent* event) {
int new_selection = mapPositionToRegister(event->pos());
if (new_selection != -1 && selection != new_selection) {
selection = new_selection;
update();
}
}
void RegisterView::mouseMoveEvent(QMouseEvent* event) {
if (event->buttons() & (Qt::MouseButton::LeftButton | Qt::MouseButton::RightButton)) {
int new_selection = mapPositionToRegister(event->pos());
if (new_selection != -1 && selection != new_selection)
{
selection = new_selection;
update();
}
}
}
void RegisterView::moveSelectionUp() {
selection = qMax<int>(0, selection - 1);
update();
}
void RegisterView::moveSelectionDown() {
selection = qMin<int>(registers.size() - 1, selection + 1);
update();
}

View File

@ -0,0 +1,52 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QFrame>
#include "util.h"
class RegisterView: public QFrame {
Q_OBJECT
public:
RegisterView(QWidget* parent = NULL);
protected:
void paintEvent(QPaintEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private slots:
// Select previous register
void moveSelectionUp();
// Select next register
void moveSelectionDown();
private:
// Reloads register values if necessary
void refreshChangedRegs();
// Calculates and sets the widget size and the used positions
void initializeSize();
// Returns the number of the register at the given position
int mapPositionToRegister(QPoint pos) const;
struct Register
{
unsigned int oldValue;
bool changed;
int category;
int index;
};
DebuggerFont font;
int border_gap;
int value_offset;
unsigned int last_pc;
QVector<Register> registers;
int selection;
};

View File

@ -2,68 +2,53 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "registers.h"
#include <QSpacerItem>
#include "core/core.h"
#include "core/arm/arm_interface.h"
#include "core/debugger/debug_interface.h"
#include "registers.h"
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
{
cpu_regs_ui.setupUi(this);
tree = cpu_regs_ui.treeWidget;
tree->addTopLevelItem(registers = new QTreeWidgetItem(QStringList("Registers")));
tree->addTopLevelItem(CSPR = new QTreeWidgetItem(QStringList("CSPR")));
registers->setExpanded(true);
CSPR->setExpanded(true);
for (int i = 0; i < 16; ++i)
for (int i = 0; i < g_debug->GetNumRegsInCategory(REGCAT_CPSR_FLAGS); i++)
{
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0'))));
registers->addChild(child);
const char* name = g_debug->GetRegName(REGCAT_CPSR_FLAGS,i);
QCheckBox* box = new QCheckBox(name,this);
cpu_regs_ui.flags->addWidget(box);
connect(box,SIGNAL(stateChanged(int)),this,SLOT(OnFlagToggled(int)));
flagLookup[box] = i;
}
CSPR->addChild(new QTreeWidgetItem(QStringList("M")));
CSPR->addChild(new QTreeWidgetItem(QStringList("T")));
CSPR->addChild(new QTreeWidgetItem(QStringList("F")));
CSPR->addChild(new QTreeWidgetItem(QStringList("I")));
CSPR->addChild(new QTreeWidgetItem(QStringList("A")));
CSPR->addChild(new QTreeWidgetItem(QStringList("E")));
CSPR->addChild(new QTreeWidgetItem(QStringList("IT")));
CSPR->addChild(new QTreeWidgetItem(QStringList("GE")));
CSPR->addChild(new QTreeWidgetItem(QStringList("DNM")));
CSPR->addChild(new QTreeWidgetItem(QStringList("J")));
CSPR->addChild(new QTreeWidgetItem(QStringList("Q")));
CSPR->addChild(new QTreeWidgetItem(QStringList("V")));
CSPR->addChild(new QTreeWidgetItem(QStringList("C")));
CSPR->addChild(new QTreeWidgetItem(QStringList("Z")));
CSPR->addChild(new QTreeWidgetItem(QStringList("N")));
cpu_regs_ui.flags->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
}
void RegistersWidget::OnFlagToggled(int state)
{
QCheckBox* sender = dynamic_cast<QCheckBox*>(this->sender());
int index = flagLookup[sender];
g_debug->SetRegValue(REGCAT_CPSR_FLAGS,index,state == 0 ? 0 : 1);
cpu_regs_ui.registerView->update();
}
void RegistersWidget::OnDebugModeEntered()
{
ARM_Interface* app_core = Core::g_app_core;
for (auto it = flagLookup.begin(); it != flagLookup.end(); it++)
{
QCheckBox* box = it.key();
int index = it.value();
for (int i = 0; i < 16; ++i)
registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
bool check = g_debug->GetRegValue(REGCAT_CPSR_FLAGS,index) != 0;
box->blockSignals(true);
box->setChecked(check);
box->blockSignals(false);
}
CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0')));
CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State
CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable
CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable
CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort
CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess
CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM)
CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal
CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify
CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state
CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow
CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow
CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend
CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
cpu_regs_ui.registerView->update();
}
void RegistersWidget::OnDebugModeLeft()

View File

@ -6,9 +6,12 @@
#include <QDockWidget>
#include <QTreeWidgetItem>
#include <QMap>
#include <QCheckBox>
class QTreeWidget;
class RegistersWidget : public QDockWidget
{
Q_OBJECT
@ -19,12 +22,9 @@ public:
public slots:
void OnDebugModeEntered();
void OnDebugModeLeft();
void OnFlagToggled(int state);
private:
QMap<QCheckBox*,int> flagLookup;
Ui::ARMRegisters cpu_regs_ui;
QTreeWidget* tree;
QTreeWidgetItem* registers;
QTreeWidgetItem* CSPR;
};

View File

@ -14,27 +14,41 @@
<string>ARM Registers</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="RegisterView" name="registerView">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>3</number>
</property>
<column>
<property name="text">
<string>Register</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="flags">
<property name="spacing">
<number>2</number>
</property>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RegisterView</class>
<extends>QFrame</extends>
<header>debugger/register_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,29 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QFontMetrics>
#include "util.h"
DebuggerFont::DebuggerFont() {
font_ = QFont("Lucida Console", 10);
font_.setStyleHint(QFont::Courier, QFont::PreferMatch);
updateMetrics();
}
void DebuggerFont::setBold(bool bold) {
font_.setBold(bold);
updateMetrics();
}
QPoint DebuggerFont::calculateDrawPos(int left, int top) {
return QPoint(left, top + charHeight() - charDescent());
}
void DebuggerFont::updateMetrics() {
QFontMetrics metrics = QFontMetrics(font_);
width = metrics.width('0');
height = metrics.height();
descent = metrics.descent();
}

View File

@ -0,0 +1,33 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QFont>
#include <QPoint>
// Abstracts font calculations used by some
// debugger widgets
class DebuggerFont {
public:
DebuggerFont();
// Returns the position needed to draw a string that should
// have its upper left corner at the given coordinates
QPoint calculateDrawPos(int left, int top);
QFont& font() { return font_; }
int charWidth() const { return width; };
int charHeight() const { return height; };
int charDescent() const { return descent; };
void setBold(bool bold);
private:
void updateMetrics();
QFont font_;
int width;
int height;
int descent;
};