mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Merge pull request #17737 from hrydgard/http-progress-bars
HTTP: Better progress bars
This commit is contained in:
commit
16ee5a50c7
@ -38,7 +38,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can
|
||||
http::RequestParams req(baseURL.Resource(), "text/plain, text/html; q=0.9, */*; q=0.8");
|
||||
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
|
||||
if (http.Connect(2, 20.0, cancel)) {
|
||||
http::RequestProgress progress(cancel);
|
||||
net::RequestProgress progress(cancel);
|
||||
code = http.GET(req, &result, responseHeaders, &progress);
|
||||
http.Disconnect();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/System/OSD.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
@ -217,7 +218,7 @@ bool GetHeaderValue(const std::vector<std::string> &responseHeaders, const std::
|
||||
return found;
|
||||
}
|
||||
|
||||
void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *progress) {
|
||||
void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength) {
|
||||
int dechunkedBytes = 0;
|
||||
while (true) {
|
||||
std::string line;
|
||||
@ -236,14 +237,11 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog
|
||||
return;
|
||||
}
|
||||
dechunkedBytes += chunkSize;
|
||||
if (progress && contentLength) {
|
||||
*progress = (float)dechunkedBytes / contentLength;
|
||||
}
|
||||
inbuffer->Skip(2);
|
||||
}
|
||||
}
|
||||
|
||||
int Client::GET(const RequestParams &req, Buffer *output, std::vector<std::string> &responseHeaders, RequestProgress *progress) {
|
||||
int Client::GET(const RequestParams &req, Buffer *output, std::vector<std::string> &responseHeaders, net::RequestProgress *progress) {
|
||||
const char *otherHeaders =
|
||||
"Accept-Encoding: gzip\r\n";
|
||||
int err = SendRequest("GET", req, otherHeaders, progress);
|
||||
@ -264,13 +262,13 @@ int Client::GET(const RequestParams &req, Buffer *output, std::vector<std::strin
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::GET(const RequestParams &req, Buffer *output, RequestProgress *progress) {
|
||||
int Client::GET(const RequestParams &req, Buffer *output, net::RequestProgress *progress) {
|
||||
std::vector<std::string> responseHeaders;
|
||||
int code = GET(req, output, responseHeaders, progress);
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress) {
|
||||
int Client::POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, net::RequestProgress *progress) {
|
||||
char otherHeaders[2048];
|
||||
if (mime.empty()) {
|
||||
snprintf(otherHeaders, sizeof(otherHeaders), "Content-Length: %lld\r\n", (long long)data.size());
|
||||
@ -296,16 +294,16 @@ int Client::POST(const RequestParams &req, const std::string &data, const std::s
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::POST(const RequestParams &req, const std::string &data, Buffer *output, RequestProgress *progress) {
|
||||
int Client::POST(const RequestParams &req, const std::string &data, Buffer *output, net::RequestProgress *progress) {
|
||||
return POST(req, data, "", output, progress);
|
||||
}
|
||||
|
||||
int Client::SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, RequestProgress *progress) {
|
||||
int Client::SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, net::RequestProgress *progress) {
|
||||
return SendRequestWithData(method, req, "", otherHeaders, progress);
|
||||
}
|
||||
|
||||
int Client::SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, RequestProgress *progress) {
|
||||
progress->progress = 0.01f;
|
||||
int Client::SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, net::RequestProgress *progress) {
|
||||
progress->Update(0, 0, false);
|
||||
|
||||
net::Buffer buffer;
|
||||
const char *tpl =
|
||||
@ -331,7 +329,7 @@ int Client::SendRequestWithData(const char *method, const RequestParams &req, co
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &responseHeaders, RequestProgress *progress) {
|
||||
int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &responseHeaders, net::RequestProgress *progress) {
|
||||
// Snarf all the data we can into RAM. A little unsafe but hey.
|
||||
static constexpr float CANCEL_INTERVAL = 0.25f;
|
||||
bool ready = false;
|
||||
@ -384,7 +382,7 @@ int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, RequestProgress *progress) {
|
||||
int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, net::RequestProgress *progress) {
|
||||
bool gzip = false;
|
||||
bool chunked = false;
|
||||
int contentLength = 0;
|
||||
@ -412,30 +410,18 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::stri
|
||||
}
|
||||
|
||||
if (contentLength < 0) {
|
||||
WARN_LOG(IO, "Negative content length %d", contentLength);
|
||||
// Just sanity checking...
|
||||
contentLength = 0;
|
||||
}
|
||||
|
||||
if (!contentLength) {
|
||||
// Content length is unknown.
|
||||
// Set progress to 1% so it looks like something is happening...
|
||||
progress->progress = 0.1f;
|
||||
}
|
||||
|
||||
if (!contentLength) {
|
||||
// No way to know how far along we are. Let's just not update the progress counter.
|
||||
if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, &progress->kBps, progress->cancelled))
|
||||
return -1;
|
||||
} else {
|
||||
// Let's read in chunks, updating progress between each.
|
||||
if (!readbuf->ReadAllWithProgress(sock(), contentLength, &progress->progress, &progress->kBps, progress->cancelled))
|
||||
return -1;
|
||||
}
|
||||
if (!readbuf->ReadAllWithProgress(sock(), contentLength, progress))
|
||||
return -1;
|
||||
|
||||
// output now contains the rest of the reply. Dechunk it.
|
||||
if (!output->IsVoid()) {
|
||||
if (chunked) {
|
||||
DeChunk(readbuf, output, contentLength, &progress->progress);
|
||||
DeChunk(readbuf, output, contentLength);
|
||||
} else {
|
||||
output->Append(*readbuf);
|
||||
}
|
||||
@ -447,22 +433,45 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::stri
|
||||
bool result = decompress_string(compressed, &decompressed);
|
||||
if (!result) {
|
||||
ERROR_LOG(IO, "Error decompressing using zlib");
|
||||
progress->progress = 0.0f;
|
||||
progress->Update(0, 0, true);
|
||||
return -1;
|
||||
}
|
||||
output->Append(decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
progress->progress = 1.0f;
|
||||
progress->Update(contentLength, contentLength, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Download::Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile)
|
||||
: method_(method), progress_(&cancelled_), url_(url), postData_(postData), postMime_(postMime), outfile_(outfile) {
|
||||
Download::Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode, const std::string &name)
|
||||
: method_(method), progress_(&cancelled_), url_(url), postData_(postData), postMime_(postMime), outfile_(outfile), progressBarMode_(progressBarMode), name_(name) {
|
||||
progress_.callback = [=](int64_t bytes, int64_t contentLength, bool done) {
|
||||
std::string message;
|
||||
if (!name_.empty()) {
|
||||
message = name_;
|
||||
} else {
|
||||
std::size_t pos = url_.rfind('/');
|
||||
if (pos != std::string::npos) {
|
||||
message = url_.substr(pos + 1);
|
||||
} else {
|
||||
message = url_;
|
||||
}
|
||||
}
|
||||
if (progressBarMode_ != ProgressBarMode::NONE) {
|
||||
INFO_LOG(IO, "Showing progress bar: %s", message.c_str());
|
||||
if (!done) {
|
||||
g_OSD.SetProgressBar(url_, std::move(message), 0.0f, (float)contentLength, (float)bytes, progressBarMode_ == ProgressBarMode::DELAYED ? 3.0f : 0.0f); // delay 3 seconds before showing.
|
||||
} else {
|
||||
g_OSD.RemoveProgressBar(url_, Failed() ? false : true, 0.5f);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Download::~Download() {
|
||||
g_OSD.RemoveProgressBar(url_, Failed() ? false : true, 0.5f);
|
||||
|
||||
_assert_msg_(joined_, "Download destructed without join");
|
||||
}
|
||||
|
||||
@ -480,7 +489,7 @@ void Download::Join() {
|
||||
|
||||
void Download::SetFailed(int code) {
|
||||
failed_ = true;
|
||||
progress_.progress = 1.0f;
|
||||
progress_.Update(0, 0, true);
|
||||
completed_ = true;
|
||||
}
|
||||
|
||||
@ -575,15 +584,14 @@ void Download::Do() {
|
||||
resultCode_ = resultCode;
|
||||
}
|
||||
|
||||
progress_.progress = 1.0f;
|
||||
|
||||
// Set this last to ensure no race conditions when checking Done. Users must always check
|
||||
// Done before looking at the result code.
|
||||
completed_ = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Download> Downloader::StartDownload(const std::string &url, const Path &outfile, const char *acceptMime) {
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::GET, url, "", "", outfile));
|
||||
std::shared_ptr<Download> Downloader::StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime) {
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::GET, url, "", "", outfile, mode));
|
||||
|
||||
if (!userAgent_.empty())
|
||||
dl->SetUserAgent(userAgent_);
|
||||
if (acceptMime)
|
||||
@ -596,9 +604,11 @@ std::shared_ptr<Download> Downloader::StartDownload(const std::string &url, cons
|
||||
std::shared_ptr<Download> Downloader::StartDownloadWithCallback(
|
||||
const std::string &url,
|
||||
const Path &outfile,
|
||||
ProgressBarMode mode,
|
||||
std::function<void(Download &)> callback,
|
||||
const std::string &name,
|
||||
const char *acceptMime) {
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::GET, url, "", "", outfile));
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::GET, url, "", "", outfile, mode, name));
|
||||
if (!userAgent_.empty())
|
||||
dl->SetUserAgent(userAgent_);
|
||||
if (acceptMime)
|
||||
@ -613,8 +623,10 @@ std::shared_ptr<Download> Downloader::AsyncPostWithCallback(
|
||||
const std::string &url,
|
||||
const std::string &postData,
|
||||
const std::string &postMime,
|
||||
std::function<void(Download &)> callback) {
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::POST, url, postData, postMime, Path()));
|
||||
ProgressBarMode mode,
|
||||
std::function<void(Download &)> callback,
|
||||
const std::string &name) {
|
||||
std::shared_ptr<Download> dl(new Download(RequestMethod::POST, url, postData, postMime, Path(), mode, name));
|
||||
if (!userAgent_.empty())
|
||||
dl->SetUserAgent(userAgent_);
|
||||
dl->SetCallback(callback);
|
||||
@ -649,15 +661,6 @@ void Downloader::WaitForAll() {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<float> Downloader::GetCurrentProgress() {
|
||||
std::vector<float> progress;
|
||||
for (size_t i = 0; i < downloads_.size(); i++) {
|
||||
if (!downloads_[i]->IsHidden())
|
||||
progress.push_back(downloads_[i]->Progress());
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
void Downloader::CancelAll() {
|
||||
for (size_t i = 0; i < downloads_.size(); i++) {
|
||||
downloads_[i]->Cancel();
|
||||
|
@ -44,16 +44,8 @@ namespace http {
|
||||
|
||||
bool GetHeaderValue(const std::vector<std::string> &responseHeaders, const std::string &header, std::string *value);
|
||||
|
||||
struct RequestProgress {
|
||||
RequestProgress() {}
|
||||
explicit RequestProgress(bool *c) : cancelled(c) {}
|
||||
|
||||
float progress = 0.0f;
|
||||
float kBps = 0.0f;
|
||||
bool *cancelled = nullptr;
|
||||
};
|
||||
|
||||
struct RequestParams {
|
||||
class RequestParams {
|
||||
public:
|
||||
RequestParams() {}
|
||||
explicit RequestParams(const char *r) : resource(r) {}
|
||||
RequestParams(const std::string &r, const char *a) : resource(r), acceptMime(a) {}
|
||||
@ -68,20 +60,20 @@ public:
|
||||
~Client();
|
||||
|
||||
// Return value is the HTTP return code. 200 means OK. < 0 means some local error.
|
||||
int GET(const RequestParams &req, Buffer *output, RequestProgress *progress);
|
||||
int GET(const RequestParams &req, Buffer *output, std::vector<std::string> &responseHeaders, RequestProgress *progress);
|
||||
int GET(const RequestParams &req, Buffer *output, net::RequestProgress *progress);
|
||||
int GET(const RequestParams &req, Buffer *output, std::vector<std::string> &responseHeaders, net::RequestProgress *progress);
|
||||
|
||||
// Return value is the HTTP return code.
|
||||
int POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress);
|
||||
int POST(const RequestParams &req, const std::string &data, Buffer *output, RequestProgress *progress);
|
||||
int POST(const RequestParams &req, const std::string &data, const std::string &mime, Buffer *output, net::RequestProgress *progress);
|
||||
int POST(const RequestParams &req, const std::string &data, Buffer *output, net::RequestProgress *progress);
|
||||
|
||||
// HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest.
|
||||
|
||||
int SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, RequestProgress *progress);
|
||||
int SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, RequestProgress *progress);
|
||||
int ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &responseHeaders, RequestProgress *progress);
|
||||
int SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, net::RequestProgress *progress);
|
||||
int SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, net::RequestProgress *progress);
|
||||
int ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &responseHeaders, net::RequestProgress *progress);
|
||||
// If your response contains a response, you must read it.
|
||||
int ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, RequestProgress *progress);
|
||||
int ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, net::RequestProgress *progress);
|
||||
|
||||
void SetDataTimeout(double t) {
|
||||
dataTimeout_ = t;
|
||||
@ -102,12 +94,26 @@ enum class RequestMethod {
|
||||
POST,
|
||||
};
|
||||
|
||||
enum class ProgressBarMode {
|
||||
NONE,
|
||||
VISIBLE,
|
||||
DELAYED,
|
||||
};
|
||||
|
||||
// Really an asynchronous request.
|
||||
class Download {
|
||||
public:
|
||||
Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile);
|
||||
Download(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode = ProgressBarMode::DELAYED, const std::string &name = "");
|
||||
~Download();
|
||||
|
||||
void SetAccept(const char *mime) {
|
||||
acceptMime_ = mime;
|
||||
}
|
||||
|
||||
void SetUserAgent(const std::string &userAgent) {
|
||||
userAgent_ = userAgent;
|
||||
}
|
||||
|
||||
void Start();
|
||||
|
||||
void Join();
|
||||
@ -125,10 +131,6 @@ public:
|
||||
std::string url() const { return url_; }
|
||||
const Path &outfile() const { return outfile_; }
|
||||
|
||||
void SetAccept(const char *mime) {
|
||||
acceptMime_ = mime;
|
||||
}
|
||||
|
||||
// If not downloading to a file, access this to get the result.
|
||||
Buffer &buffer() { return buffer_; }
|
||||
const Buffer &buffer() const { return buffer_; }
|
||||
@ -141,7 +143,7 @@ public:
|
||||
return cancelled_;
|
||||
}
|
||||
|
||||
// NOTE: Callbacks are NOT executed until RunCallback is called. This is so that
|
||||
// NOTE: Completion callbacks (which these are) are deferred until RunCallback is called. This is so that
|
||||
// the call will end up on the thread that calls g_DownloadManager.Update().
|
||||
void SetCallback(std::function<void(Download &)> callback) {
|
||||
callback_ = callback;
|
||||
@ -152,22 +154,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Just metadata. Convenient for download managers, for example, if set,
|
||||
// Downloader::GetCurrentProgress won't return it in the results.
|
||||
bool IsHidden() const { return hidden_; }
|
||||
void SetHidden(bool hidden) { hidden_ = hidden; }
|
||||
void SetUserAgent(const std::string &userAgent) {
|
||||
userAgent_ = userAgent;
|
||||
}
|
||||
|
||||
private:
|
||||
void Do(); // Actually does the download. Runs on thread.
|
||||
int Perform(const std::string &url);
|
||||
std::string RedirectLocation(const std::string &baseUrl);
|
||||
void SetFailed(int code);
|
||||
|
||||
RequestProgress progress_;
|
||||
RequestMethod method_;
|
||||
net::RequestProgress progress_;
|
||||
std::string postData_;
|
||||
std::string userAgent_;
|
||||
Buffer buffer_;
|
||||
@ -181,8 +175,9 @@ private:
|
||||
bool completed_ = false;
|
||||
bool failed_ = false;
|
||||
bool cancelled_ = false;
|
||||
bool hidden_ = false;
|
||||
ProgressBarMode progressBarMode_;
|
||||
bool joined_ = false;
|
||||
std::string name_;
|
||||
std::function<void(Download &)> callback_;
|
||||
};
|
||||
|
||||
@ -194,19 +189,23 @@ public:
|
||||
CancelAll();
|
||||
}
|
||||
|
||||
std::shared_ptr<Download> StartDownload(const std::string &url, const Path &outfile, const char *acceptMime = nullptr);
|
||||
std::shared_ptr<Download> StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime = nullptr);
|
||||
|
||||
std::shared_ptr<Download> StartDownloadWithCallback(
|
||||
const std::string &url,
|
||||
const Path &outfile,
|
||||
ProgressBarMode mode,
|
||||
std::function<void(Download &)> callback,
|
||||
const std::string &name = "",
|
||||
const char *acceptMime = nullptr);
|
||||
|
||||
std::shared_ptr<Download> AsyncPostWithCallback(
|
||||
const std::string &url,
|
||||
const std::string &postData,
|
||||
const std::string &postMime, // Use postMime = "application/x-www-form-urlencoded" for standard form-style posts, such as used by retroachievements. For encoding form data manually we have MultipartFormDataEncoder.
|
||||
std::function<void(Download &)> callback);
|
||||
ProgressBarMode mode,
|
||||
std::function<void(Download &)> callback,
|
||||
const std::string &name = "");
|
||||
|
||||
// Drops finished downloads from the list.
|
||||
void Update();
|
||||
@ -217,12 +216,6 @@ public:
|
||||
userAgent_ = userAgent;
|
||||
}
|
||||
|
||||
std::vector<float> GetCurrentProgress();
|
||||
|
||||
size_t GetActiveCount() const {
|
||||
return downloads_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Download>> downloads_;
|
||||
// These get copied to downloads_ in Update(). It's so that callbacks can add new downloads
|
||||
|
@ -48,7 +48,7 @@ bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled) {
|
||||
bool Buffer::ReadAllWithProgress(int fd, int knownSize, RequestProgress *progress) {
|
||||
static constexpr float CANCEL_INTERVAL = 0.25f;
|
||||
std::vector<char> buf;
|
||||
// We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time.
|
||||
@ -64,8 +64,8 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *
|
||||
int total = 0;
|
||||
while (true) {
|
||||
bool ready = false;
|
||||
while (!ready && cancelled) {
|
||||
if (*cancelled)
|
||||
while (!ready && (!progress || !*progress->cancelled)) {
|
||||
if (*progress->cancelled)
|
||||
return false;
|
||||
ready = fd_util::WaitUntilReady(fd, CANCEL_INTERVAL, false);
|
||||
}
|
||||
@ -88,10 +88,10 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *
|
||||
char *p = Append((size_t)retval);
|
||||
memcpy(p, &buf[0], retval);
|
||||
total += retval;
|
||||
if (progress)
|
||||
*progress = (float)total / (float)knownSize;
|
||||
if (kBps)
|
||||
*kBps = (float)(total / (time_now_d() - st)) / 1024.0f;
|
||||
if (progress) {
|
||||
progress->Update(total, knownSize, false);
|
||||
progress->kBps = (float)(total / (time_now_d() - st)) / 1024.0f;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -114,4 +114,4 @@ int Buffer::Read(int fd, size_t sz) {
|
||||
return (int)received;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
@ -1,16 +1,39 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class RequestProgress {
|
||||
public:
|
||||
RequestProgress() {}
|
||||
explicit RequestProgress(bool *c) : cancelled(c) {}
|
||||
|
||||
void Update(int64_t downloaded, int64_t totalBytes, bool done) {
|
||||
if (totalBytes) {
|
||||
progress = (double)downloaded / (double)totalBytes;
|
||||
} else {
|
||||
progress = 0.01f;
|
||||
}
|
||||
if (callback) {
|
||||
callback(downloaded, totalBytes, done);
|
||||
}
|
||||
}
|
||||
|
||||
float progress = 0.0f;
|
||||
float kBps = 0.0f;
|
||||
bool *cancelled = nullptr;
|
||||
std::function<void(int64_t, int64_t, bool)> callback;
|
||||
};
|
||||
|
||||
class Buffer : public ::Buffer {
|
||||
public:
|
||||
bool FlushSocket(uintptr_t sock, double timeout, bool *cancelled = nullptr);
|
||||
|
||||
bool ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled);
|
||||
bool ReadAllWithProgress(int fd, int knownSize, RequestProgress *progress);
|
||||
|
||||
// < 0: error
|
||||
// >= 0: number of bytes read
|
||||
|
@ -215,10 +215,15 @@ void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float durat
|
||||
Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, float minValue, float maxValue, float progress, float delay) {
|
||||
_dbg_assert_(!my_isnanorinf(progress));
|
||||
_dbg_assert_(!my_isnanorinf(minValue));
|
||||
_dbg_assert_(!my_isnanorinf(maxValue));
|
||||
|
||||
double now = time_now_d();
|
||||
bool found = false;
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
for (auto &bar : bars_) {
|
||||
if (bar.id == id) {
|
||||
bar.minValue = minValue;
|
||||
@ -230,22 +235,37 @@ void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int
|
||||
}
|
||||
}
|
||||
|
||||
if (message == "dorequest.php") {
|
||||
found = found;
|
||||
}
|
||||
|
||||
ProgressBar bar;
|
||||
bar.id = id;
|
||||
bar.message = std::move(message);
|
||||
bar.minValue = minValue;
|
||||
bar.maxValue = maxValue;
|
||||
bar.progress = progress;
|
||||
bar.startTime = now + delay;
|
||||
bar.endTime = now + 60.0; // Show the progress bar for 60 seconds, then fade it out.
|
||||
bars_.push_back(bar);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::RemoveProgressBar(std::string id) {
|
||||
void OnScreenDisplay::RemoveProgressBar(std::string id, bool success, float delay_s) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
for (auto iter = bars_.begin(); iter != bars_.end(); iter++) {
|
||||
if (iter->id == id) {
|
||||
iter->progress = iter->maxValue;
|
||||
iter->endTime = time_now_d() + FadeoutTime();
|
||||
if (success) {
|
||||
// Quickly shoot up to max, if we weren't there.
|
||||
if (iter->maxValue != 0.0f) {
|
||||
iter->progress = iter->maxValue;
|
||||
} else {
|
||||
// Fake a full progress
|
||||
iter->minValue = 0;
|
||||
iter->maxValue = 1;
|
||||
iter->progress = 1;
|
||||
}
|
||||
}
|
||||
iter->endTime = time_now_d() + delay_s + FadeoutTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ public:
|
||||
// Progress bar controls
|
||||
// Set is both create and update. If you set maxValue <= minValue, you'll create an "indeterminate" progress
|
||||
// bar that doesn't show a specific amount of progress.
|
||||
void SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress);
|
||||
void RemoveProgressBar(std::string id);
|
||||
void SetProgressBar(std::string id, std::string &&message, float minValue, float maxValue, float progress, float delay_s);
|
||||
void RemoveProgressBar(std::string id, bool success, float delay_s);
|
||||
|
||||
// Call every frame to keep the sidebar visible. Otherwise it'll fade out.
|
||||
void NudgeSidebar();
|
||||
@ -74,9 +74,10 @@ public:
|
||||
struct ProgressBar {
|
||||
std::string id;
|
||||
std::string message;
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int progress;
|
||||
float minValue;
|
||||
float maxValue;
|
||||
float progress;
|
||||
double startTime;
|
||||
double endTime;
|
||||
};
|
||||
|
||||
|
@ -1174,8 +1174,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
if (iRunCount % 10 == 0 && bCheckForNewVersion) {
|
||||
const char *versionUrl = "http://www.ppsspp.org/version.json";
|
||||
const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8";
|
||||
auto dl = g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), &DownloadCompletedCallback, acceptMime);
|
||||
dl->SetHidden(true);
|
||||
g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::ProgressBarMode::NONE, &DownloadCompletedCallback, acceptMime);
|
||||
}
|
||||
|
||||
INFO_LOG(LOADER, "Loading controller config: %s", controllerIniFilename_.c_str());
|
||||
|
@ -71,7 +71,7 @@ private:
|
||||
s64 filepos_ = 0;
|
||||
Url url_;
|
||||
http::Client client_;
|
||||
http::RequestProgress progress_;
|
||||
net::RequestProgress progress_;
|
||||
::Path filename_;
|
||||
bool connected_ = false;
|
||||
bool cancel_ = false;
|
||||
|
@ -131,7 +131,7 @@ static int sceNpMatching2ContextStart(int ctxId)
|
||||
//npMatching2Ctx.started = true;
|
||||
Url url("http://static-resource.np.community.playstation.net/np/resource/psp-title/" + std::string(npTitleId.data) + "_00/matching/" + std::string(npTitleId.data) + "_00-matching.xml");
|
||||
http::Client client;
|
||||
http::RequestProgress progress;
|
||||
net::RequestProgress progress;
|
||||
if (!client.Resolve(url.Host().c_str(), url.Port())) {
|
||||
return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP failed to resolve %s", url.Resource().c_str());
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ namespace Reporting
|
||||
bool SendReportRequest(const char *uri, const std::string &data, const std::string &mimeType, Buffer *output = NULL)
|
||||
{
|
||||
http::Client http;
|
||||
http::RequestProgress progress(&pendingMessagesDone);
|
||||
net::RequestProgress progress(&pendingMessagesDone);
|
||||
Buffer theVoid = Buffer::Void();
|
||||
|
||||
http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION));
|
||||
|
@ -175,8 +175,9 @@ static void server_call_callback(const rc_api_request_t *request,
|
||||
rc_client_server_callback_t callback, void *callback_data, rc_client_t *client)
|
||||
{
|
||||
// If post data is provided, we need to make a POST request, otherwise, a GET request will suffice.
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
if (request->post_data) {
|
||||
g_DownloadManager.AsyncPostWithCallback(std::string(request->url), std::string(request->post_data), "application/x-www-form-urlencoded", [=](http::Download &download) {
|
||||
std::shared_ptr<http::Download> download = g_DownloadManager.AsyncPostWithCallback(std::string(request->url), std::string(request->post_data), "application/x-www-form-urlencoded", http::ProgressBarMode::DELAYED, [=](http::Download &download) {
|
||||
std::string buffer;
|
||||
download.buffer().TakeAll(&buffer);
|
||||
rc_api_server_response_t response{};
|
||||
@ -184,9 +185,9 @@ static void server_call_callback(const rc_api_request_t *request,
|
||||
response.body_length = buffer.size();
|
||||
response.http_status_code = download.ResultCode();
|
||||
callback(&response, callback_data);
|
||||
});
|
||||
}, ac->T("Contacting RetroAchievements server..."));
|
||||
} else {
|
||||
g_DownloadManager.StartDownloadWithCallback(std::string(request->url), Path(), [=](http::Download &download) {
|
||||
std::shared_ptr<http::Download> download = g_DownloadManager.StartDownloadWithCallback(std::string(request->url), Path(), http::ProgressBarMode::DELAYED, [=](http::Download &download) {
|
||||
std::string buffer;
|
||||
download.buffer().TakeAll(&buffer);
|
||||
rc_api_server_response_t response{};
|
||||
@ -194,7 +195,7 @@ static void server_call_callback(const rc_api_request_t *request,
|
||||
response.body_length = buffer.size();
|
||||
response.http_status_code = download.ResultCode();
|
||||
callback(&response, callback_data);
|
||||
});
|
||||
}, ac->T("Contacting RetroAchievements server..."));
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,7 +395,7 @@ static void login_password_callback(int result, const char *error_message, rc_cl
|
||||
}
|
||||
}
|
||||
|
||||
g_OSD.RemoveProgressBar("cheevos_async_login");
|
||||
g_OSD.RemoveProgressBar("cheevos_async_login", true, 0.1f);
|
||||
g_isLoggingIn = false;
|
||||
}
|
||||
|
||||
@ -403,7 +404,7 @@ bool LoginAsync(const char *username, const char *password) {
|
||||
if (IsLoggedIn() || std::strlen(username) == 0 || std::strlen(password) == 0 || IsUsingRAIntegration())
|
||||
return false;
|
||||
|
||||
g_OSD.SetProgressBar("cheevos_async_login", di->T("Logging in..."), 0, 0, 0);
|
||||
g_OSD.SetProgressBar("cheevos_async_login", di->T("Logging in..."), 0, 0, 0, 0.0f);
|
||||
|
||||
g_isLoggingIn = true;
|
||||
rc_client_begin_login_with_password(g_rcClient, username, password, &login_password_callback, nullptr);
|
||||
@ -540,7 +541,7 @@ bool HasAchievementsOrLeaderboards() {
|
||||
void DownloadImageIfMissing(const std::string &cache_key, std::string &&url) {
|
||||
if (g_iconCache.MarkPending(cache_key)) {
|
||||
INFO_LOG(ACHIEVEMENTS, "Downloading image: %s (%s)", url.c_str(), cache_key.c_str());
|
||||
g_DownloadManager.StartDownloadWithCallback(url, Path(), [cache_key](http::Download &download) {
|
||||
g_DownloadManager.StartDownloadWithCallback(url, Path(), http::ProgressBarMode::NONE, [cache_key](http::Download &download) {
|
||||
if (download.ResultCode() != 200)
|
||||
return;
|
||||
std::string data;
|
||||
|
@ -102,7 +102,7 @@ bool GameManager::DownloadAndInstall(std::string storeFileUrl) {
|
||||
|
||||
Path filename = GetTempFilename();
|
||||
const char *acceptMime = "application/zip, application/x-cso, application/x-iso9660-image, application/octet-stream; q=0.9, */*; q=0.8";
|
||||
curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename, acceptMime);
|
||||
curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename, http::ProgressBarMode::VISIBLE, acceptMime);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ static ServerStatus RetrieveStatus() {
|
||||
static bool RegisterServer(int port) {
|
||||
bool success = false;
|
||||
http::Client http;
|
||||
http::RequestProgress progress;
|
||||
net::RequestProgress progress;
|
||||
Buffer theVoid = Buffer::Void();
|
||||
|
||||
http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION));
|
||||
|
@ -819,19 +819,23 @@ void SystemInfoScreen::CreateTabs() {
|
||||
});
|
||||
internals->Add(new ItemHeader(si->T("Progress tests")));
|
||||
internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30);
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100);
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0);
|
||||
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.RemoveProgressBar("testprogress");
|
||||
internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.RemoveProgressBar("testprogress", true, 0.5f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.RemoveProgressBar("testprogress", false, 0.5f);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new ItemHeader(si->T("Achievement tests")));
|
||||
@ -1336,7 +1340,7 @@ void FrameDumpTestScreen::update() {
|
||||
|
||||
if (!listing_) {
|
||||
const char *acceptMime = "text/html, */*; q=0.8";
|
||||
listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), acceptMime);
|
||||
listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), http::ProgressBarMode::DELAYED, acceptMime);
|
||||
}
|
||||
|
||||
if (listing_ && listing_->Done() && files_.empty()) {
|
||||
|
@ -292,13 +292,9 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get height
|
||||
float w, h;
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h);
|
||||
|
||||
y = 10.0f;
|
||||
|
||||
// Then draw them all.
|
||||
// Draw the progress bars.
|
||||
const std::vector<OnScreenDisplay::ProgressBar> bars = g_OSD.ProgressBars();
|
||||
for (auto &bar : bars) {
|
||||
float tw, th;
|
||||
@ -306,11 +302,14 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
Bounds b(0.0f, y, tw, th);
|
||||
b.x = (bounds_.w - b.w) * 0.5f;
|
||||
|
||||
float alpha = Clamp((float)(bar.endTime - now) * 4.0f, 0.0f, 1.0f);
|
||||
float enterAlpha = saturatef((float)(now - bar.startTime) * 4.0f);
|
||||
float leaveAlpha = saturatef((float)(bar.endTime - now) * 4.0f);
|
||||
float alpha = std::min(enterAlpha, leaveAlpha);
|
||||
RenderOSDProgressBar(dc, bar, b, 0, alpha);
|
||||
y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
// Draw the rest of the top-center messages.
|
||||
const std::vector<OnScreenDisplay::Entry> entries = g_OSD.Entries();
|
||||
for (const auto &entry : entries) {
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
@ -365,28 +364,6 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
RenderOSDEntry(dc, entry, b, h1, align, alpha);
|
||||
y += (b.h * scale + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
// Thin bar at the top of the screen.
|
||||
// TODO: Remove and replace with "proper" progress bars.
|
||||
std::vector<float> progress = g_DownloadManager.GetCurrentProgress();
|
||||
if (!progress.empty()) {
|
||||
static const uint32_t colors[4] = {
|
||||
0xFFFFFFFF,
|
||||
0xFFCCCCCC,
|
||||
0xFFAAAAAA,
|
||||
0xFF777777,
|
||||
};
|
||||
|
||||
dc.Begin();
|
||||
int h = 5;
|
||||
for (size_t i = 0; i < progress.size(); i++) {
|
||||
float barWidth = 10 + (dc.GetBounds().w - 10) * progress[i];
|
||||
Bounds bounds(0, h * i, barWidth, h);
|
||||
UI::Drawable solid(colors[i & 3]);
|
||||
dc.FillRect(solid, bounds);
|
||||
}
|
||||
dc.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
std::string OnScreenMessagesView::DescribeText() const {
|
||||
|
@ -139,7 +139,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort
|
||||
}
|
||||
|
||||
SetStatus("Loading game list from [URL]...", host, port);
|
||||
http::RequestProgress progress(&scanCancelled);
|
||||
net::RequestProgress progress(&scanCancelled);
|
||||
code = http.GET(http::RequestParams(subdir.c_str()), &result, &progress);
|
||||
http.Disconnect();
|
||||
|
||||
@ -193,7 +193,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort
|
||||
SetStatus("Looking for peers...", "", 0);
|
||||
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) {
|
||||
if (http.Connect(2, 20.0, &scanCancelled)) {
|
||||
http::RequestProgress progress(&scanCancelled);
|
||||
net::RequestProgress progress(&scanCancelled);
|
||||
code = http.GET(http::RequestParams("/match/list"), &result, &progress);
|
||||
http.Disconnect();
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
|
||||
if (useIconCache && g_iconCache.MarkPending(path_)) {
|
||||
const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8";
|
||||
downloader_->StartDownloadWithCallback(path_, Path(), [&](http::Download &download) {
|
||||
downloader_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::DELAYED, [&](http::Download &download) {
|
||||
if (download.ResultCode() == 200) {
|
||||
std::string data;
|
||||
download.buffer().TakeAll(&data);
|
||||
@ -165,8 +165,7 @@ void HttpImageFileView::Draw(UIContext &dc) {
|
||||
if (!texture_ && !textureFailed_ && !path_.empty() && !download_) {
|
||||
auto cb = std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1);
|
||||
const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8";
|
||||
download_ = downloader_->StartDownloadWithCallback(path_, Path(), cb, acceptMime);
|
||||
download_->SetHidden(true);
|
||||
downloader_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::NONE, cb, acceptMime);
|
||||
}
|
||||
|
||||
if (!textureData_.empty()) {
|
||||
@ -404,7 +403,7 @@ StoreScreen::StoreScreen() {
|
||||
|
||||
std::string indexPath = storeBaseUrl + "index.json";
|
||||
const char *acceptMime = "application/json, */*; q=0.8";
|
||||
listing_ = g_DownloadManager.StartDownload(indexPath, Path(), acceptMime);
|
||||
listing_ = g_DownloadManager.StartDownload(indexPath, Path(), http::ProgressBarMode::DELAYED, acceptMime);
|
||||
}
|
||||
|
||||
StoreScreen::~StoreScreen() {
|
||||
|
@ -32,6 +32,7 @@ Achievements = Achievements
|
||||
Achievements are disabled = Achievements are disabled
|
||||
Challenge Mode = Challenge Mode
|
||||
Challenge Mode (no savestates) = Challenge Mode (no savestates)
|
||||
Contacting RetroAchievements server... = Contacting RetroAchievements server...
|
||||
Customize = Customize
|
||||
Earned = You have earned %d of %d achievements, and %d of %d points
|
||||
Encore Mode = Encore Mode
|
||||
|
Loading…
Reference in New Issue
Block a user