mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
Merge branch 'feat-fix/controller-overlay-std-badalloc' into 'main'
feat(ui)/(crash): Implement real-time Controller Overlay & Resolve std::bad_alloc See merge request citron/emulator!122
This commit is contained in:
@@ -36,6 +36,8 @@ add_executable(citron
|
|||||||
applets/qt_web_browser_scripts.h
|
applets/qt_web_browser_scripts.h
|
||||||
bootmanager.cpp
|
bootmanager.cpp
|
||||||
bootmanager.h
|
bootmanager.h
|
||||||
|
controller_overlay.cpp
|
||||||
|
controller_overlay.h
|
||||||
compatdb.ui
|
compatdb.ui
|
||||||
compatibility_list.cpp
|
compatibility_list.cpp
|
||||||
compatibility_list.h
|
compatibility_list.h
|
||||||
@@ -229,7 +231,6 @@ add_executable(citron
|
|||||||
util/vram_overlay.h
|
util/vram_overlay.h
|
||||||
util/sequence_dialog/sequence_dialog.cpp
|
util/sequence_dialog/sequence_dialog.cpp
|
||||||
util/sequence_dialog/sequence_dialog.h
|
util/sequence_dialog/sequence_dialog.h
|
||||||
util/title_ids.h
|
|
||||||
util/url_request_interceptor.cpp
|
util/url_request_interceptor.cpp
|
||||||
util/url_request_interceptor.h
|
util/url_request_interceptor.h
|
||||||
util/util.cpp
|
util/util.cpp
|
||||||
@@ -242,6 +243,8 @@ add_executable(citron
|
|||||||
citron.rc
|
citron.rc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(controller_overlay.cpp PROPERTIES SKIP_AUTOUIC ON)
|
||||||
|
|
||||||
if (CITRON_CRASH_DUMPS)
|
if (CITRON_CRASH_DUMPS)
|
||||||
target_sources(citron PRIVATE
|
target_sources(citron PRIVATE
|
||||||
breakpad.cpp
|
breakpad.cpp
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -22,6 +23,10 @@ PlayerControlPreview::~PlayerControlPreview() {
|
|||||||
UnloadController();
|
UnloadController();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void PlayerControlPreview::SetRawJoystickVisible(bool visible) {
|
||||||
|
raw_joystick_visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) {
|
void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) {
|
||||||
UnloadController();
|
UnloadController();
|
||||||
is_controller_set = true;
|
is_controller_set = true;
|
||||||
@@ -226,29 +231,50 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
|
|||||||
QFrame::paintEvent(event);
|
QFrame::paintEvent(event);
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
// Define the base size that the original drawing coordinates were designed for.
|
||||||
|
// A Pro Controller is roughly 420x320 pixels in its drawing function.
|
||||||
|
constexpr QSizeF base_size(450.0, 350.0);
|
||||||
|
|
||||||
|
// Get the current size of the widget.
|
||||||
|
const QSize current_size = this->size();
|
||||||
|
|
||||||
|
// Calculate the scaling factor. We want to maintain aspect ratio,
|
||||||
|
// so we use the smaller of the width/height scaling factors.
|
||||||
|
const double scale_x = current_size.width() / base_size.width();
|
||||||
|
const double scale_y = current_size.height() / base_size.height();
|
||||||
|
const double scale = std::min(scale_x, scale_y);
|
||||||
|
|
||||||
|
// Save the painter's state, apply the scaling, and center the drawing.
|
||||||
|
p.save();
|
||||||
const QPointF center = rect().center();
|
const QPointF center = rect().center();
|
||||||
|
p.translate(center);
|
||||||
|
p.scale(scale, scale);
|
||||||
|
p.translate(-center);
|
||||||
|
|
||||||
switch (controller_type) {
|
switch (controller_type) {
|
||||||
case Core::HID::NpadStyleIndex::Handheld:
|
case Core::HID::NpadStyleIndex::Handheld:
|
||||||
DrawHandheldController(p, center);
|
DrawHandheldController(p, center);
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||||
DrawDualController(p, center);
|
DrawDualController(p, center);
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||||
DrawLeftController(p, center);
|
DrawLeftController(p, center);
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||||
DrawRightController(p, center);
|
DrawRightController(p, center);
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::GameCube:
|
case Core::HID::NpadStyleIndex::GameCube:
|
||||||
DrawGCController(p, center);
|
DrawGCController(p, center);
|
||||||
break;
|
break;
|
||||||
case Core::HID::NpadStyleIndex::Fullkey:
|
case Core::HID::NpadStyleIndex::Fullkey:
|
||||||
default:
|
default:
|
||||||
DrawProController(p, center);
|
DrawProController(p, center);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.restore(); // Restore the painter's original state.
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) {
|
void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) {
|
||||||
@@ -314,7 +340,9 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
|
|||||||
center + QPointF(9, -69) +
|
center + QPointF(9, -69) +
|
||||||
(QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8),
|
(QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8),
|
||||||
1.8f, button_values[Settings::NativeButton::LStick]);
|
1.8f, button_values[Settings::NativeButton::LStick]);
|
||||||
DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -449,7 +477,9 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
|
|||||||
center + QPointF(-9, 11) +
|
center + QPointF(-9, 11) +
|
||||||
(QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8),
|
(QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8),
|
||||||
1.8f, button_values[Settings::NativeButton::RStick]);
|
1.8f, button_values[Settings::NativeButton::RStick]);
|
||||||
DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -593,7 +623,9 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
|
|||||||
|
|
||||||
DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button);
|
DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button);
|
||||||
DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button);
|
DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button);
|
||||||
DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -696,7 +728,9 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
|
|||||||
|
|
||||||
DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
|
DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
|
||||||
DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);
|
DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);
|
||||||
DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -808,7 +842,9 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
|
|||||||
button_values[Settings::NativeButton::LStick]);
|
button_values[Settings::NativeButton::LStick]);
|
||||||
DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11,
|
DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11,
|
||||||
button_values[Settings::NativeButton::RStick]);
|
button_values[Settings::NativeButton::RStick]);
|
||||||
DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -910,7 +946,9 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
|
|||||||
p.setPen(colors.transparent);
|
p.setPen(colors.transparent);
|
||||||
p.setBrush(colors.font);
|
p.setBrush(colors.font);
|
||||||
DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f);
|
DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f);
|
||||||
DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
|
if (raw_joystick_visible) {
|
||||||
|
DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Settings::NativeButton;
|
using namespace Settings::NativeButton;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -29,6 +30,8 @@ public:
|
|||||||
// Sets the emulated controller to be displayed
|
// Sets the emulated controller to be displayed
|
||||||
void SetController(Core::HID::EmulatedController* controller);
|
void SetController(Core::HID::EmulatedController* controller);
|
||||||
|
|
||||||
|
void SetRawJoystickVisible(bool visible);
|
||||||
|
|
||||||
// Disables events from the emulated controller
|
// Disables events from the emulated controller
|
||||||
void UnloadController();
|
void UnloadController();
|
||||||
|
|
||||||
@@ -207,6 +210,8 @@ private:
|
|||||||
void SetTextFont(QPainter& p, float text_size,
|
void SetTextFont(QPainter& p, float text_size,
|
||||||
const QString& font_family = QStringLiteral("sans-serif"));
|
const QString& font_family = QStringLiteral("sans-serif"));
|
||||||
|
|
||||||
|
bool raw_joystick_visible = false;
|
||||||
|
|
||||||
bool is_controller_set{};
|
bool is_controller_set{};
|
||||||
bool is_connected{};
|
bool is_connected{};
|
||||||
bool needs_redraw{};
|
bool needs_redraw{};
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
|
|||||||
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
||||||
Core::System& system_)
|
Core::System& system_)
|
||||||
: QDialog(parent),
|
: QDialog(parent),
|
||||||
ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, file_name{file_name}, system{system_},
|
ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, file_name{file_name_}, system{system_},
|
||||||
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
||||||
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} ,
|
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} ,
|
||||||
rainbow_timer{new QTimer(this)} {
|
rainbow_timer{new QTimer(this)} {
|
||||||
|
|||||||
123
src/citron/controller_overlay.cpp
Normal file
123
src/citron/controller_overlay.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "citron/controller_overlay.h"
|
||||||
|
#include "citron/configuration/configure_input_player_widget.h"
|
||||||
|
#include "citron/main.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "hid_core/hid_core.h"
|
||||||
|
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <QSizeGrip>
|
||||||
|
#include <QWindow> // Required for Wayland dragging
|
||||||
|
#include <QResizeEvent>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Helper to get the active controller for Player 1
|
||||||
|
Core::HID::EmulatedController* GetPlayer1Controller(Core::System* system) {
|
||||||
|
if (!system) return nullptr;
|
||||||
|
Core::HID::HIDCore& hid_core = system->HIDCore();
|
||||||
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
|
if (handheld && handheld->IsConnected()) {
|
||||||
|
return handheld;
|
||||||
|
}
|
||||||
|
return hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerOverlay::ControllerOverlay(GMainWindow* parent)
|
||||||
|
: QWidget(parent, Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
|
||||||
|
main_window(parent) {
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
|
||||||
|
auto* layout = new QGridLayout(this);
|
||||||
|
setLayout(layout);
|
||||||
|
// Set margins to 0 so the controller can go right to the edge of the resizable window
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// Create the widget that draws the controller and make it transparent
|
||||||
|
controller_widget = new PlayerControlPreview(this);
|
||||||
|
controller_widget->setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
|
||||||
|
// Disable the raw joystick (deadzone) visualization
|
||||||
|
controller_widget->SetRawJoystickVisible(false);
|
||||||
|
|
||||||
|
// Allow the widget to expand and shrink with the window
|
||||||
|
controller_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
layout->addWidget(controller_widget, 0, 0);
|
||||||
|
|
||||||
|
// Add a size grip for resizing
|
||||||
|
size_grip = new QSizeGrip(this);
|
||||||
|
layout->addWidget(size_grip, 0, 0, Qt::AlignBottom | Qt::AlignRight);
|
||||||
|
|
||||||
|
// Start the timer for continuous updates
|
||||||
|
connect(&update_timer, &QTimer::timeout, this, &ControllerOverlay::UpdateControllerState);
|
||||||
|
update_timer.start(16); // ~60 FPS
|
||||||
|
|
||||||
|
// Set a minimum size and a default starting size
|
||||||
|
setMinimumSize(225, 175);
|
||||||
|
resize(450, 350);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerOverlay::~ControllerOverlay() = default;
|
||||||
|
|
||||||
|
void ControllerOverlay::UpdateControllerState() {
|
||||||
|
Core::System* system = main_window->GetSystem();
|
||||||
|
Core::HID::EmulatedController* controller = GetPlayer1Controller(system);
|
||||||
|
if (controller_widget && controller) {
|
||||||
|
controller_widget->SetController(controller);
|
||||||
|
controller_widget->UpdateInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The paint event is now empty, which makes the background fully transparent.
|
||||||
|
void ControllerOverlay::paintEvent(QPaintEvent* event) {
|
||||||
|
Q_UNUSED(event);
|
||||||
|
// Intentionally left blank to achieve a fully transparent window background.
|
||||||
|
}
|
||||||
|
|
||||||
|
// These functions handle dragging the frameless window
|
||||||
|
void ControllerOverlay::mousePressEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton && !size_grip->geometry().contains(event->pos())) {
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
// Use system move on Wayland/Linux for proper dragging
|
||||||
|
if (windowHandle()) {
|
||||||
|
windowHandle()->startSystemMove();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Original dragging implementation for other platforms (Windows, etc.)
|
||||||
|
is_dragging = true;
|
||||||
|
drag_start_pos = event->globalPosition().toPoint() - this->pos();
|
||||||
|
#endif
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerOverlay::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
#if !defined(Q_OS_LINUX)
|
||||||
|
if (is_dragging) {
|
||||||
|
move(event->globalPosition().toPoint() - drag_start_pos);
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// On Linux, the window manager handles the move, so we do nothing here.
|
||||||
|
Q_UNUSED(event);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerOverlay::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
is_dragging = false;
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerOverlay::resizeEvent(QResizeEvent* event) {
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
// This ensures the layout and its widgets (like the size grip) are correctly repositioned on resize.
|
||||||
|
layout()->update();
|
||||||
|
}
|
||||||
38
src/citron/controller_overlay.h
Normal file
38
src/citron/controller_overlay.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class GMainWindow;
|
||||||
|
class PlayerControlPreview;
|
||||||
|
class QSizeGrip;
|
||||||
|
|
||||||
|
class ControllerOverlay : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ControllerOverlay(GMainWindow* parent);
|
||||||
|
~ControllerOverlay() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent* event) override;
|
||||||
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void UpdateControllerState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
GMainWindow* main_window;
|
||||||
|
QTimer update_timer;
|
||||||
|
|
||||||
|
PlayerControlPreview* controller_widget;
|
||||||
|
QSizeGrip* size_grip;
|
||||||
|
|
||||||
|
bool is_dragging = false;
|
||||||
|
QPoint drag_start_pos;
|
||||||
|
};
|
||||||
@@ -156,6 +156,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#include "citron/about_dialog.h"
|
#include "citron/about_dialog.h"
|
||||||
#include "citron/bootmanager.h"
|
#include "citron/bootmanager.h"
|
||||||
#include "citron/compatdb.h"
|
#include "citron/compatdb.h"
|
||||||
|
#include "citron/controller_overlay.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "citron/compatibility_list.h"
|
#include "citron/compatibility_list.h"
|
||||||
#include "citron/configuration/configure_dialog.h"
|
#include "citron/configuration/configure_dialog.h"
|
||||||
#include "citron/configuration/configure_input_per_game.h"
|
#include "citron/configuration/configure_input_per_game.h"
|
||||||
@@ -181,7 +183,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#include "citron/util/clickable_label.h"
|
#include "citron/util/clickable_label.h"
|
||||||
#include "citron/util/performance_overlay.h"
|
#include "citron/util/performance_overlay.h"
|
||||||
#include "citron/util/multiplayer_room_overlay.h"
|
#include "citron/util/multiplayer_room_overlay.h"
|
||||||
#include "citron/util/title_ids.h"
|
|
||||||
#include "citron/util/vram_overlay.h"
|
#include "citron/util/vram_overlay.h"
|
||||||
#include "citron/vk_device_info.h"
|
#include "citron/vk_device_info.h"
|
||||||
|
|
||||||
@@ -1668,6 +1669,9 @@ void GMainWindow::ConnectMenuEvents() {
|
|||||||
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
||||||
connect_menu(ui->action_Check_For_Updates, &GMainWindow::OnCheckForUpdates);
|
connect_menu(ui->action_Check_For_Updates, &GMainWindow::OnCheckForUpdates);
|
||||||
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
||||||
|
|
||||||
|
connect(ui->actionControllerOverlay, &QAction::triggered, this, &GMainWindow::OnToggleControllerOverlay);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateMenuState() {
|
void GMainWindow::UpdateMenuState() {
|
||||||
@@ -2020,8 +2024,7 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
system->ApplySettings();
|
system->ApplySettings();
|
||||||
|
|
||||||
// Final Fantasy Tactics requires single-core mode to boot properly
|
// Final Fantasy Tactics requires single-core mode to boot properly
|
||||||
if (title_id == UICommon::TitleID::FinalFantasyTactics) {
|
if (title_id == 0x010038B015560000ULL) {
|
||||||
LOG_INFO(Frontend, "Applying workaround: forcing single-core mode for Final Fantasy Tactics");
|
|
||||||
Settings::values.use_multi_core.SetValue(false);
|
Settings::values.use_multi_core.SetValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4924,6 +4927,16 @@ void GMainWindow::OnAbout() {
|
|||||||
aboutDialog.exec();
|
aboutDialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnToggleControllerOverlay() {
|
||||||
|
const bool visible = ui->actionControllerOverlay->isChecked();
|
||||||
|
if (visible && !controller_overlay) {
|
||||||
|
controller_overlay = new ControllerOverlay(this);
|
||||||
|
}
|
||||||
|
if (controller_overlay) {
|
||||||
|
controller_overlay->setVisible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnToggleFilterBar() {
|
void GMainWindow::OnToggleFilterBar() {
|
||||||
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
||||||
if (ui->action_Show_Filter_Bar->isChecked()) {
|
if (ui->action_Show_Filter_Bar->isChecked()) {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class OverlayDialog;
|
|||||||
class PerformanceOverlay;
|
class PerformanceOverlay;
|
||||||
class MultiplayerRoomOverlay;
|
class MultiplayerRoomOverlay;
|
||||||
class VramOverlay;
|
class VramOverlay;
|
||||||
|
class ControllerOverlay;
|
||||||
class ProfilerWidget;
|
class ProfilerWidget;
|
||||||
class ControllerDialog;
|
class ControllerDialog;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
@@ -109,6 +110,7 @@ public:
|
|||||||
bool DropAction(QDropEvent* event);
|
bool DropAction(QDropEvent* event);
|
||||||
void AcceptDropEvent(QDropEvent* event);
|
void AcceptDropEvent(QDropEvent* event);
|
||||||
MultiplayerState* GetMultiplayerState() { return multiplayer_state; }
|
MultiplayerState* GetMultiplayerState() { return multiplayer_state; }
|
||||||
|
Core::System* GetSystem() { return system.get(); }
|
||||||
bool IsEmulationRunning() const { return emulation_running; }
|
bool IsEmulationRunning() const { return emulation_running; }
|
||||||
signals:
|
signals:
|
||||||
void EmulationStarting(EmuThread* emu_thread);
|
void EmulationStarting(EmuThread* emu_thread);
|
||||||
@@ -249,6 +251,7 @@ private slots:
|
|||||||
void OnTogglePerformanceOverlay();
|
void OnTogglePerformanceOverlay();
|
||||||
void OnToggleMultiplayerRoomOverlay();
|
void OnToggleMultiplayerRoomOverlay();
|
||||||
void OnToggleVramOverlay();
|
void OnToggleVramOverlay();
|
||||||
|
void OnToggleControllerOverlay();
|
||||||
void OnDisplayTitleBars(bool);
|
void OnDisplayTitleBars(bool);
|
||||||
double GetCurrentFPS() const;
|
double GetCurrentFPS() const;
|
||||||
double GetCurrentFrameTime() const;
|
double GetCurrentFrameTime() const;
|
||||||
@@ -335,6 +338,7 @@ private:
|
|||||||
PerformanceOverlay* performance_overlay{};
|
PerformanceOverlay* performance_overlay{};
|
||||||
MultiplayerRoomOverlay* multiplayer_room_overlay{};
|
MultiplayerRoomOverlay* multiplayer_room_overlay{};
|
||||||
VramOverlay* vram_overlay{};
|
VramOverlay* vram_overlay{};
|
||||||
|
ControllerOverlay* controller_overlay{};
|
||||||
GameListPlaceholder* game_list_placeholder;
|
GameListPlaceholder* game_list_placeholder;
|
||||||
std::vector<VkDeviceInfo::Record> vk_device_records;
|
std::vector<VkDeviceInfo::Record> vk_device_records;
|
||||||
QLabel* message_label = nullptr;
|
QLabel* message_label = nullptr;
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
<addaction name="action_Show_Performance_Overlay"/>
|
<addaction name="action_Show_Performance_Overlay"/>
|
||||||
<addaction name="action_Show_Vram_Overlay"/>
|
<addaction name="action_Show_Vram_Overlay"/>
|
||||||
<addaction name="action_Show_Multiplayer_Room_Overlay"/>
|
<addaction name="action_Show_Multiplayer_Room_Overlay"/>
|
||||||
|
<addaction name="actionControllerOverlay"/>
|
||||||
<addaction name="action_Toggle_Grid_View"/>
|
<addaction name="action_Toggle_Grid_View"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_Reset_Window_Size"/>
|
<addaction name="menu_Reset_Window_Size"/>
|
||||||
@@ -347,6 +348,14 @@
|
|||||||
<string>Show Multiplayer Room Overlay</string>
|
<string>Show Multiplayer Room Overlay</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionControllerOverlay">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Show Controller Overlay</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_Toggle_Grid_View">
|
<action name="action_Toggle_Grid_View">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
Reference in New Issue
Block a user