From 26823cd38be09b8fd4f2a20f8047d6fa95ff725c Mon Sep 17 00:00:00 2001 From: James Rowe Date: Thu, 23 Feb 2017 14:29:00 -0700 Subject: Use QFileSystemWatcher to reload the game list when a change is detected. (#2555) * Added a refresh game directory option to the file menu * Make the game list watcher recursive and have it start watching from the initial load * Rework game list watcher to be thread safe * Fix code style issues --- src/citra_qt/game_list.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++- src/citra_qt/game_list.h | 4 ++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'src/citra_qt') diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 222c82b1c..db6f920ff 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -39,6 +39,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} { connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); + connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); // We must register all custom types with the Qt Automoc system so that we are able to use it // with signals/slots. In this case, QList falls under the umbrells of custom types. @@ -103,6 +104,12 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { item_model->removeRows(0, item_model->rowCount()); emit ShouldCancelWorker(); + + auto watch_dirs = watcher.directories(); + if (!watch_dirs.isEmpty()) { + watcher.removePaths(watch_dirs); + } + UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0); GameListWorker* worker = new GameListWorker(dir_path, deep_scan); connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); @@ -140,6 +147,45 @@ static bool HasSupportedFileExtension(const std::string& file_name) { return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); } +void GameList::RefreshGameDirectory() { + if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { + LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); + PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + } +} + +/** + * Adds the game list folder to the QFileSystemWatcher to check for updates. + * + * The file watcher will fire off an update to the game list when a change is detected in the game + * list folder. + * + * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and + * this function is fast enough to not stall the UI thread. If performance is an issue, it should + * be moved to another thread and properly locked to prevent concurrency issues. + * + * @param dir folder to check for changes in + * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each + * directory recursively to the watcher and will update the file list if any of the folders + * change. The number determines how deep the recursion should traverse. + */ +void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) { + const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { + std::string physical_name = directory + DIR_SEP + virtual_name; + + if (FileUtil::IsDirectory(physical_name)) { + UpdateWatcherList(physical_name, recursion - 1); + } + return true; + }; + + watcher.addPath(QString::fromStdString(dir)); + if (recursion > 0) { + FileUtil::ForeachDirectoryEntry(nullptr, dir, callback); + } +} + void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, const std::string& virtual_name) -> bool { @@ -182,6 +228,6 @@ void GameListWorker::run() { } void GameListWorker::Cancel() { - disconnect(this, nullptr, nullptr, nullptr); + this->disconnect(); stop_processing = true; } diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index e6b7eea0b..b141fa3a5 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -46,8 +47,11 @@ private: void DonePopulating(); void PopupContextMenu(const QPoint& menu_location); + void UpdateWatcherList(const std::string& path, unsigned int recursion); + void RefreshGameDirectory(); QTreeView* tree_view = nullptr; QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; + QFileSystemWatcher watcher; }; -- cgit v1.2.3