mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
UI: Handle remote browsing asynchronously.
This commit is contained in:
parent
8d3da2ce88
commit
e5eb849e8b
@ -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)
|
||||
: 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;
|
||||
Refresh();
|
||||
}
|
||||
@ -510,10 +510,17 @@ bool GameBrowser::HasSpecialFiles(std::vector<std::string> &filenames) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameBrowser::Update() {
|
||||
LinearLayout::Update();
|
||||
if (listingPending_ && path_.IsListingReady()) {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void GameBrowser::Refresh() {
|
||||
using namespace UI;
|
||||
|
||||
homebrewStoreButton_ = 0;
|
||||
homebrewStoreButton_ = nullptr;
|
||||
// Kill all the contents
|
||||
Clear();
|
||||
|
||||
@ -556,12 +563,14 @@ void GameBrowser::Refresh() {
|
||||
std::vector<DirButton *> dirButtons;
|
||||
std::vector<GameButton *> gameButtons;
|
||||
|
||||
listingPending_ = !path_.IsListingReady();
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
if (HasSpecialFiles(filenames)) {
|
||||
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)));
|
||||
}
|
||||
} else {
|
||||
} else if (!listingPending_) {
|
||||
std::vector<FileInfo> fileInfo;
|
||||
path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:");
|
||||
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++) {
|
||||
gameList_->Add(dirButtons[i])->OnClick.Handle(this, &GameBrowser::NavigateClick);
|
||||
}
|
||||
@ -645,7 +658,7 @@ void GameBrowser::Refresh() {
|
||||
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)));
|
||||
} else {
|
||||
homebrewStoreButton_ = 0;
|
||||
homebrewStoreButton_ = nullptr;
|
||||
}
|
||||
|
||||
if (!lastText_.empty() && gameButtons.empty()) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
class GameBrowser : public UI::LinearLayout {
|
||||
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 OnHoldChoice;
|
||||
@ -37,6 +37,8 @@ public:
|
||||
void FocusGame(const std::string &gamePath);
|
||||
void SetPath(const std::string &path);
|
||||
|
||||
void Update() override;
|
||||
|
||||
protected:
|
||||
virtual bool DisplayTopBar();
|
||||
virtual bool HasSpecialFiles(std::vector<std::string> &filenames);
|
||||
@ -57,15 +59,16 @@ private:
|
||||
UI::EventReturn HomeClick(UI::EventParams &e);
|
||||
UI::EventReturn PinToggleClick(UI::EventParams &e);
|
||||
|
||||
UI::ViewGroup *gameList_;
|
||||
UI::ViewGroup *gameList_ = nullptr;
|
||||
PathBrowser path_;
|
||||
bool *gridStyle_;
|
||||
bool allowBrowsing_;
|
||||
std::string lastText_;
|
||||
std::string lastLink_;
|
||||
int flags_;
|
||||
UI::Choice *homebrewStoreButton_;
|
||||
UI::Choice *homebrewStoreButton_ = nullptr;
|
||||
std::string focusGamePath_;
|
||||
bool listingPending_ = false;
|
||||
};
|
||||
|
||||
class RemoteISOBrowseScreen;
|
||||
|
@ -160,7 +160,9 @@ static bool LoadGameList(const std::string &host, int port, std::vector<std::str
|
||||
return false;
|
||||
}
|
||||
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
|
||||
|
@ -1,11 +1,13 @@
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "base/stringutil.h"
|
||||
#include "base/timeutil.h"
|
||||
#include "file/path.h"
|
||||
#include "net/http_client.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;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
@ -58,6 +60,28 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileIn
|
||||
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;
|
||||
if (filter) {
|
||||
std::string tmp;
|
||||
@ -73,40 +97,33 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileIn
|
||||
filters.insert(std::move(tmp));
|
||||
}
|
||||
|
||||
for (std::string item : items) {
|
||||
// Apply some workarounds.
|
||||
if (item.empty())
|
||||
continue;
|
||||
if (item.back() == '\r')
|
||||
item.pop_back();
|
||||
auto pred = [&](const FileInfo &info) {
|
||||
if (info.isDirectory || !filter)
|
||||
return false;
|
||||
std::string ext = getFileExtension(info.fullName);
|
||||
return filters.find(ext) == filters.end();
|
||||
};
|
||||
files.erase(std::remove_if(files.begin(), files.end(), pred), files.end());
|
||||
return files;
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
info.name = item;
|
||||
info.fullName = baseURL.Relative(item).ToString();
|
||||
info.isDirectory = endsWith(item, "/");
|
||||
info.exists = true;
|
||||
info.size = 0;
|
||||
info.isWritable = false;
|
||||
if (!info.isDirectory) {
|
||||
std::string ext = getFileExtension(info.fullName);
|
||||
if (filter) {
|
||||
if (filters.find(ext) == filters.end())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
PathBrowser::~PathBrowser() {
|
||||
std::unique_lock<std::mutex> guard(pendingLock_);
|
||||
pendingCancel_ = true;
|
||||
pendingStop_ = true;
|
||||
pendingCond_.notify_all();
|
||||
guard.unlock();
|
||||
|
||||
files.push_back(info);
|
||||
if (pendingThread_.joinable()) {
|
||||
pendingThread_.join();
|
||||
}
|
||||
|
||||
|
||||
std::sort(files.begin(), files.end());
|
||||
return !files.empty();
|
||||
}
|
||||
|
||||
// Normalize slashes.
|
||||
void PathBrowser::SetPath(const std::string &path) {
|
||||
if (path[0] == '!') {
|
||||
path_ = path;
|
||||
HandlePath();
|
||||
return;
|
||||
}
|
||||
path_ = path;
|
||||
@ -115,9 +132,78 @@ void PathBrowser::SetPath(const std::string &path) {
|
||||
}
|
||||
if (!path_.size() || (path_[path_.size() - 1] != '/'))
|
||||
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) {
|
||||
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
|
||||
if (path_ == "/") {
|
||||
// Special path that means root of file system.
|
||||
@ -138,7 +224,8 @@ bool PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter
|
||||
#endif
|
||||
|
||||
if (startsWith(path_, "http://") || startsWith(path_, "https://")) {
|
||||
return LoadRemoteFileList(path_, cancel, fileInfo, filter);
|
||||
fileInfo = ApplyFilter(pendingFiles_, filter);
|
||||
return true;
|
||||
} else {
|
||||
getFilesInDir(path_.c_str(), &fileInfo, filter);
|
||||
return true;
|
||||
@ -167,4 +254,5 @@ void PathBrowser::Navigate(const std::string &path) {
|
||||
if (path_[path_.size() - 1] != '/')
|
||||
path_ += "/";
|
||||
}
|
||||
HandlePath();
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -14,8 +17,10 @@ class PathBrowser {
|
||||
public:
|
||||
PathBrowser() {}
|
||||
PathBrowser(std::string path) { SetPath(path); }
|
||||
~PathBrowser();
|
||||
|
||||
void SetPath(const std::string &path);
|
||||
bool IsListingReady();
|
||||
bool GetListing(std::vector<FileInfo> &fileInfo, const char *filter = nullptr, bool *cancel = nullptr);
|
||||
void Navigate(const std::string &path);
|
||||
|
||||
@ -39,6 +44,17 @@ public:
|
||||
return str;
|
||||
}
|
||||
|
||||
private:
|
||||
void HandlePath();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user