From a458e02e8450eeadb5e288bb5970a51e8f26ad7a Mon Sep 17 00:00:00 2001 From: offtkp Date: Fri, 27 Oct 2023 13:34:56 +0300 Subject: [PATCH] Downloader window, settings, stuff --- CMakeLists.txt | 1 + include/download.hxx | 15 ++++++++--- include/settings.hxx | 1 + qt/downloaderwindow.cxx | 60 +++++++++++++++++++++++++++++++++++++++++ qt/downloaderwindow.hxx | 26 ++++++++++++++++++ qt/mainwindow.cxx | 6 ++--- qt/mainwindow.hxx | 4 ++- qt/settingswindow.cxx | 21 +++++++++++++++ vendored/httplib.h | 2 ++ 9 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 qt/downloaderwindow.cxx create mode 100644 qt/downloaderwindow.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a307d91..52e5630e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ set(HYDRA_QT_FILES qt/aboutwindow.cxx qt/keypicker.cxx qt/terminalwindow.cxx + qt/downloaderwindow.cxx vendored/miniaudio.c vendored/stb_image_write.c vendored/miniz/miniz.c diff --git a/include/download.hxx b/include/download.hxx index 8516fdff..ee05498f 100644 --- a/include/download.hxx +++ b/include/download.hxx @@ -11,7 +11,7 @@ namespace hydra inline std::pair split_url(const std::string& url) { - std::regex path_regex("https://(.*?)(/.*)"); + std::regex path_regex("http[s]?://(.*?)(/.*)"); std::string host = "https://", query; std::smatch match; if (std::regex_search(url, match, path_regex) && match.size() == 3) @@ -21,7 +21,7 @@ namespace hydra } else { - printf("[rcheevos]: failed to parse URL: %s\n", url.c_str()); + printf("Failed to parse URL: %s\n", url.c_str()); } return std::make_pair(host, query); } @@ -29,13 +29,22 @@ namespace hydra struct Downloader { static HydraBufferWrapper Download(const std::string& url) + { + return DownloadProgress(url); + } + + static HydraBufferWrapper + DownloadProgress(const std::string& url, + std::function progress = nullptr) { try { auto [host, query] = split_url(url); httplib::Client client(host); client.set_follow_location(true); - auto response = client.Get(query); + httplib::Result response = + progress ? client.Get(query, progress) : client.Get(query); + printf("Finished: %s\n", url.c_str()); if (!response) { diff --git a/include/settings.hxx b/include/settings.hxx index 4482fe6f..1cce7a78 100644 --- a/include/settings.hxx +++ b/include/settings.hxx @@ -40,6 +40,7 @@ class Settings public: static void Open(const std::filesystem::path& path) { + map_.clear(); save_path_ = path; std::ifstream ifs(save_path_); if (ifs.good()) diff --git a/qt/downloaderwindow.cxx b/qt/downloaderwindow.cxx new file mode 100644 index 00000000..b3c95a53 --- /dev/null +++ b/qt/downloaderwindow.cxx @@ -0,0 +1,60 @@ +#include "downloaderwindow.hxx" +#include "download.hxx" +#include +#include +#include +#include +#include +#include + +DownloaderWindow::DownloaderWindow(const std::vector& download_queue) + : QWidget(nullptr, Qt::Window), download_queue_(download_queue) +{ + setMinimumSize(500, 400); + setAttribute(Qt::WA_DeleteOnClose); + QVBoxLayout* layout = new QVBoxLayout; + log_ = new QTextEdit; + log_->setReadOnly(true); + log_->setLineWrapMode(QTextEdit::NoWrap); + log_->setMinimumSize(500, 400); + log_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + progress_bar_ = new QProgressBar; + progress_bar_->setMinimum(0); + progress_bar_->setMaximum(100); + progress_bar_->setValue(0); + + byte_progress_label_ = new QLabel(""); + byte_progress_label_->setAlignment(Qt::AlignCenter); + + layout->addWidget(log_); + layout->addWidget(byte_progress_label_); + layout->addWidget(progress_bar_); + setLayout(layout); + + setWindowFlags(Qt::WindowStaysOnTopHint); + + downloading_ = true; + std::thread t([this]() { + std::future f = std::async(std::launch::async, [this]() { + auto ret = hydra::Downloader::DownloadProgress( + download_queue_[0], [this](uint64_t current, uint64_t total) { + if (current == 0 && total == 0) + { + printf("Unknown size\n"); + } + else + { + int percent = (int)(current * 100 / total); + printf("Progress: %d%%\n", percent); + } + return downloading_; + }); + return ret; + }); + f.wait(); + }); + t.detach(); +} + +DownloaderWindow::~DownloaderWindow() {} diff --git a/qt/downloaderwindow.hxx b/qt/downloaderwindow.hxx new file mode 100644 index 00000000..3e4dedcc --- /dev/null +++ b/qt/downloaderwindow.hxx @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +class QProgressBar; +class QTextEdit; +class QLabel; + +class DownloaderWindow : public QWidget +{ + Q_OBJECT + +public: + DownloaderWindow(const std::vector& download_queue); + ~DownloaderWindow(); + +private: + std::vector download_queue_; + bool downloading_ = false; + + QProgressBar* progress_bar_; + QTextEdit* log_; + QLabel* byte_progress_label_; +}; \ No newline at end of file diff --git a/qt/mainwindow.cxx b/qt/mainwindow.cxx index 9a8f2db6..7e02985d 100644 --- a/qt/mainwindow.cxx +++ b/qt/mainwindow.cxx @@ -1,5 +1,7 @@ #include "mainwindow.hxx" #include "aboutwindow.hxx" +#include "downloaderwindow.hxx" +#include "input.hxx" #include "qthelper.hxx" #include "scripteditor.hxx" #include "settingswindow.hxx" @@ -139,10 +141,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) msg->show(); watcher->deleteLater(); } - else - { - printf("Result: %d\n", static_cast(watcher->result())); - } }); watcher->setFuture(future); } diff --git a/qt/mainwindow.hxx b/qt/mainwindow.hxx index ee5e703b..3b975e67 100644 --- a/qt/mainwindow.hxx +++ b/qt/mainwindow.hxx @@ -1,6 +1,5 @@ #pragma once -#include "input.hxx" #include "screenwidget.hxx" #include "settings.hxx" #include @@ -20,6 +19,8 @@ #include #include +class DownloaderWindow; + class MainWindow : public QMainWindow { Q_OBJECT @@ -92,6 +93,7 @@ private: QAction* recent_act_; QTimer* emulator_timer_; ScreenWidget* screen_; + DownloaderWindow* downloader_; ma_device sound_device_{}; bool frontend_driven_ = false; std::unique_ptr emulator_; diff --git a/qt/settingswindow.cxx b/qt/settingswindow.cxx index 3560735f..b83aba68 100644 --- a/qt/settingswindow.cxx +++ b/qt/settingswindow.cxx @@ -181,6 +181,25 @@ void SettingsWindow::create_tabs() use_cwd->setCheckState(Settings::Get("screenshot_path").empty() ? Qt::Checked : Qt::Unchecked); general_layout->addWidget(use_cwd, 1, 0, 1, 2); + + QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); + general_layout->addItem(spacer, 2, 0); + + QPushButton* reset_settings = new QPushButton("Reset settings"); + connect(reset_settings, &QPushButton::clicked, this, [this]() { + auto reply = QMessageBox::question(this, "Reset settings", + "Are you sure you want to reset your settings?", + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) + { + std::filesystem::copy(Settings::GetSavePath() / "settings.json", + Settings::GetSavePath() / "settings.json.bak", + std::filesystem::copy_options::overwrite_existing); + std::filesystem::remove(Settings::GetSavePath() / "settings.json"); + Settings::Open(Settings::GetSavePath() / "settings.json"); + } + }); + general_layout->addWidget(reset_settings, 3, 0, 1, 3); } { QGridLayout* cores_layout = new QGridLayout; @@ -232,6 +251,7 @@ void SettingsWindow::create_tabs() msg.exec(); }); cores_layout->addWidget(core_list, 0, 0, 1, 0); + // TODO: become a non-lazy developer add_filepicker(cores_layout, "Core directory", "core_path", "", 1, 0, true, [](const std::string&) { Settings::ReinitCoreInfo(); @@ -273,6 +293,7 @@ void SettingsWindow::create_tabs() const auto& core = Settings::CoreInfo[i]; QGridLayout* core_layout = new QGridLayout; core_layout->setAlignment(Qt::AlignTop); + core_layout->setSpacing(12); core_layout->setColumnStretch(0, 1); core_layout->setColumnStretch(1, 2); const std::vector& firmware_files = core.firmware_files; diff --git a/vendored/httplib.h b/vendored/httplib.h index e400a2b6..ad612413 100644 --- a/vendored/httplib.h +++ b/vendored/httplib.h @@ -3897,6 +3897,8 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, auto exceed_payload_max_length = false; if (is_chunked_transfer_encoding(x.headers)) { + // Hack: signal that we won't know the progress + progress(0, 0); ret = read_content_chunked(strm, x, out); } else if (!has_header(x.headers, "Content-Length")) { ret = read_content_without_length(strm, out);