mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-17 18:20:45 -04:00
fix: Add destruction callback to EmulatedController for safe pointer management
Add SetDestructionCallback() method to allow observers to safely null out pointers when the controller is destroyed. This prevents use-after-free issues in UI components. Also includes cleanup and const correctness improvements in configure_input_player_widget. Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -12,6 +12,9 @@
|
|||||||
|
|
||||||
PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
|
PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
|
||||||
is_controller_set = false;
|
is_controller_set = false;
|
||||||
|
controller = nullptr;
|
||||||
|
callback_key = -1;
|
||||||
|
|
||||||
QTimer* timer = new QTimer(this);
|
QTimer* timer = new QTimer(this);
|
||||||
connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput));
|
connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput));
|
||||||
|
|
||||||
@@ -31,6 +34,14 @@ void PlayerControlPreview::SetController(Core::HID::EmulatedController* controll
|
|||||||
UnloadController();
|
UnloadController();
|
||||||
is_controller_set = true;
|
is_controller_set = true;
|
||||||
controller = controller_;
|
controller = controller_;
|
||||||
|
|
||||||
|
// This registers a function that the controller will call from its destructor.
|
||||||
|
// When called, it safely nulls our pointer so we don't use it after it's been freed.
|
||||||
|
controller->SetDestructionCallback([this]() {
|
||||||
|
this->controller = nullptr;
|
||||||
|
this->is_controller_set = false;
|
||||||
|
});
|
||||||
|
|
||||||
Core::HID::ControllerUpdateCallback engine_callback{
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
.on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
|
.on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
|
||||||
.is_npad_service = false,
|
.is_npad_service = false,
|
||||||
@@ -40,10 +51,14 @@ void PlayerControlPreview::SetController(Core::HID::EmulatedController* controll
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerControlPreview::UnloadController() {
|
void PlayerControlPreview::UnloadController() {
|
||||||
if (is_controller_set) {
|
// Only try to access the controller if the pointer is valid.
|
||||||
|
if (controller) {
|
||||||
controller->DeleteCallback(callback_key);
|
controller->DeleteCallback(callback_key);
|
||||||
is_controller_set = false;
|
// Also clear the destruction callback we set.
|
||||||
|
controller->SetDestructionCallback(nullptr);
|
||||||
}
|
}
|
||||||
|
is_controller_set = false;
|
||||||
|
controller = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerControlPreview::BeginMappingButton(std::size_t button_id) {
|
void PlayerControlPreview::BeginMappingButton(std::size_t button_id) {
|
||||||
|
|||||||
@@ -14,12 +14,6 @@
|
|||||||
#include "hid_core/frontend/emulated_controller.h"
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
|
|
||||||
class QLabel;
|
|
||||||
|
|
||||||
using AnalogParam = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
|
||||||
using ButtonParam = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
|
||||||
|
|
||||||
// Widget for representing controller animations
|
|
||||||
class PlayerControlPreview : public QFrame {
|
class PlayerControlPreview : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -206,30 +200,33 @@ private:
|
|||||||
// Draw primitive types
|
// Draw primitive types
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon);
|
void DrawPolygon(QPainter& p, const std::array<QPointF, N>& polygon);
|
||||||
void DrawCircle(QPainter& p, QPointF center, float size);
|
void DrawCircle(QPainter& p, const QPointF center, float size);
|
||||||
void DrawRectangle(QPainter& p, QPointF center, float width, float height);
|
void DrawRectangle(QPainter& p, const QPointF center, float width, float height);
|
||||||
void DrawRoundRectangle(QPainter& p, QPointF center, float width, float height, float round);
|
void DrawRoundRectangle(QPainter& p, const QPointF center, float width, float height, float round);
|
||||||
void DrawText(QPainter& p, QPointF center, float text_size, const QString& text);
|
void DrawText(QPainter& p, const QPointF center, float text_size, const QString& text);
|
||||||
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 raw_joystick_visible = false;
|
||||||
|
|
||||||
bool is_controller_set{};
|
Core::HID::EmulatedController* controller = nullptr;
|
||||||
bool is_connected{};
|
Core::HID::NpadStyleIndex controller_type{};
|
||||||
bool needs_redraw{};
|
int callback_key = -1;
|
||||||
Core::HID::NpadStyleIndex controller_type;
|
bool is_controller_set = false;
|
||||||
|
bool is_connected = false;
|
||||||
|
bool needs_redraw = false;
|
||||||
|
|
||||||
|
bool mapping_active = false;
|
||||||
|
int blink_counter = 0;
|
||||||
|
|
||||||
bool mapping_active{};
|
|
||||||
int blink_counter{};
|
|
||||||
int callback_key;
|
|
||||||
QColor button_color{};
|
QColor button_color{};
|
||||||
ColorMapping colors{};
|
ColorMapping colors{};
|
||||||
Core::HID::LedPattern led_pattern{0, 0, 0, 0};
|
Core::HID::LedPattern led_pattern{0, 0, 0, 0};
|
||||||
std::size_t player_index{};
|
|
||||||
Core::HID::EmulatedController* controller;
|
std::size_t player_index = 0;
|
||||||
std::size_t button_mapping_index{Settings::NativeButton::NumButtons};
|
std::size_t button_mapping_index{Settings::NativeButton::NumButtons};
|
||||||
std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs};
|
std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs};
|
||||||
|
|
||||||
Core::HID::ButtonValues button_values{};
|
Core::HID::ButtonValues button_values{};
|
||||||
Core::HID::SticksValues stick_values{};
|
Core::HID::SticksValues stick_values{};
|
||||||
Core::HID::TriggerValues trigger_values{};
|
Core::HID::TriggerValues trigger_values{};
|
||||||
|
|||||||
@@ -24,7 +24,15 @@ constexpr Common::UUID VIRTUAL_UUID =
|
|||||||
|
|
||||||
EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
|
EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
|
||||||
|
|
||||||
EmulatedController::~EmulatedController() = default;
|
EmulatedController::~EmulatedController() {
|
||||||
|
if (destruction_callback) {
|
||||||
|
destruction_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedController::SetDestructionCallback(std::function<void()> callback) {
|
||||||
|
destruction_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
|
NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|||||||
@@ -473,6 +473,12 @@ public:
|
|||||||
/// Changes sensitivity of the motion sensor
|
/// Changes sensitivity of the motion sensor
|
||||||
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode);
|
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a callback to be invoked when this object is about to be destructed.
|
||||||
|
* This is used for observers to safely null out their pointers.
|
||||||
|
*/
|
||||||
|
void SetDestructionCallback(std::function<void()> callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to the list of events
|
* Adds a callback to the list of events
|
||||||
* @param update_callback A ConsoleUpdateCallback that will be triggered
|
* @param update_callback A ConsoleUpdateCallback that will be triggered
|
||||||
@@ -654,6 +660,8 @@ private:
|
|||||||
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
||||||
int last_callback_key = 0;
|
int last_callback_key = 0;
|
||||||
|
|
||||||
|
std::function<void()> destruction_callback;
|
||||||
|
|
||||||
// Stores the current status of all controller input
|
// Stores the current status of all controller input
|
||||||
ControllerStatus controller;
|
ControllerStatus controller;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user