2015-02-05 16:53:25 +00:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2017-02-20 02:35:04 +00:00
|
|
|
#include <QAction>
|
|
|
|
#include <QLayout>
|
2015-08-17 21:25:21 +00:00
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QString>
|
2020-08-17 21:31:58 +00:00
|
|
|
#include <QTimer>
|
2016-09-21 06:52:38 +00:00
|
|
|
#include "citra_qt/debugger/profiler.h"
|
2015-08-17 21:25:21 +00:00
|
|
|
#include "citra_qt/util/util.h"
|
2016-04-29 07:07:10 +00:00
|
|
|
#include "common/common_types.h"
|
2015-08-17 21:25:21 +00:00
|
|
|
#include "common/microprofile.h"
|
|
|
|
|
|
|
|
// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the
|
|
|
|
// non-Qt frontends don't need it (and don't implement the UI drawing hooks either).
|
2016-04-29 00:17:31 +00:00
|
|
|
#if MICROPROFILE_ENABLED
|
2015-08-17 21:25:21 +00:00
|
|
|
#define MICROPROFILEUI_IMPL 1
|
|
|
|
#include "common/microprofileui.h"
|
2016-04-29 00:17:31 +00:00
|
|
|
|
2015-08-17 21:25:21 +00:00
|
|
|
class MicroProfileWidget : public QWidget {
|
|
|
|
public:
|
2015-09-12 03:04:19 +00:00
|
|
|
MicroProfileWidget(QWidget* parent = nullptr);
|
2015-08-17 21:25:21 +00:00
|
|
|
|
|
|
|
protected:
|
2020-08-15 14:34:56 +00:00
|
|
|
void paintEvent(QPaintEvent* event) override;
|
|
|
|
void showEvent(QShowEvent* event) override;
|
|
|
|
void hideEvent(QHideEvent* event) override;
|
2015-08-17 21:25:21 +00:00
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void mouseMoveEvent(QMouseEvent* event) override;
|
|
|
|
void mousePressEvent(QMouseEvent* event) override;
|
|
|
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
|
|
|
void wheelEvent(QWheelEvent* event) override;
|
2015-08-17 21:25:21 +00:00
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void keyPressEvent(QKeyEvent* event) override;
|
|
|
|
void keyReleaseEvent(QKeyEvent* event) override;
|
2015-08-17 21:25:21 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
/// This timer is used to redraw the widget's contents continuously. To save resources, it only
|
|
|
|
/// runs while the widget is visible.
|
|
|
|
QTimer update_timer;
|
2016-06-19 06:37:09 +00:00
|
|
|
/// Scale the coordinate system appropriately when dpi != 96.
|
|
|
|
qreal x_scale = 1.0, y_scale = 1.0;
|
2015-08-17 21:25:21 +00:00
|
|
|
};
|
|
|
|
|
2016-04-29 00:17:31 +00:00
|
|
|
#endif
|
|
|
|
|
2016-09-18 00:38:01 +00:00
|
|
|
MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
|
2019-07-25 19:50:36 +00:00
|
|
|
setObjectName(QStringLiteral("MicroProfile"));
|
2015-08-17 21:25:21 +00:00
|
|
|
setWindowTitle(tr("MicroProfile"));
|
|
|
|
resize(1000, 600);
|
2023-01-28 16:08:15 +00:00
|
|
|
// Enable the maximize button
|
|
|
|
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
|
2015-08-17 21:25:21 +00:00
|
|
|
|
2016-04-29 00:17:31 +00:00
|
|
|
#if MICROPROFILE_ENABLED
|
|
|
|
|
2015-08-17 21:25:21 +00:00
|
|
|
MicroProfileWidget* widget = new MicroProfileWidget(this);
|
|
|
|
|
|
|
|
QLayout* layout = new QVBoxLayout(this);
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
layout->addWidget(widget);
|
|
|
|
setLayout(layout);
|
|
|
|
|
2016-09-18 00:38:01 +00:00
|
|
|
// Configure focus so that widget is focusable and the dialog automatically forwards focus to
|
|
|
|
// it.
|
2015-08-17 21:25:21 +00:00
|
|
|
setFocusProxy(widget);
|
|
|
|
widget->setFocusPolicy(Qt::StrongFocus);
|
|
|
|
widget->setFocus();
|
2016-04-29 00:17:31 +00:00
|
|
|
#endif
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QAction* MicroProfileDialog::toggleViewAction() {
|
|
|
|
if (toggle_view_action == nullptr) {
|
|
|
|
toggle_view_action = new QAction(windowTitle(), this);
|
|
|
|
toggle_view_action->setCheckable(true);
|
|
|
|
toggle_view_action->setChecked(isVisible());
|
2017-12-17 21:24:19 +00:00
|
|
|
connect(toggle_view_action, &QAction::toggled, this, &MicroProfileDialog::setVisible);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return toggle_view_action;
|
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileDialog::showEvent(QShowEvent* event) {
|
2015-08-17 21:25:21 +00:00
|
|
|
if (toggle_view_action) {
|
|
|
|
toggle_view_action->setChecked(isVisible());
|
|
|
|
}
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::showEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileDialog::hideEvent(QHideEvent* event) {
|
2015-08-17 21:25:21 +00:00
|
|
|
if (toggle_view_action) {
|
|
|
|
toggle_view_action->setChecked(isVisible());
|
|
|
|
}
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::hideEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 00:17:31 +00:00
|
|
|
#if MICROPROFILE_ENABLED
|
|
|
|
|
2015-08-17 21:25:21 +00:00
|
|
|
/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
|
|
|
|
/// QPainter available inside the drawing callbacks.
|
|
|
|
static QPainter* mp_painter = nullptr;
|
|
|
|
|
|
|
|
MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
|
|
|
|
// Send mouse motion events even when not dragging.
|
|
|
|
setMouseTracking(true);
|
|
|
|
|
|
|
|
MicroProfileSetDisplayMode(1); // Timers screen
|
|
|
|
MicroProfileInitUI();
|
|
|
|
|
2020-08-14 16:16:04 +00:00
|
|
|
connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::paintEvent([[maybe_unused]] QPaintEvent* event) {
|
2015-08-17 21:25:21 +00:00
|
|
|
QPainter painter(this);
|
|
|
|
|
2016-06-19 06:37:09 +00:00
|
|
|
// The units used by Microprofile for drawing are based in pixels on a 96 dpi display.
|
|
|
|
x_scale = qreal(painter.device()->logicalDpiX()) / 96.0;
|
|
|
|
y_scale = qreal(painter.device()->logicalDpiY()) / 96.0;
|
2016-05-12 20:49:02 +00:00
|
|
|
painter.scale(x_scale, y_scale);
|
|
|
|
|
2015-08-17 21:25:21 +00:00
|
|
|
painter.setBackground(Qt::black);
|
|
|
|
painter.eraseRect(rect());
|
|
|
|
|
|
|
|
QFont font = GetMonospaceFont();
|
|
|
|
font.setPixelSize(MICROPROFILE_TEXT_HEIGHT);
|
|
|
|
painter.setFont(font);
|
|
|
|
|
|
|
|
mp_painter = &painter;
|
2016-06-19 06:37:09 +00:00
|
|
|
MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale);
|
2015-08-17 21:25:21 +00:00
|
|
|
mp_painter = nullptr;
|
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::showEvent(QShowEvent* event) {
|
2015-08-17 21:25:21 +00:00
|
|
|
update_timer.start(15); // ~60 Hz
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::showEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::hideEvent(QHideEvent* event) {
|
2015-08-17 21:25:21 +00:00
|
|
|
update_timer.stop();
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::hideEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::mouseMoveEvent(QMouseEvent* event) {
|
|
|
|
MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0);
|
|
|
|
event->accept();
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::mousePressEvent(QMouseEvent* event) {
|
|
|
|
MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0);
|
|
|
|
MicroProfileMouseButton(event->buttons() & Qt::LeftButton, event->buttons() & Qt::RightButton);
|
|
|
|
event->accept();
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* event) {
|
|
|
|
MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0);
|
|
|
|
MicroProfileMouseButton(event->buttons() & Qt::LeftButton, event->buttons() & Qt::RightButton);
|
|
|
|
event->accept();
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::wheelEvent(QWheelEvent* event) {
|
2022-11-04 22:32:57 +00:00
|
|
|
MicroProfileMousePosition(event->position().x() / x_scale, event->position().y() / y_scale,
|
|
|
|
event->angleDelta().y() / 120);
|
2020-08-15 14:34:56 +00:00
|
|
|
event->accept();
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::keyPressEvent(QKeyEvent* event) {
|
|
|
|
if (event->key() == Qt::Key_Control) {
|
2015-08-17 21:25:21 +00:00
|
|
|
// Inform MicroProfile that the user is holding Ctrl.
|
|
|
|
MicroProfileModKey(1);
|
|
|
|
}
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::keyPressEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:34:56 +00:00
|
|
|
void MicroProfileWidget::keyReleaseEvent(QKeyEvent* event) {
|
|
|
|
if (event->key() == Qt::Key_Control) {
|
2015-08-17 21:25:21 +00:00
|
|
|
MicroProfileModKey(0);
|
|
|
|
}
|
2020-08-15 14:34:56 +00:00
|
|
|
QWidget::keyReleaseEvent(event);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// These functions are called by MicroProfileDraw to draw the interface elements on the screen.
|
|
|
|
|
|
|
|
void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 text_length) {
|
|
|
|
// hex_color does not include an alpha, so it must be assumed to be 255
|
|
|
|
mp_painter->setPen(QColor::fromRgb(hex_color));
|
|
|
|
|
|
|
|
// It's impossible to draw a string using a monospaced font with a fixed width per cell in a
|
|
|
|
// way that's reliable across different platforms and fonts as far as I (yuriks) can tell, so
|
|
|
|
// draw each character individually in order to precisely control the text advance.
|
|
|
|
for (u32 i = 0; i < text_length; ++i) {
|
|
|
|
// Position the text baseline 1 pixel above the bottom of the text cell, this gives nice
|
|
|
|
// vertical alignment of text for a wide range of tested fonts.
|
2019-07-25 19:50:36 +00:00
|
|
|
mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QString{QLatin1Char{text[i]}});
|
2015-08-17 21:25:21 +00:00
|
|
|
x += MICROPROFILE_TEXT_WIDTH + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 00:38:01 +00:00
|
|
|
void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
|
|
|
|
MicroProfileBoxType type) {
|
2015-08-17 21:25:21 +00:00
|
|
|
QColor color = QColor::fromRgba(hex_color);
|
|
|
|
QBrush brush = color;
|
|
|
|
if (type == MicroProfileBoxTypeBar) {
|
|
|
|
QLinearGradient gradient(left, top, left, bottom);
|
|
|
|
gradient.setColorAt(0.f, color.lighter(125));
|
|
|
|
gradient.setColorAt(1.f, color.darker(125));
|
|
|
|
brush = gradient;
|
|
|
|
}
|
|
|
|
mp_painter->fillRect(left, top, right - left, bottom - top, brush);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) {
|
|
|
|
// Temporary vector used to convert between the float array and QPointF. Marked static to reuse
|
|
|
|
// the allocation across calls.
|
|
|
|
static std::vector<QPointF> point_buf;
|
|
|
|
|
|
|
|
for (u32 i = 0; i < vertices_length; ++i) {
|
2016-09-18 00:38:01 +00:00
|
|
|
point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
|
2015-08-17 21:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// hex_color does not include an alpha, so it must be assumed to be 255
|
|
|
|
mp_painter->setPen(QColor::fromRgb(hex_color));
|
|
|
|
mp_painter->drawPolyline(point_buf.data(), vertices_length);
|
|
|
|
point_buf.clear();
|
|
|
|
}
|
2016-04-29 00:17:31 +00:00
|
|
|
#endif
|