diff --git a/src/duckstation-qt/autoupdaterdialog.cpp b/src/duckstation-qt/autoupdaterdialog.cpp index bd018be3d..c5bfe98b2 100644 --- a/src/duckstation-qt/autoupdaterdialog.cpp +++ b/src/duckstation-qt/autoupdaterdialog.cpp @@ -28,7 +28,7 @@ Log_SetChannel(AutoUpdaterDialog); #ifdef WIN32 #if defined(__has_include) && __has_include("scmversion/tag.h") #include "scmversion/tag.h" -#ifdef SCM_RELEASE_TAG +#ifdef SCM_RELEASE_TAGS #define AUTO_UPDATER_SUPPORTED #endif #endif @@ -36,11 +36,12 @@ Log_SetChannel(AutoUpdaterDialog); #ifdef AUTO_UPDATER_SUPPORTED -static constexpr char LATEST_TAG_URL[] = "https://api.github.com/repos/stenzek/duckstation/tags"; -static constexpr char LATEST_RELEASE_URL[] = - "https://api.github.com/repos/stenzek/duckstation/releases/tags/" SCM_RELEASE_TAG; -static constexpr char CHANGES_URL[] = "https://api.github.com/repos/stenzek/duckstation/compare/%s..." SCM_RELEASE_TAG; -static constexpr char UPDATE_ASSET_FILENAME[] = SCM_RELEASE_ASSET; +static constexpr char* LATEST_TAG_URL = "https://api.github.com/repos/stenzek/duckstation/tags"; +static constexpr char* LATEST_RELEASE_URL = "https://api.github.com/repos/stenzek/duckstation/releases/tags/%s"; +static constexpr char* CHANGES_URL = "https://api.github.com/repos/stenzek/duckstation/compare/%s...%s"; +static constexpr char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET; +static constexpr char* UPDATE_TAGS[] = SCM_RELEASE_TAGS; +static constexpr char* THIS_RELEASE_TAG = SCM_RELEASE_TAG; #endif @@ -69,6 +70,33 @@ bool AutoUpdaterDialog::isSupported() #endif } +QStringList AutoUpdaterDialog::getTagList() +{ +#ifdef AUTO_UPDATER_SUPPORTED + return QStringList(std::begin(UPDATE_TAGS), std::end(UPDATE_TAGS)); +#else + return QStringList(); +#endif +} + +std::string AutoUpdaterDialog::getDefaultTag() +{ +#ifdef AUTO_UPDATER_SUPPORTED + return THIS_RELEASE_TAG; +#else + return {}; +#endif +} + +std::string AutoUpdaterDialog::getCurrentUpdateTag() const +{ +#ifdef AUTO_UPDATER_SUPPORTED + return m_host_interface->GetStringSettingValue("AutoUpdater", "UpdateTag", THIS_RELEASE_TAG); +#else + return {}; +#endif +} + void AutoUpdaterDialog::reportError(const char* msg, ...) { std::va_list ap; @@ -86,7 +114,7 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_message) #ifdef AUTO_UPDATER_SUPPORTED connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestTagComplete); - QUrl url(QUrl::fromEncoded(QByteArray(LATEST_TAG_URL, sizeof(LATEST_TAG_URL) - 1))); + QUrl url(QUrl::fromEncoded(QByteArray(LATEST_TAG_URL))); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); m_network_access_mgr->get(request); @@ -100,7 +128,10 @@ void AutoUpdaterDialog::queueGetLatestRelease() #ifdef AUTO_UPDATER_SUPPORTED connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestReleaseComplete); - QUrl url(QUrl::fromEncoded(QByteArray(LATEST_RELEASE_URL, sizeof(LATEST_RELEASE_URL) - 1))); + SmallString url_string; + url_string.Format(LATEST_RELEASE_URL, getCurrentUpdateTag().c_str()); + + QUrl url(QUrl::fromEncoded(QByteArray(url_string))); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); m_network_access_mgr->get(request); @@ -110,6 +141,9 @@ void AutoUpdaterDialog::queueGetLatestRelease() void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply) { #ifdef AUTO_UPDATER_SUPPORTED + const std::string selected_tag(getCurrentUpdateTag()); + const QString selected_tag_qstr = QString::fromStdString(selected_tag); + // this might fail due to a lack of internet connection - in which case, don't spam the user with messages every time. m_network_access_mgr->disconnect(this); reply->deleteLater(); @@ -127,7 +161,7 @@ void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply) if (!val.isObject()) continue; - if (val["name"].toString() != QStringLiteral(SCM_RELEASE_TAG)) + if (val["name"].toString() != selected_tag_qstr) continue; m_latest_sha = val["commit"].toObject()["sha"].toString(); @@ -150,7 +184,7 @@ void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply) } if (m_display_messages) - reportError("latest release not found in JSON"); + reportError("%s release not found in JSON", selected_tag.c_str()); } else { @@ -228,7 +262,8 @@ void AutoUpdaterDialog::queueGetChanges() #ifdef AUTO_UPDATER_SUPPORTED connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getChangesComplete); - const std::string url_string(StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str)); + const std::string url_string( + StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag().c_str())); QUrl url(QUrl::fromEncoded(QByteArray(url_string.c_str(), static_cast(url_string.size())))); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); @@ -275,7 +310,7 @@ void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply) if (message.contains(QStringLiteral("[SAVEVERSION+]"))) update_will_break_save_states = true; - + if (message.contains(QStringLiteral("[SETTINGSVERSION+]"))) update_increases_settings_version = true; } diff --git a/src/duckstation-qt/autoupdaterdialog.h b/src/duckstation-qt/autoupdaterdialog.h index 6fede9e85..6f1f37ba7 100644 --- a/src/duckstation-qt/autoupdaterdialog.h +++ b/src/duckstation-qt/autoupdaterdialog.h @@ -1,5 +1,7 @@ #pragma once #include "ui_autoupdaterdialog.h" +#include +#include #include class QNetworkAccessManager; @@ -16,6 +18,8 @@ public: ~AutoUpdaterDialog(); static bool isSupported(); + static QStringList getTagList(); + static std::string getDefaultTag(); Q_SIGNALS: void updateCheckCompleted(); @@ -38,6 +42,7 @@ private Q_SLOTS: private: void reportError(const char* msg, ...); bool updateNeeded() const; + std::string getCurrentUpdateTag() const; #ifdef WIN32 bool processUpdate(const QByteArray& update_data); diff --git a/src/duckstation-qt/generalsettingswidget.cpp b/src/duckstation-qt/generalsettingswidget.cpp index adba02f9b..8cfb2b7ac 100644 --- a/src/duckstation-qt/generalsettingswidget.cpp +++ b/src/duckstation-qt/generalsettingswidget.cpp @@ -1,7 +1,9 @@ #include "generalsettingswidget.h" #include "autoupdaterdialog.h" #include "frontend-common/controller_interface.h" +#include "mainwindow.h" #include "qtutils.h" +#include "scmversion/scmversion.h" #include "settingsdialog.h" #include "settingwidgetbinder.h" @@ -113,17 +115,28 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW #endif if (AutoUpdaterDialog::isSupported()) { - QCheckBox* enableDiscordPresence = new QCheckBox(tr("Enable Automatic Update Check"), m_ui.groupBox_4); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, enableDiscordPresence, "AutoUpdater", + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true); - m_ui.formLayout_4->addWidget(enableDiscordPresence, current_row, current_col); - dialog->registerWidgetHelp(enableDiscordPresence, tr("Enable Automatic Update Check"), tr("Checked"), + dialog->registerWidgetHelp(m_ui.autoUpdateEnabled, tr("Enable Automatic Update Check"), tr("Checked"), tr("Automatically checks for updates to the program on startup. Updates can be deferred " "until later or skipped entirely.")); + + m_ui.autoUpdateTag->addItems(AutoUpdaterDialog::getTagList()); + SettingWidgetBinder::BindWidgetToStringSetting(m_host_interface, m_ui.autoUpdateTag, "AutoUpdater", "UpdateTag", + AutoUpdaterDialog::getDefaultTag()); + + m_ui.autoUpdateCurrentVersion->setText(tr("%1 (%2)").arg(g_scm_tag_str).arg(g_scm_date_str)); + connect(m_ui.checkForUpdates, &QPushButton::clicked, + [this]() { m_host_interface->getMainWindow()->checkForUpdates(true); }); current_col++; current_row += (current_col / 2); current_col %= 2; } + else + { + m_ui.verticalLayout->removeWidget(m_ui.automaticUpdaterGroup); + m_ui.automaticUpdaterGroup->hide(); + } } GeneralSettingsWidget::~GeneralSettingsWidget() = default; diff --git a/src/duckstation-qt/generalsettingswidget.ui b/src/duckstation-qt/generalsettingswidget.ui index a65442ff8..597e06b17 100644 --- a/src/duckstation-qt/generalsettingswidget.ui +++ b/src/duckstation-qt/generalsettingswidget.ui @@ -157,6 +157,70 @@ + + + + Automatic Updater + + + + + + Update Channel: + + + + + + + + + + Current Version: + + + + + + + + + + + + + + Enable Automatic Update Check + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Check for Updates... + + + + + + + + diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index f0f5cc3f5..a5bdc1e56 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -39,6 +39,8 @@ public Q_SLOTS: /// Updates debug menu visibility (hides if disabled). void updateDebugMenuVisibility(); + void checkForUpdates(bool display_message); + private Q_SLOTS: void reportError(const QString& message); void reportMessage(const QString& message); @@ -93,7 +95,6 @@ private Q_SLOTS: void onGameListSetCoverImageRequested(const GameListEntry* entry); void onCPUDebuggerClosed(); - void checkForUpdates(bool display_message); void onUpdateCheckComplete(); protected: