Merge pull request #17737 from hrydgard/http-progress-bars

HTTP: Better progress bars
This commit is contained in:
Henrik Rydgård 2023-07-18 17:02:07 +02:00 committed by GitHub
commit 16ee5a50c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 188 additions and 167 deletions

View File

@ -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();
}

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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());

View File

@ -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;

View File

@ -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());
}

View File

@ -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));

View File

@ -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;

View File

@ -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;
}

View File

@ -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));

View File

@ -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()) {

View File

@ -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 {

View File

@ -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();
}

View File

@ -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() {

View File

@ -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