From bf0e6ca17447a67f73b48542d6ea0419a6f8d10f Mon Sep 17 00:00:00 2001 From: msuih Date: Wed, 31 Oct 2018 20:38:48 +0200 Subject: [PATCH] Load trophies in another thread --- rpcs3/rpcs3qt/trophy_manager_dialog.cpp | 87 ++++++++++++++++++++----- rpcs3/rpcs3qt/trophy_manager_dialog.h | 37 ++++++++++- 2 files changed, 105 insertions(+), 19 deletions(-) diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index d9564cc4fc..4e234d9b87 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace { @@ -321,7 +322,22 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s m_game_combo->setCurrentText(m_game_table->item(m_game_table->selectedItems().first()->row(), GameColumns::GameName)->text()); }); - RepaintUI(true); + RepaintUI(); + + StartTrophyLoadThread(); +} + +trophy_manager_dialog::~trophy_manager_dialog() +{ + if (m_thread_state != TrophyThreadState::CLOSED) + { + TrophyThreadState expected = TrophyThreadState::RUNNING; + m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING); + while (m_thread_state != TrophyThreadState::CLOSED) + { + std::this_thread::yield(); + } + } } bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) @@ -395,7 +411,7 @@ bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) return true; } -void trophy_manager_dialog::RepaintUI(bool refresh_trophies) +void trophy_manager_dialog::RepaintUI() { if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) { @@ -406,11 +422,6 @@ void trophy_manager_dialog::RepaintUI(bool refresh_trophies) m_game_icon_color = gui::utils::get_label_color("gamelist_icon_background_color"); } - if (refresh_trophies) - { - PopulateTrophyDB(); - } - PopulateGameTable(); if (!restoreGeometry(m_gui_settings->GetValue(gui::tr_geometry).toByteArray())) @@ -581,18 +592,62 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& loc) menu->exec(globalPos); } -void trophy_manager_dialog::PopulateTrophyDB() +void trophy_manager_dialog::StartTrophyLoadThread() { - m_trophies_db.clear(); - - QDirIterator dir_iter(qstr(vfs::get(m_trophy_dir)), QDir::Dirs | QDir::NoDotAndDotDot); - while (dir_iter.hasNext()) + auto progressDialog = new QProgressDialog( + tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, this, + Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + progressDialog->setWindowTitle(tr("Loading trophies")); + connect(progressDialog, &QProgressDialog::canceled, [this]() { - dir_iter.next(); - std::string dirName = sstr(dir_iter.fileName()); - LOG_TRACE(GENERAL, "Loading trophy dir: %s", dirName); - LoadTrophyFolderToDB(dirName); + TrophyThreadState expected = TrophyThreadState::RUNNING; + m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING); + this->close(); // It's pointless to show an empty window + }); + progressDialog->show(); + + auto trophyThread = new trophy_manager_dialog::trophy_load_thread(this); + connect(trophyThread, &QThread::finished, trophyThread, &QThread::deleteLater); + connect(trophyThread, &QThread::finished, progressDialog, &QProgressDialog::deleteLater); + connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::TotalCountChanged, progressDialog, &QProgressDialog::setMaximum); + connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::ProcessedCountChanged, progressDialog, &QProgressDialog::setValue); + connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::FinishedSuccessfully, this, &trophy_manager_dialog::HandleRepaintUiRequest); + m_thread_state = TrophyThreadState::RUNNING; + trophyThread->start(); +} + +void trophy_manager_dialog::trophy_load_thread::run() +{ + m_manager->m_trophies_db.clear(); + + QDir trophy_dir(qstr(vfs::get(m_manager->m_trophy_dir))); + const auto folder_list = trophy_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + const int count = folder_list.count(); + Q_EMIT TotalCountChanged(count); + + for (int i = 0; m_manager->m_thread_state == TrophyThreadState::RUNNING && i < count; i++) + { + std::string dir_name = sstr(folder_list.value(i)); + LOG_TRACE(GENERAL, "Loading trophy dir: %s", dir_name); + try + { + m_manager->LoadTrophyFolderToDB(dir_name); + } + catch (const std::exception& e) + { + // TODO: Add error checks & throws to LoadTrophyFolderToDB so that they can be caught here. + // Also add a way of showing the number of corrupted/invalid folders in UI somewhere. + LOG_ERROR(GENERAL, "Exception occurred while parsing folder %s for trophies: %s", dir_name, e.what()); + } + Q_EMIT ProcessedCountChanged(i + 1); } + + if (m_manager->m_thread_state == TrophyThreadState::RUNNING) + { + Q_EMIT FinishedSuccessfully(); + } + + m_manager->m_thread_state = TrophyThreadState::CLOSED; } void trophy_manager_dialog::PopulateGameTable() diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.h b/rpcs3/rpcs3qt/trophy_manager_dialog.h index 51ab360843..8b14ce0658 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.h +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.h @@ -13,6 +13,7 @@ #include #include #include +#include struct GameTrophiesData { @@ -44,8 +45,17 @@ enum GameColumns GameColumnsCount }; +enum TrophyThreadState +{ + RUNNING, + CLOSING, + CLOSED +}; + class trophy_manager_dialog : public QWidget { + Q_OBJECT + const QString Bronze = "Bronze"; const QString Silver = "Silver"; const QString Gold = "Gold"; @@ -53,7 +63,8 @@ class trophy_manager_dialog : public QWidget public: explicit trophy_manager_dialog(std::shared_ptr gui_settings); - void RepaintUI(bool refresh_trophies = false); + ~trophy_manager_dialog() override; + void RepaintUI(); public Q_SLOTS: void HandleRepaintUiRequest(); @@ -71,8 +82,8 @@ private: */ bool LoadTrophyFolderToDB(const std::string& trop_name); - /** Populate the trophy database */ - void PopulateTrophyDB(); + /** Populate the trophy database (in another thread). */ + void StartTrophyLoadThread(); /** Fills game table with information. Takes results from LoadTrophyFolderToDB and puts it into the UI. @@ -99,6 +110,9 @@ private: QTableWidget* m_trophy_table; //! UI element to display trophy stuff. QTableWidget* m_game_table; //! UI element to display games. + class trophy_load_thread; //Qt cannot parse nested classes, declaration is below + std::atomic m_thread_state = TrophyThreadState::CLOSED; + bool m_show_hidden_trophies = false; bool m_show_unlocked_trophies = true; bool m_show_locked_trophies = true; @@ -118,3 +132,20 @@ private: QSlider* m_game_icon_slider = nullptr; QColor m_game_icon_color; }; + +class trophy_manager_dialog::trophy_load_thread : public QThread +{ + Q_OBJECT + + public: + explicit trophy_load_thread(trophy_manager_dialog *manager) : m_manager(manager) {} + void run() override; + + Q_SIGNALS: + void TotalCountChanged(int count); + void ProcessedCountChanged(int processed); + void FinishedSuccessfully(); + + private: + trophy_manager_dialog *m_manager; +};