Merge branch 'controller-crash-grid-view-memory-leak' into 'master'

fix: Prevent controller crashes and memory leaks in game list grid view

See merge request citron/rewrite!36
This commit is contained in:
Zephyron
2025-07-21 11:25:01 +00:00
2 changed files with 58 additions and 4 deletions

View File

@@ -203,6 +203,13 @@ void GameList::FilterGridView(const QString& filter_text) {
// Repopulate the grid view with filtered items // Repopulate the grid view with filtered items
QStandardItemModel* hierarchical_model = item_model; QStandardItemModel* hierarchical_model = item_model;
// Delete the previous flat model if it exists to prevent memory leaks
if (QAbstractItemModel* old_model = list_view->model()) {
if (old_model != item_model) {
old_model->deleteLater();
}
}
// Create a new flat model for grid view // Create a new flat model for grid view
QStandardItemModel* flat_model = new QStandardItemModel(this); QStandardItemModel* flat_model = new QStandardItemModel(this);
@@ -470,9 +477,19 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
if (!this->isActiveWindow()) { if (!this->isActiveWindow()) {
return; return;
} }
// Only send events to visible and properly initialized views
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
if (tree_view->isVisible() && tree_view->model()) {
QCoreApplication::postEvent(tree_view, event); QCoreApplication::postEvent(tree_view, event);
QCoreApplication::postEvent(list_view, event); }
if (list_view->isVisible() && list_view->model()) {
// Create a new event for the list view to avoid double deletion
QKeyEvent* list_event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(list_view, list_event);
}
}); });
// We must register all custom types with the Qt Automoc system so that we are able to use // We must register all custom types with the Qt Automoc system so that we are able to use
@@ -496,6 +513,13 @@ void GameList::UnloadController() {
GameList::~GameList() { GameList::~GameList() {
UnloadController(); UnloadController();
// Clean up any custom models that might have been created for grid view
if (QAbstractItemModel* current_model = list_view->model()) {
if (current_model != item_model) {
current_model->deleteLater();
}
}
} }
void GameList::SetFilterFocus() { void GameList::SetFilterFocus() {
@@ -1114,19 +1138,32 @@ void GameList::SetViewMode(bool grid_view) {
PopulateGridView(); PopulateGridView();
tree_view->setVisible(false); tree_view->setVisible(false);
list_view->setVisible(true); list_view->setVisible(true);
// Only set current index if the model has items
if (list_view->model() && list_view->model()->rowCount() > 0) {
list_view->setCurrentIndex(list_view->model()->index(0, 0)); list_view->setCurrentIndex(list_view->model()->index(0, 0));
}
} else { } else {
// Restore the hierarchical model for tree view // Restore the hierarchical model for tree view
list_view->setVisible(false); list_view->setVisible(false);
tree_view->setVisible(true); tree_view->setVisible(true);
// Only set current index if the model has items
if (item_model && item_model->rowCount() > 0) {
tree_view->setCurrentIndex(item_model->index(0, 0)); tree_view->setCurrentIndex(item_model->index(0, 0));
} }
}
} }
void GameList::PopulateGridView() { void GameList::PopulateGridView() {
// Store the current hierarchical model // Store the current hierarchical model
QStandardItemModel* hierarchical_model = item_model; QStandardItemModel* hierarchical_model = item_model;
// Delete the previous flat model if it exists to prevent memory leaks
if (QAbstractItemModel* old_model = list_view->model()) {
if (old_model != item_model) {
old_model->deleteLater();
}
}
// Create a new flat model for grid view // Create a new flat model for grid view
QStandardItemModel* flat_model = new QStandardItemModel(this); QStandardItemModel* flat_model = new QStandardItemModel(this);

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 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 "common/settings_input.h" #include "common/settings_input.h"
@@ -42,6 +43,12 @@ void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerTyp
if (!Settings::values.controller_navigation) { if (!Settings::values.controller_navigation) {
return; return;
} }
// Safety check: ensure controllers are properly initialized
if (!is_controller_set || !player1_controller || !handheld_controller) {
return;
}
if (type == Core::HID::ControllerTriggerType::Button) { if (type == Core::HID::ControllerTriggerType::Button) {
ControllerUpdateButton(); ControllerUpdateButton();
return; return;
@@ -54,6 +61,11 @@ void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerTyp
} }
void ControllerNavigation::ControllerUpdateButton() { void ControllerNavigation::ControllerUpdateButton() {
// Safety check: ensure controllers are properly initialized
if (!is_controller_set || !player1_controller || !handheld_controller) {
return;
}
const auto controller_type = player1_controller->GetNpadStyleIndex(); const auto controller_type = player1_controller->GetNpadStyleIndex();
const auto& player1_buttons = player1_controller->GetButtonsValues(); const auto& player1_buttons = player1_controller->GetButtonsValues();
const auto& handheld_buttons = handheld_controller->GetButtonsValues(); const auto& handheld_buttons = handheld_controller->GetButtonsValues();
@@ -91,6 +103,11 @@ void ControllerNavigation::ControllerUpdateButton() {
} }
void ControllerNavigation::ControllerUpdateStick() { void ControllerNavigation::ControllerUpdateStick() {
// Safety check: ensure controllers are properly initialized
if (!is_controller_set || !player1_controller || !handheld_controller) {
return;
}
const auto controller_type = player1_controller->GetNpadStyleIndex(); const auto controller_type = player1_controller->GetNpadStyleIndex();
const auto& player1_sticks = player1_controller->GetSticksValues(); const auto& player1_sticks = player1_controller->GetSticksValues();
const auto& handheld_sticks = player1_controller->GetSticksValues(); const auto& handheld_sticks = player1_controller->GetSticksValues();