diff options
| author | 2017-02-23 14:29:00 -0700 | |
|---|---|---|
| committer | 2017-02-23 16:29:00 -0500 | |
| commit | 26823cd38be09b8fd4f2a20f8047d6fa95ff725c (patch) | |
| tree | f1829d9109f3f841eedb74db98ee93c3cf7b6566 /src | |
| parent | Merge pull request #2441 from jroweboy/titlebar (diff) | |
| download | yuzu-26823cd38be09b8fd4f2a20f8047d6fa95ff725c.tar.gz yuzu-26823cd38be09b8fd4f2a20f8047d6fa95ff725c.tar.xz yuzu-26823cd38be09b8fd4f2a20f8047d6fa95ff725c.zip | |
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/game_list.cpp | 48 | ||||
| -rw-r--r-- | src/citra_qt/game_list.h | 4 |
2 files changed, 51 insertions, 1 deletions
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} { | |||
| 39 | 39 | ||
| 40 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 40 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| 41 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | 41 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); |
| 42 | connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); | ||
| 42 | 43 | ||
| 43 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 44 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 44 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | 45 | // 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) { | |||
| 103 | item_model->removeRows(0, item_model->rowCount()); | 104 | item_model->removeRows(0, item_model->rowCount()); |
| 104 | 105 | ||
| 105 | emit ShouldCancelWorker(); | 106 | emit ShouldCancelWorker(); |
| 107 | |||
| 108 | auto watch_dirs = watcher.directories(); | ||
| 109 | if (!watch_dirs.isEmpty()) { | ||
| 110 | watcher.removePaths(watch_dirs); | ||
| 111 | } | ||
| 112 | UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0); | ||
| 106 | GameListWorker* worker = new GameListWorker(dir_path, deep_scan); | 113 | GameListWorker* worker = new GameListWorker(dir_path, deep_scan); |
| 107 | 114 | ||
| 108 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | 115 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); |
| @@ -140,6 +147,45 @@ static bool HasSupportedFileExtension(const std::string& file_name) { | |||
| 140 | return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); | 147 | return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); |
| 141 | } | 148 | } |
| 142 | 149 | ||
| 150 | void GameList::RefreshGameDirectory() { | ||
| 151 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | ||
| 152 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | ||
| 153 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Adds the game list folder to the QFileSystemWatcher to check for updates. | ||
| 159 | * | ||
| 160 | * The file watcher will fire off an update to the game list when a change is detected in the game | ||
| 161 | * list folder. | ||
| 162 | * | ||
| 163 | * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and | ||
| 164 | * this function is fast enough to not stall the UI thread. If performance is an issue, it should | ||
| 165 | * be moved to another thread and properly locked to prevent concurrency issues. | ||
| 166 | * | ||
| 167 | * @param dir folder to check for changes in | ||
| 168 | * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each | ||
| 169 | * directory recursively to the watcher and will update the file list if any of the folders | ||
| 170 | * change. The number determines how deep the recursion should traverse. | ||
| 171 | */ | ||
| 172 | void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) { | ||
| 173 | const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, | ||
| 174 | const std::string& virtual_name) -> bool { | ||
| 175 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 176 | |||
| 177 | if (FileUtil::IsDirectory(physical_name)) { | ||
| 178 | UpdateWatcherList(physical_name, recursion - 1); | ||
| 179 | } | ||
| 180 | return true; | ||
| 181 | }; | ||
| 182 | |||
| 183 | watcher.addPath(QString::fromStdString(dir)); | ||
| 184 | if (recursion > 0) { | ||
| 185 | FileUtil::ForeachDirectoryEntry(nullptr, dir, callback); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 143 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | 189 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { |
| 144 | const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, | 190 | const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, |
| 145 | const std::string& virtual_name) -> bool { | 191 | const std::string& virtual_name) -> bool { |
| @@ -182,6 +228,6 @@ void GameListWorker::run() { | |||
| 182 | } | 228 | } |
| 183 | 229 | ||
| 184 | void GameListWorker::Cancel() { | 230 | void GameListWorker::Cancel() { |
| 185 | disconnect(this, nullptr, nullptr, nullptr); | 231 | this->disconnect(); |
| 186 | stop_processing = true; | 232 | stop_processing = true; |
| 187 | } | 233 | } |
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 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <QFileSystemWatcher> | ||
| 7 | #include <QModelIndex> | 8 | #include <QModelIndex> |
| 8 | #include <QSettings> | 9 | #include <QSettings> |
| 9 | #include <QStandardItem> | 10 | #include <QStandardItem> |
| @@ -46,8 +47,11 @@ private: | |||
| 46 | void DonePopulating(); | 47 | void DonePopulating(); |
| 47 | 48 | ||
| 48 | void PopupContextMenu(const QPoint& menu_location); | 49 | void PopupContextMenu(const QPoint& menu_location); |
| 50 | void UpdateWatcherList(const std::string& path, unsigned int recursion); | ||
| 51 | void RefreshGameDirectory(); | ||
| 49 | 52 | ||
| 50 | QTreeView* tree_view = nullptr; | 53 | QTreeView* tree_view = nullptr; |
| 51 | QStandardItemModel* item_model = nullptr; | 54 | QStandardItemModel* item_model = nullptr; |
| 52 | GameListWorker* current_worker = nullptr; | 55 | GameListWorker* current_worker = nullptr; |
| 56 | QFileSystemWatcher watcher; | ||
| 53 | }; | 57 | }; |