diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index 984f749cb6..b49478e34f 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -423,11 +423,11 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vectorReadAllWithProgress(sock(), contentLength, nullptr, progress->cancelled)) + 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->cancelled)) + if (!readbuf->ReadAllWithProgress(sock(), contentLength, &progress->progress, &progress->kBps, progress->cancelled)) return -1; } diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index 99da33b5dc..2087f1ea71 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -48,6 +48,7 @@ struct RequestProgress { explicit RequestProgress(bool *c) : cancelled(c) {} float progress = 0.0f; + float kBps = 0.0f; bool *cancelled = nullptr; }; @@ -98,6 +99,7 @@ public: // Returns 1.0 when done. That one value can be compared exactly - or just use Done(). float Progress() const { return progress_.progress; } + float SpeedKBps() const { return progress_.kBps; } bool Done() const { return completed_; } bool Failed() const { return failed_; } diff --git a/Common/Net/NetBuffer.cpp b/Common/Net/NetBuffer.cpp index baf903acc5..331f24b55e 100644 --- a/Common/Net/NetBuffer.cpp +++ b/Common/Net/NetBuffer.cpp @@ -47,7 +47,7 @@ bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { return true; } -bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled) { +bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled) { static constexpr float CANCEL_INTERVAL = 0.25f; std::vector buf; // We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time. @@ -59,6 +59,7 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, bool *c buf.resize(1024); } + double st = time_now_d(); int total = 0; while (true) { bool ready = false; @@ -84,6 +85,8 @@ bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, bool *c total += retval; if (progress) *progress = (float)total / (float)knownSize; + if (kBps) + *kBps = (float)(total / (time_now_d() - st)) / 1024.0f; } return true; } diff --git a/Common/Net/NetBuffer.h b/Common/Net/NetBuffer.h index 3ee7390aa3..6247aca985 100644 --- a/Common/Net/NetBuffer.h +++ b/Common/Net/NetBuffer.h @@ -8,7 +8,7 @@ class Buffer : public ::Buffer { public: bool FlushSocket(uintptr_t sock, double timeout, bool *cancelled = nullptr); - bool ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled); + bool ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled); // < 0: error // >= 0: number of bytes read diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 3e0c618b0e..fa911f34d4 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -91,6 +91,12 @@ bool GameManager::CancelDownload() { return true; } +float GameManager::DownloadSpeedKBps() { + if (curDownload_) + return curDownload_->SpeedKBps(); + return 0.0f; +} + bool GameManager::Uninstall(std::string name) { if (name.empty()) { ERROR_LOG(HLE, "Cannot remove an empty-named game"); diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 63460f66d7..54c685c1c2 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -48,6 +48,8 @@ public: // Cancels the download in progress, if any. bool CancelDownload(); + float DownloadSpeedKBps(); + // Call from time to time to check on completed downloads from the // main UI thread. void Update(); diff --git a/UI/Store.cpp b/UI/Store.cpp index 2ccf72c6a7..9c13934dbe 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -227,6 +227,7 @@ private: UI::Button *installButton_ = nullptr; UI::Button *launchButton_ = nullptr; UI::Button *cancelButton_ = nullptr; + UI::TextView *speedView_ = nullptr; bool wasInstalled_ = false; }; @@ -245,8 +246,13 @@ void ProductView::CreateViews() { wasInstalled_ = IsGameInstalled(); if (!wasInstalled_) { launchButton_ = nullptr; - installButton_ = Add(new Button(st->T("Install"))); + LinearLayout *progressDisplay = new LinearLayout(ORIENT_HORIZONTAL); + installButton_ = progressDisplay->Add(new Button(st->T("Install"))); installButton_->OnClick.Handle(this, &ProductView::OnInstall); + + speedView_ = progressDisplay->Add(new TextView("")); + speedView_->SetVisibility(V_GONE); + Add(progressDisplay); } else { installButton_ = nullptr; Add(new TextView(st->T("Already Installed"))); @@ -277,8 +283,17 @@ void ProductView::Update() { if (installButton_) { installButton_->SetEnabled(g_GameManager.GetState() == GameManagerState::IDLE); } - if (cancelButton_ && g_GameManager.GetState() != GameManagerState::DOWNLOADING) - cancelButton_->SetVisibility(UI::V_GONE); + if (g_GameManager.GetState() == GameManagerState::DOWNLOADING) { + if (speedView_) { + float speed = g_GameManager.DownloadSpeedKBps(); + speedView_->SetText(StringFromFormat("%0.1f KB/s", speed)); + } + } else { + if (cancelButton_) + cancelButton_->SetVisibility(UI::V_GONE); + if (speedView_) + speedView_->SetVisibility(UI::V_GONE); + } if (launchButton_) launchButton_->SetEnabled(g_GameManager.GetState() == GameManagerState::IDLE); View::Update(); @@ -302,6 +317,10 @@ UI::EventReturn ProductView::OnInstall(UI::EventParams &e) { if (cancelButton_) { cancelButton_->SetVisibility(UI::V_VISIBLE); } + if (speedView_) { + speedView_->SetVisibility(UI::V_VISIBLE); + speedView_->SetText(""); + } INFO_LOG(SYSTEM, "Triggering install of '%s'", fileUrl.c_str()); g_GameManager.DownloadAndInstall(fileUrl); return UI::EVENT_DONE;