mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-06-29 07:25:26 +02:00
[desktop] Clean up game list code, fix external watcher crash, and fix macOS flickering (#4106)
- Remove unnecessary icon update code (the UI reloads this stuff anyways); test on Windows please - Cleaned up a bunch of duplicated/unused code within the game list - Fix the game list constantly reloading on macOS * When you reconstruct the entire directory list on the watcher the directoryChanged signal fires on macOS--seems like a behavioral change that occurred somewhere in the 6.8 release cycle--and it would enter an infinite loop very quickly * To fix this, only the differences between the current and old watch list are accounted for on both ends. * Since this bug is now fixed, macOS uses Qt 6.11.1 now. Should theoretically improve our situation. - Fix the external content watcher crashing; the worker would attempt to read files that didn't exist without any bounds since its cache was still pointing to that file. This supersedes and replaces #4099. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4106 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
parent
7c0e993b5b
commit
102d254530
12 changed files with 181 additions and 277 deletions
|
|
@ -48,6 +48,14 @@ static QPixmap GetDefaultIcon(u32 size) {
|
|||
return icon;
|
||||
}
|
||||
|
||||
static QPixmap ThemeIcon(const char* name) {
|
||||
const int size = UISettings::values.folder_icon_size.GetValue();
|
||||
|
||||
return QIcon::fromTheme(QLatin1String(name))
|
||||
.pixmap(size, size)
|
||||
.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
class GameListItem : public QStandardItem {
|
||||
|
||||
public:
|
||||
|
|
@ -296,45 +304,33 @@ public:
|
|||
UISettings::GameDir* game_dir = &directory;
|
||||
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
|
||||
|
||||
const int icon_size = UISettings::values.folder_icon_size.GetValue();
|
||||
const char* icon_name = nullptr;
|
||||
|
||||
switch (dir_type) {
|
||||
case GameListItemType::SdmcDir:
|
||||
setData(
|
||||
QIcon::fromTheme(QStringLiteral("sd_card"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
icon_name = "sd_card";
|
||||
setData(QObject::tr("Installed SD Titles"), Qt::DisplayRole);
|
||||
break;
|
||||
case GameListItemType::UserNandDir:
|
||||
setData(
|
||||
QIcon::fromTheme(QStringLiteral("chip"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
icon_name = "chip";
|
||||
setData(QObject::tr("Installed NAND Titles"), Qt::DisplayRole);
|
||||
break;
|
||||
case GameListItemType::SysNandDir:
|
||||
setData(
|
||||
QIcon::fromTheme(QStringLiteral("chip"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
icon_name = "chip";
|
||||
setData(QObject::tr("System Titles"), Qt::DisplayRole);
|
||||
break;
|
||||
case GameListItemType::CustomDir: {
|
||||
const QString path = QString::fromStdString(game_dir->path);
|
||||
const QString icon_name =
|
||||
QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder");
|
||||
setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
|
||||
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
icon_name = QFileInfo::exists(path) ? "folder" : "bad_folder";
|
||||
setData(path, Qt::DisplayRole);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (icon_name != nullptr)
|
||||
setData(ThemeIcon(icon_name), Qt::DecorationRole);
|
||||
}
|
||||
|
||||
int type() const override {
|
||||
|
|
@ -357,12 +353,7 @@ public:
|
|||
explicit GameListAddDir() {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
const int icon_size = UISettings::values.folder_icon_size.GetValue();
|
||||
|
||||
setData(QIcon::fromTheme(QStringLiteral("list-add"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
setData(ThemeIcon("list-add"), Qt::DecorationRole);
|
||||
setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole);
|
||||
}
|
||||
|
||||
|
|
@ -380,12 +371,7 @@ public:
|
|||
explicit GameListFavorites() {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
const int icon_size = UISettings::values.folder_icon_size.GetValue();
|
||||
|
||||
setData(QIcon::fromTheme(QStringLiteral("star"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
setData(ThemeIcon("star"), Qt::DecorationRole);
|
||||
setData(QObject::tr("Favorites"), Qt::DisplayRole);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
#include "qt_common/util/game.h"
|
||||
|
||||
#include "qt_common/game_list/game_list_p.h"
|
||||
#include "qt_common/game_list/worker.h"
|
||||
#include "qt_common/game_list/model.h"
|
||||
#include "qt_common/game_list/worker.h"
|
||||
|
||||
GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
||||
FileSys::ManualContentProvider* provider_,
|
||||
|
|
@ -36,6 +36,8 @@ GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
|||
connect(external_watcher, &QFileSystemWatcher::directoryChanged, this,
|
||||
&GameListModel::RefreshExternalContent);
|
||||
|
||||
ResetExternalWatcher();
|
||||
|
||||
insertColumns(0, COLUMN_COUNT);
|
||||
RetranslateUI();
|
||||
|
||||
|
|
@ -45,6 +47,8 @@ GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
|||
GameListModel::~GameListModel() = default;
|
||||
|
||||
void GameListModel::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
||||
emit PopulatingStarted();
|
||||
|
||||
current_worker.reset();
|
||||
removeRows(0, rowCount());
|
||||
|
||||
|
|
@ -57,12 +61,6 @@ void GameListModel::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
|||
QThreadPool::globalInstance()->start(current_worker.get());
|
||||
}
|
||||
|
||||
void GameListModel::StopWorker() {
|
||||
// ~GameListWorker sets stop_requested and blocks until run() finishes, so this returns only
|
||||
// once the worker is no longer touching the content providers.
|
||||
current_worker.reset();
|
||||
}
|
||||
|
||||
void GameListModel::WorkerEvent() {
|
||||
current_worker->ProcessEvents(this);
|
||||
}
|
||||
|
|
@ -203,24 +201,25 @@ void GameListModel::LoadCompatibilityList() {
|
|||
}
|
||||
}
|
||||
|
||||
void GameListModel::Repopulate() {
|
||||
current_worker.reset();
|
||||
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
||||
PopulateAsync(UISettings::values.game_dirs);
|
||||
}
|
||||
|
||||
void GameListModel::RefreshGameDirectory() {
|
||||
ResetExternalWatcher();
|
||||
|
||||
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
|
||||
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||
StopWorker();
|
||||
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
||||
PopulateAsync(UISettings::values.game_dirs);
|
||||
Repopulate();
|
||||
}
|
||||
}
|
||||
|
||||
void GameListModel::RefreshExternalContent() {
|
||||
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
|
||||
LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache.");
|
||||
StopWorker();
|
||||
QtCommon::Game::ResetMetadata(false);
|
||||
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
||||
PopulateAsync(UISettings::values.game_dirs);
|
||||
Repopulate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,60 +234,6 @@ void GameListModel::ResetExternalWatcher() {
|
|||
}
|
||||
}
|
||||
|
||||
void GameListModel::OnUpdateThemedIcons() {
|
||||
for (int i = 0; i < invisibleRootItem()->rowCount(); i++) {
|
||||
QStandardItem* child = invisibleRootItem()->child(i);
|
||||
|
||||
const int icon_size = UISettings::values.folder_icon_size.GetValue();
|
||||
|
||||
switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
|
||||
case GameListItemType::SdmcDir:
|
||||
child->setData(
|
||||
QIcon::fromTheme(QStringLiteral("sd_card"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
break;
|
||||
case GameListItemType::UserNandDir:
|
||||
case GameListItemType::SysNandDir:
|
||||
child->setData(
|
||||
QIcon::fromTheme(QStringLiteral("chip"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
break;
|
||||
case GameListItemType::CustomDir: {
|
||||
const UISettings::GameDir& game_dir =
|
||||
UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
|
||||
const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path))
|
||||
? QStringLiteral("folder")
|
||||
: QStringLiteral("bad_folder");
|
||||
child->setData(
|
||||
QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
|
||||
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
break;
|
||||
}
|
||||
case GameListItemType::AddDir:
|
||||
child->setData(
|
||||
QIcon::fromTheme(QStringLiteral("list-add"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
break;
|
||||
case GameListItemType::Favorites:
|
||||
child->setData(
|
||||
QIcon::fromTheme(QStringLiteral("star"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
Qt::DecorationRole);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameListModel::RetranslateUI() {
|
||||
setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QStandardItemModel>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_common/play_time_manager.h"
|
||||
|
|
@ -52,10 +52,6 @@ public:
|
|||
void DonePopulating(const QStringList& watch_list);
|
||||
|
||||
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
|
||||
// Stops and joins the running populate worker, if any. Must be called before rebuilding the
|
||||
// content providers (CreateFactories), otherwise the worker keeps scanning a cache that is
|
||||
// being torn down underneath it.
|
||||
void StopWorker();
|
||||
void WorkerEvent();
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
|
@ -68,7 +64,6 @@ public:
|
|||
|
||||
void LoadCompatibilityList();
|
||||
|
||||
void OnUpdateThemedIcons();
|
||||
void RetranslateUI();
|
||||
|
||||
QFileSystemWatcher* GetWatcher() const;
|
||||
|
|
@ -80,6 +75,7 @@ public:
|
|||
signals:
|
||||
void ShowList(bool show);
|
||||
void PopulatingCompleted(const QStringList& watch_list);
|
||||
void PopulatingStarted();
|
||||
void SaveConfig();
|
||||
|
||||
private:
|
||||
|
|
@ -87,6 +83,7 @@ private:
|
|||
|
||||
void AddFavorite(u64 program_id);
|
||||
void RemoveFavorite(u64 program_id);
|
||||
void Repopulate();
|
||||
|
||||
bool m_flat = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@
|
|||
#include "qt_common/config/uisettings.h"
|
||||
#include "qt_common/qt_common.h"
|
||||
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "qt_common/game_list/game_list_p.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
|
||||
#include "qt_common/game_list/worker.h"
|
||||
#include "qt_common/game_list/model.h"
|
||||
#include "qt_common/game_list/worker.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -391,6 +391,25 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
|||
std::vector<u64> program_ids;
|
||||
loader->ReadProgramIds(program_ids);
|
||||
|
||||
const auto addEntry = [this, physical_name,
|
||||
parent_dir](std::unique_ptr<Loader::AppLoader>& app_loader,
|
||||
const u64 id) {
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = app_loader->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = app_loader->ReadTitle(name);
|
||||
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
auto entry = MakeGameListEntry(
|
||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *app_loader,
|
||||
id, compatibility_list, play_time_manager, patch);
|
||||
|
||||
RecordEvent([=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
|
||||
};
|
||||
|
||||
if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
for (const auto id : program_ids) {
|
||||
|
|
@ -404,38 +423,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
|||
continue;
|
||||
}
|
||||
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
|
||||
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
auto entry = MakeGameListEntry(
|
||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||
id, compatibility_list, play_time_manager, patch);
|
||||
|
||||
RecordEvent(
|
||||
[=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
|
||||
addEntry(loader, id);
|
||||
}
|
||||
} else {
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
|
||||
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
auto entry = MakeGameListEntry(
|
||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||
program_id, compatibility_list, play_time_manager, patch);
|
||||
|
||||
RecordEvent(
|
||||
[=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
|
||||
addEntry(loader, program_id);
|
||||
}
|
||||
}
|
||||
} else if (is_dir) {
|
||||
|
|
@ -466,29 +457,33 @@ void GameListWorker::run() {
|
|||
break;
|
||||
}
|
||||
|
||||
GameListDir* game_list_dir;
|
||||
bool scan = false;
|
||||
|
||||
if (game_dir.path == std::string("SDMC")) {
|
||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||
} else if (game_dir.path == std::string("UserNAND")) {
|
||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||
} else if (game_dir.path == std::string("SysNAND")) {
|
||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||
} else {
|
||||
const QString qpath = QString::fromStdString(game_dir.path);
|
||||
if (QDir(qpath).exists()) {
|
||||
watch_list.append(qpath);
|
||||
}
|
||||
auto* const game_list_dir = new GameListDir(game_dir);
|
||||
DirEntryReady(game_list_dir);
|
||||
|
||||
game_list_dir = new GameListDir(game_dir);
|
||||
scan = true;
|
||||
}
|
||||
|
||||
DirEntryReady(game_list_dir);
|
||||
if (scan) {
|
||||
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
|
||||
game_list_dir);
|
||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
|
||||
game_list_dir);
|
||||
} else {
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue