UI: Handle remote browsing asynchronously.

This commit is contained in:
Unknown W. Brackets 2019-10-06 11:49:35 -07:00
parent 8d3da2ce88
commit e5eb849e8b
5 changed files with 157 additions and 35 deletions

View File

@ -441,7 +441,7 @@ void DirButton::Draw(UIContext &dc) {
} }
GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags, UI::LayoutParams *layoutParams) GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags, UI::LayoutParams *layoutParams)
: LinearLayout(UI::ORIENT_VERTICAL, layoutParams), gameList_(0), path_(path), gridStyle_(gridStyle), allowBrowsing_(allowBrowsing), lastText_(lastText), lastLink_(lastLink), flags_(flags) { : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), allowBrowsing_(allowBrowsing), lastText_(lastText), lastLink_(lastLink), flags_(flags) {
using namespace UI; using namespace UI;
Refresh(); Refresh();
} }
@ -510,10 +510,17 @@ bool GameBrowser::HasSpecialFiles(std::vector<std::string> &filenames) {
return false; return false;
} }
void GameBrowser::Update() {
LinearLayout::Update();
if (listingPending_ && path_.IsListingReady()) {
Refresh();
}
}
void GameBrowser::Refresh() { void GameBrowser::Refresh() {
using namespace UI; using namespace UI;
homebrewStoreButton_ = 0; homebrewStoreButton_ = nullptr;
// Kill all the contents // Kill all the contents
Clear(); Clear();
@ -556,12 +563,14 @@ void GameBrowser::Refresh() {
std::vector<DirButton *> dirButtons; std::vector<DirButton *> dirButtons;
std::vector<GameButton *> gameButtons; std::vector<GameButton *> gameButtons;
listingPending_ = !path_.IsListingReady();
std::vector<std::string> filenames; std::vector<std::string> filenames;
if (HasSpecialFiles(filenames)) { if (HasSpecialFiles(filenames)) {
for (size_t i = 0; i < filenames.size(); i++) { for (size_t i = 0; i < filenames.size(); i++) {
gameButtons.push_back(new GameButton(filenames[i], *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT))); gameButtons.push_back(new GameButton(filenames[i], *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT)));
} }
} else { } else if (!listingPending_) {
std::vector<FileInfo> fileInfo; std::vector<FileInfo> fileInfo;
path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:"); path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:");
for (size_t i = 0; i < fileInfo.size(); i++) { for (size_t i = 0; i < fileInfo.size(); i++) {
@ -616,6 +625,10 @@ void GameBrowser::Refresh() {
} }
} }
if (listingPending_) {
gameList_->Add(new UI::TextView(mm->T("Loading..."), ALIGN_CENTER, false, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)));
}
for (size_t i = 0; i < dirButtons.size(); i++) { for (size_t i = 0; i < dirButtons.size(); i++) {
gameList_->Add(dirButtons[i])->OnClick.Handle(this, &GameBrowser::NavigateClick); gameList_->Add(dirButtons[i])->OnClick.Handle(this, &GameBrowser::NavigateClick);
} }
@ -645,7 +658,7 @@ void GameBrowser::Refresh() {
Add(new Spacer()); Add(new Spacer());
homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT))); homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT)));
} else { } else {
homebrewStoreButton_ = 0; homebrewStoreButton_ = nullptr;
} }
if (!lastText_.empty() && gameButtons.empty()) { if (!lastText_.empty() && gameButtons.empty()) {

View File

@ -26,7 +26,7 @@
class GameBrowser : public UI::LinearLayout { class GameBrowser : public UI::LinearLayout {
public: public:
GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle_, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = 0); GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = nullptr);
UI::Event OnChoice; UI::Event OnChoice;
UI::Event OnHoldChoice; UI::Event OnHoldChoice;
@ -37,6 +37,8 @@ public:
void FocusGame(const std::string &gamePath); void FocusGame(const std::string &gamePath);
void SetPath(const std::string &path); void SetPath(const std::string &path);
void Update() override;
protected: protected:
virtual bool DisplayTopBar(); virtual bool DisplayTopBar();
virtual bool HasSpecialFiles(std::vector<std::string> &filenames); virtual bool HasSpecialFiles(std::vector<std::string> &filenames);
@ -57,15 +59,16 @@ private:
UI::EventReturn HomeClick(UI::EventParams &e); UI::EventReturn HomeClick(UI::EventParams &e);
UI::EventReturn PinToggleClick(UI::EventParams &e); UI::EventReturn PinToggleClick(UI::EventParams &e);
UI::ViewGroup *gameList_; UI::ViewGroup *gameList_ = nullptr;
PathBrowser path_; PathBrowser path_;
bool *gridStyle_; bool *gridStyle_;
bool allowBrowsing_; bool allowBrowsing_;
std::string lastText_; std::string lastText_;
std::string lastLink_; std::string lastLink_;
int flags_; int flags_;
UI::Choice *homebrewStoreButton_; UI::Choice *homebrewStoreButton_ = nullptr;
std::string focusGamePath_; std::string focusGamePath_;
bool listingPending_ = false;
}; };
class RemoteISOBrowseScreen; class RemoteISOBrowseScreen;

View File

@ -160,7 +160,9 @@ static bool LoadGameList(const std::string &host, int port, std::vector<std::str
return false; return false;
} }
for (auto &file : files) { for (auto &file : files) {
games.push_back(file.fullName); if (RemoteISOFileSupported(file.name)) {
games.push_back(file.fullName);
}
} }
// Save for next time unless manual is true // Save for next time unless manual is true

View File

@ -1,11 +1,13 @@
#include <algorithm> #include <algorithm>
#include <set> #include <set>
#include "base/stringutil.h" #include "base/stringutil.h"
#include "base/timeutil.h"
#include "file/path.h" #include "file/path.h"
#include "net/http_client.h" #include "net/http_client.h"
#include "net/url.h" #include "net/url.h"
#include "thread/threadutil.h"
bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileInfo> &files, const char *filter) { bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileInfo> &files) {
http::Client http; http::Client http;
Buffer result; Buffer result;
int code = 500; int code = 500;
@ -58,6 +60,28 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileIn
return false; return false;
} }
for (std::string item : items) {
// Apply some workarounds.
if (item.empty())
continue;
if (item.back() == '\r')
item.pop_back();
FileInfo info;
info.name = item;
info.fullName = baseURL.Relative(item).ToString();
info.isDirectory = endsWith(item, "/");
info.exists = true;
info.size = 0;
info.isWritable = false;
files.push_back(info);
}
return !files.empty();
}
std::vector<FileInfo> ApplyFilter(std::vector<FileInfo> files, const char *filter) {
std::set<std::string> filters; std::set<std::string> filters;
if (filter) { if (filter) {
std::string tmp; std::string tmp;
@ -73,40 +97,33 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileIn
filters.insert(std::move(tmp)); filters.insert(std::move(tmp));
} }
for (std::string item : items) { auto pred = [&](const FileInfo &info) {
// Apply some workarounds. if (info.isDirectory || !filter)
if (item.empty()) return false;
continue; std::string ext = getFileExtension(info.fullName);
if (item.back() == '\r') return filters.find(ext) == filters.end();
item.pop_back(); };
files.erase(std::remove_if(files.begin(), files.end(), pred), files.end());
return files;
}
FileInfo info; PathBrowser::~PathBrowser() {
info.name = item; std::unique_lock<std::mutex> guard(pendingLock_);
info.fullName = baseURL.Relative(item).ToString(); pendingCancel_ = true;
info.isDirectory = endsWith(item, "/"); pendingStop_ = true;
info.exists = true; pendingCond_.notify_all();
info.size = 0; guard.unlock();
info.isWritable = false;
if (!info.isDirectory) {
std::string ext = getFileExtension(info.fullName);
if (filter) {
if (filters.find(ext) == filters.end())
continue;
}
}
files.push_back(info); if (pendingThread_.joinable()) {
pendingThread_.join();
} }
std::sort(files.begin(), files.end());
return !files.empty();
} }
// Normalize slashes. // Normalize slashes.
void PathBrowser::SetPath(const std::string &path) { void PathBrowser::SetPath(const std::string &path) {
if (path[0] == '!') { if (path[0] == '!') {
path_ = path; path_ = path;
HandlePath();
return; return;
} }
path_ = path; path_ = path;
@ -115,9 +132,78 @@ void PathBrowser::SetPath(const std::string &path) {
} }
if (!path_.size() || (path_[path_.size() - 1] != '/')) if (!path_.size() || (path_[path_.size() - 1] != '/'))
path_ += "/"; path_ += "/";
HandlePath();
}
void PathBrowser::HandlePath() {
std::lock_guard<std::mutex> guard(pendingLock_);
if (!path_.empty() && path_[0] == '!') {
ready_ = true;
pendingCancel_ = true;
pendingPath_.clear();
return;
}
if (!startsWith(path_, "http://") && !startsWith(path_, "https://")) {
ready_ = true;
pendingCancel_ = true;
pendingPath_.clear();
return;
}
ready_ = false;
pendingCancel_ = false;
pendingFiles_.clear();
pendingPath_ = path_;
pendingCond_.notify_all();
if (pendingThread_.joinable())
return;
pendingThread_ = std::thread([&] {
setCurrentThreadName("PathBrowser");
std::unique_lock<std::mutex> guard(pendingLock_);
std::vector<FileInfo> results;
std::string lastPath;
while (!pendingStop_) {
while (lastPath == pendingPath_ && !pendingCancel_) {
pendingCond_.wait(guard);
}
lastPath = pendingPath_;
bool success = false;
if (!lastPath.empty()) {
guard.unlock();
results.clear();
success = LoadRemoteFileList(lastPath, &pendingCancel_, results);
guard.lock();
}
if (pendingPath_ == lastPath) {
if (success && !pendingCancel_) {
pendingFiles_ = results;
}
pendingPath_.clear();
lastPath.clear();
ready_ = true;
}
}
});
}
bool PathBrowser::IsListingReady() {
return ready_;
} }
bool PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter, bool *cancel) { bool PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter, bool *cancel) {
std::unique_lock<std::mutex> guard(pendingLock_);
while (!IsListingReady() && (!cancel || !*cancel)) {
// In case cancel changes, just sleep.
guard.unlock();
sleep_ms(100);
guard.lock();
}
#ifdef _WIN32 #ifdef _WIN32
if (path_ == "/") { if (path_ == "/") {
// Special path that means root of file system. // Special path that means root of file system.
@ -138,7 +224,8 @@ bool PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter
#endif #endif
if (startsWith(path_, "http://") || startsWith(path_, "https://")) { if (startsWith(path_, "http://") || startsWith(path_, "https://")) {
return LoadRemoteFileList(path_, cancel, fileInfo, filter); fileInfo = ApplyFilter(pendingFiles_, filter);
return true;
} else { } else {
getFilesInDir(path_.c_str(), &fileInfo, filter); getFilesInDir(path_.c_str(), &fileInfo, filter);
return true; return true;
@ -167,4 +254,5 @@ void PathBrowser::Navigate(const std::string &path) {
if (path_[path_.size() - 1] != '/') if (path_[path_.size() - 1] != '/')
path_ += "/"; path_ += "/";
} }
HandlePath();
} }

View File

@ -1,7 +1,10 @@
#pragma once #pragma once
#include <condition_variable>
#include <mutex>
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <thread>
#include <vector> #include <vector>
#include <stdlib.h> #include <stdlib.h>
@ -14,8 +17,10 @@ class PathBrowser {
public: public:
PathBrowser() {} PathBrowser() {}
PathBrowser(std::string path) { SetPath(path); } PathBrowser(std::string path) { SetPath(path); }
~PathBrowser();
void SetPath(const std::string &path); void SetPath(const std::string &path);
bool IsListingReady();
bool GetListing(std::vector<FileInfo> &fileInfo, const char *filter = nullptr, bool *cancel = nullptr); bool GetListing(std::vector<FileInfo> &fileInfo, const char *filter = nullptr, bool *cancel = nullptr);
void Navigate(const std::string &path); void Navigate(const std::string &path);
@ -39,6 +44,17 @@ public:
return str; return str;
} }
private:
void HandlePath();
std::string path_; std::string path_;
std::string pendingPath_;
std::vector<FileInfo> pendingFiles_;
std::condition_variable pendingCond_;
std::mutex pendingLock_;
std::thread pendingThread_;
bool pendingCancel_ = false;
bool pendingStop_ = false;
bool ready_ = false;
}; };