Qt: Add auto updater UI

This commit is contained in:
Connor McLaughlin 2022-04-04 21:27:23 +10:00 committed by refractionpcsx2
parent a289723f66
commit 41f1ec445f
11 changed files with 846 additions and 9 deletions

View File

@ -0,0 +1,542 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "AutoUpdaterDialog.h"
#include "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "pcsx2/SysForwardDefs.h"
#include "svnrev.h"
#include "updater/UpdaterExtractor.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
#include <QtCore/QProcess>
#include <QtCore/QString>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtWidgets/QDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressDialog>
// Logic to detect whether we can use the auto updater.
// We use tagged commit, because this gets set on nightly builds.
#if (defined(_WIN32)) && (defined(GIT_TAGGED_COMMIT) && GIT_TAGGED_COMMIT)
#define AUTO_UPDATER_SUPPORTED 1
#if defined(_WIN32)
#define UPDATE_PLATFORM_STR "Windows"
#if _M_SSE >= 0x500
#define UPDATE_ADDITIONAL_TAGS "AVX2"
#endif
#endif
#endif
#ifdef AUTO_UPDATER_SUPPORTED
#define LATEST_RELEASE_URL "https://api.pcsx2.net/v1/%1Releases?pageSize=1"
#define CHANGES_URL "https://api.github.com/repos/PCSX2/pcsx2/compare/%1...%2"
// Available release channels.
static const char* UPDATE_TAGS[] = {"stable", "nightly"};
// Bit annoying, because PCSX2_isReleaseVersion is a bool, but whatever.
#define SCM_RELEASE_TAG (PCSX2_isReleaseVersion ? "stable" : "nightly")
#endif
AutoUpdaterDialog::AutoUpdaterDialog(QWidget* parent /* = nullptr */)
: QDialog(parent)
{
m_network_access_mgr = new QNetworkAccessManager(this);
m_ui.setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
connect(m_ui.downloadAndInstall, &QPushButton::clicked, this, &AutoUpdaterDialog::downloadUpdateClicked);
connect(m_ui.skipThisUpdate, &QPushButton::clicked, this, &AutoUpdaterDialog::skipThisUpdateClicked);
connect(m_ui.remindMeLater, &QPushButton::clicked, this, &AutoUpdaterDialog::remindMeLaterClicked);
}
AutoUpdaterDialog::~AutoUpdaterDialog() = default;
bool AutoUpdaterDialog::isSupported()
{
#ifdef AUTO_UPDATER_SUPPORTED
return true;
#else
return false;
#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
}
QString AutoUpdaterDialog::getCurrentVersion()
{
return QStringLiteral(GIT_TAG);
}
QString AutoUpdaterDialog::getCurrentVersionDate()
{
// 20220403235450ll
const QDateTime current_build_date(QDateTime::fromString(QStringLiteral("%1").arg(SVN_REV), "yyyyMMddhhmmss"));
return current_build_date.toString();
}
QString AutoUpdaterDialog::getCurrentUpdateTag() const
{
#ifdef AUTO_UPDATER_SUPPORTED
return QString::fromStdString(QtHost::GetBaseStringSettingValue("AutoUpdater", "UpdateTag", THIS_RELEASE_TAG));
#else
return QString();
#endif
}
void AutoUpdaterDialog::reportError(const char* msg, ...)
{
std::va_list ap;
va_start(ap, msg);
std::string full_msg = StringUtil::StdStringFromFormatV(msg, ap);
va_end(ap);
QMessageBox::critical(this, tr("Updater Error"), QString::fromStdString(full_msg));
}
void AutoUpdaterDialog::queueUpdateCheck(bool display_message)
{
m_display_messages = display_message;
#ifdef AUTO_UPDATER_SUPPORTED
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestReleaseComplete);
QUrl url(QStringLiteral(LATEST_RELEASE_URL).arg(getCurrentUpdateTag()));
QNetworkRequest request(url);
m_network_access_mgr->get(request);
#else
emit updateCheckCompleted();
#endif
}
void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
{
#ifdef AUTO_UPDATER_SUPPORTED
// 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();
bool found_update_info = false;
if (reply->error() == QNetworkReply::NoError)
{
const QByteArray reply_json(reply->readAll());
QJsonParseError parse_error;
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
if (doc.isObject())
{
const QJsonObject doc_object(doc.object());
const QJsonArray data_array(doc_object["data"].toArray());
if (!data_array.isEmpty())
{
// just take the first one, that's all we requested anyway
const QJsonObject data_object(data_array.first().toObject());
const QJsonObject assets_object(data_object["assets"].toObject());
const QJsonArray platform_array(assets_object[UPDATE_PLATFORM_STR].toArray());
if (!platform_array.isEmpty())
{
// search for the correct file
for (const QJsonValue& asset_value : platform_array)
{
const QJsonObject asset_object(asset_value.toObject());
const QJsonArray additional_tags_array(asset_object["additionalTags"].toArray());
bool is_matching_asset = false;
bool is_qt_asset = false;
for (const QJsonValue& additional_tag : additional_tags_array)
{
const QString additional_tag_str(additional_tag.toString());
if (additional_tag_str == QStringLiteral("symbols"))
{
// we're not interested in symbols downloads
is_matching_asset = false;
break;
}
else if (additional_tag_str == QStringLiteral("Qt"))
{
// found a qt build
is_qt_asset = true;
}
// is this the right variant?
if (additional_tag_str == QStringLiteral(UPDATE_ADDITIONAL_TAGS))
{
// yep! found the right one. but keep checking in case it's symbols.
is_matching_asset = true;
}
}
if (!is_qt_asset || !is_matching_asset)
{
// skip this asset
continue;
}
m_latest_version = data_object["version"].toString();
m_latest_version_timestamp = QDateTime::fromString(data_object["publishedAt"].toString(), QStringLiteral("yyyy-MM-ddThh:mm:ss.zzzZ"));
m_download_url = asset_object["url"].toString();
if (!m_latest_version.isEmpty() && !m_download_url.isEmpty())
{
found_update_info = true;
break;
}
else
{
reportError("missing version/download info");
}
}
if (!found_update_info)
reportError("matching asset not found");
}
else
{
reportError("platform not found in assets array");
}
}
else
{
reportError("data is not an array");
}
}
else
{
reportError("JSON is not an object");
}
}
else
{
reportError("Failed to download latest release info: %d", static_cast<int>(reply->error()));
}
if (found_update_info)
checkIfUpdateNeeded();
emit updateCheckCompleted();
#endif
}
void AutoUpdaterDialog::queueGetChanges()
{
#ifdef AUTO_UPDATER_SUPPORTED
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getChangesComplete);
const QString url_string(QStringLiteral(CHANGES_URL).arg(GIT_HASH).arg(m_latest_version));
QUrl url(url_string);
QNetworkRequest request(url);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
#endif
m_network_access_mgr->get(request);
#endif
}
void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
{
#ifdef AUTO_UPDATER_SUPPORTED
m_network_access_mgr->disconnect(this);
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError)
{
const QByteArray reply_json(reply->readAll());
QJsonParseError parse_error;
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
if (doc.isObject())
{
const QJsonObject doc_object(doc.object());
QString changes_html = tr("<h2>Changes:</h2>");
changes_html += QStringLiteral("<ul>");
const QJsonArray commits(doc_object["commits"].toArray());
bool update_will_break_save_states = false;
bool update_increases_settings_version = false;
for (const QJsonValue& commit : commits)
{
const QJsonObject commit_obj(commit["commit"].toObject());
QString message = commit_obj["message"].toString();
QString author = commit_obj["author"].toObject()["name"].toString();
const int first_line_terminator = message.indexOf('\n');
if (first_line_terminator >= 0)
message.remove(first_line_terminator, message.size() - first_line_terminator);
if (!message.isEmpty())
{
changes_html +=
QStringLiteral("<li>%1 <i>(%2)</i></li>").arg(message.toHtmlEscaped()).arg(author.toHtmlEscaped());
}
if (message.contains(QStringLiteral("[SAVEVERSION+]")))
update_will_break_save_states = true;
if (message.contains(QStringLiteral("[SETTINGSVERSION+]")))
update_increases_settings_version = true;
}
changes_html += "</ul>";
if (update_will_break_save_states)
{
changes_html.prepend(tr("<h2>Save State Warning</h2><p>Installing this update will make your save states "
"<b>incompatible</b>. Please ensure you have saved your games to memory card "
"before installing this update or you will lose progress.</p>"));
}
if (update_increases_settings_version)
{
changes_html.prepend(
tr("<h2>Settings Warning</h2><p>Installing this update will reset your program configuration. Please note "
"that you will have to reconfigure your settings after this update.</p>"));
}
changes_html += tr("<h4>Installing this update will download %1 MB through your internet connection.</h4>")
.arg(static_cast<double>(m_download_size) / 1000000.0, 0, 'f', 2);
m_ui.updateNotes->setText(changes_html);
}
else
{
reportError("Change list JSON is not an object");
}
}
else
{
reportError("Failed to download change list: %d", static_cast<int>(reply->error()));
}
#endif
m_ui.downloadAndInstall->setEnabled(true);
}
void AutoUpdaterDialog::downloadUpdateClicked()
{
QUrl url(m_download_url);
QNetworkRequest request(url);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
#endif
QNetworkReply* reply = m_network_access_mgr->get(request);
QProgressDialog progress(tr("Downloading %1...").arg(m_download_url), tr("Cancel"), 0, 1);
progress.setWindowTitle(tr("Automatic Updater"));
progress.setWindowIcon(windowIcon());
progress.setAutoClose(false);
connect(reply, &QNetworkReply::downloadProgress, [&progress](quint64 received, quint64 total) {
progress.setRange(0, static_cast<int>(total));
progress.setValue(static_cast<int>(received));
});
connect(m_network_access_mgr, &QNetworkAccessManager::finished, [this, &progress](QNetworkReply* reply) {
m_network_access_mgr->disconnect();
if (reply->error() != QNetworkReply::NoError)
{
reportError("Download failed: %s", reply->errorString().toUtf8().constData());
progress.done(-1);
return;
}
const QByteArray data = reply->readAll();
if (data.isEmpty())
{
reportError("Download failed: Update is empty");
progress.done(-1);
return;
}
if (processUpdate(data))
progress.done(1);
else
progress.done(-1);
});
const int result = progress.exec();
if (result == 0)
{
// cancelled
reply->abort();
}
else if (result == 1)
{
// updater started. since we're a modal on the main window, we have to queue this.
QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit, Qt::QueuedConnection);
done(0);
}
reply->deleteLater();
}
void AutoUpdaterDialog::checkIfUpdateNeeded()
{
const QString last_checked_version(
QString::fromStdString(QtHost::GetBaseStringSettingValue("AutoUpdater", "LastVersion")));
Console.WriteLn(Color_StrongGreen, "Current version: %s", GIT_TAG);
Console.WriteLn(Color_StrongYellow, "Latest SHA: %s", m_latest_version.toUtf8().constData());
Console.WriteLn(Color_StrongOrange, "Last Checked SHA: %s", last_checked_version.toUtf8().constData());
if (m_latest_version == GIT_TAG || m_latest_version == last_checked_version)
{
Console.WriteLn(Color_StrongGreen, "No update needed.");
if (m_display_messages)
{
QMessageBox::information(this, tr("Automatic Updater"),
tr("No updates are currently available. Please try again later."));
}
return;
}
Console.WriteLn(Color_StrongRed, "Update needed.");
m_ui.currentVersion->setText(tr("Current Version: %1 (%2)").arg(getCurrentVersion()).arg(getCurrentVersionDate()));
m_ui.newVersion->setText(tr("New Version: %1 (%2)").arg(m_latest_version).arg(m_latest_version_timestamp.toString()));
m_ui.updateNotes->setText(tr("Loading..."));
queueGetChanges();
exec();
}
void AutoUpdaterDialog::skipThisUpdateClicked()
{
QtHost::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_version.toUtf8().constData());
done(0);
}
void AutoUpdaterDialog::remindMeLaterClicked()
{
done(0);
}
#ifdef _WIN32
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
{
const QString update_directory = QCoreApplication::applicationDirPath();
const QString update_zip_path = QStringLiteral("%1" FS_OSPATH_SEPARATOR_STR "%2").arg(update_directory).arg(UPDATER_ARCHIVE_NAME);
const QString updater_path = QStringLiteral("%1" FS_OSPATH_SEPARATOR_STR "%2").arg(update_directory).arg(UPDATER_EXECUTABLE);
Q_ASSERT(!update_zip_path.isEmpty() && !updater_path.isEmpty() && !update_directory.isEmpty());
if ((QFile::exists(update_zip_path) && !QFile::remove(update_zip_path)) ||
(QFile::exists(updater_path) && !QFile::remove(updater_path)))
{
reportError("Removing existing update zip/updater failed");
return false;
}
{
QFile update_zip_file(update_zip_path);
if (!update_zip_file.open(QIODevice::WriteOnly) || update_zip_file.write(update_data) != update_data.size())
{
reportError("Writing update zip to '%s' failed", update_zip_path.toUtf8().constData());
return false;
}
update_zip_file.close();
}
std::string updater_extract_error;
if (!ExtractUpdater(update_zip_path.toUtf8().constData(), updater_path.toUtf8().constData(), &updater_extract_error))
{
reportError("Extracting updater failed: %s", updater_extract_error.c_str());
return false;
}
if (!doUpdate(update_zip_path, updater_path, update_directory))
{
reportError("Launching updater failed");
return false;
}
return true;
}
bool AutoUpdaterDialog::doUpdate(const QString& zip_path, const QString& updater_path, const QString& destination_path)
{
const QString program_path = QCoreApplication::applicationFilePath();
if (program_path.isEmpty())
{
reportError("Failed to get current application path");
return false;
}
QStringList arguments;
arguments << QString::number(QCoreApplication::applicationPid());
arguments << destination_path;
arguments << zip_path;
arguments << program_path;
// this will leak, but not sure how else to handle it...
QProcess* updater_process = new QProcess();
updater_process->setProgram(updater_path);
updater_process->setArguments(arguments);
updater_process->start(QIODevice::NotOpen);
if (!updater_process->waitForStarted())
{
reportError("Failed to start updater");
return false;
}
return true;
}
#else
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
{
return false;
}
#endif

View File

@ -0,0 +1,78 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ui_AutoUpdaterDialog.h"
#include <string>
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
#include <QtWidgets/QDialog>
class QNetworkAccessManager;
class QNetworkReply;
class AutoUpdaterDialog final : public QDialog
{
Q_OBJECT
public:
explicit AutoUpdaterDialog(QWidget* parent = nullptr);
~AutoUpdaterDialog();
static bool isSupported();
static QStringList getTagList();
static std::string getDefaultTag();
static QString getCurrentVersion();
static QString getCurrentVersionDate();
Q_SIGNALS:
void updateCheckCompleted();
public Q_SLOTS:
void queueUpdateCheck(bool display_message);
private Q_SLOTS:
void getLatestReleaseComplete(QNetworkReply* reply);
void queueGetChanges();
void getChangesComplete(QNetworkReply* reply);
void downloadUpdateClicked();
void skipThisUpdateClicked();
void remindMeLaterClicked();
private:
void reportError(const char* msg, ...);
void checkIfUpdateNeeded();
QString getCurrentUpdateTag() const;
#ifdef _WIN32
bool processUpdate(const QByteArray& update_data);
bool doUpdate(const QString& zip_path, const QString& updater_path, const QString& destination_path);
#else
bool processUpdate(const QByteArray& update_data);
#endif
Ui::AutoUpdaterDialog m_ui;
QNetworkAccessManager* m_network_access_mgr = nullptr;
QString m_latest_version;
QDateTime m_latest_version_timestamp;
QString m_download_url;
int m_download_size = 0;
bool m_display_messages = false;
bool m_update_will_break_save_states = false;
};

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AutoUpdaterDialog</class>
<widget class="QDialog" name="AutoUpdaterDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>474</height>
</rect>
</property>
<property name="windowTitle">
<string>Automatic Updater</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources/resources.qrc">:/icons/update.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Update Available</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="currentVersion">
<property name="text">
<string>Current Version: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="newVersion">
<property name="text">
<string>New Version: </string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="updateNotes"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="downloadAndInstall">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Download and Install...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="skipThisUpdate">
<property name="text">
<string>Skip This Update</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="remindMeLater">
<property name="text">
<string>Remind Me Later</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -214,6 +214,8 @@ int main(int argc, char* argv[])
if (autoboot)
g_emu_thread->startVM(std::move(autoboot));
else
main_window->startupUpdateCheck();
const int result = app.exec();

View File

@ -32,6 +32,7 @@
#include "pcsx2/PerformanceMetrics.h"
#include "AboutDialog.h"
#include "AutoUpdaterDialog.h"
#include "DisplayWidget.h"
#include "EmuThread.h"
#include "GameList/GameListRefreshThread.h"
@ -1065,7 +1066,66 @@ void MainWindow::onAboutActionTriggered()
about.exec();
}
void MainWindow::onCheckForUpdatesActionTriggered() {}
void MainWindow::onCheckForUpdatesActionTriggered()
{
// Wipe out the last version, that way it displays the update if we've previously skipped it.
QtHost::RemoveBaseSettingValue("AutoUpdater", "LastVersion");
checkForUpdates(true);
}
void MainWindow::checkForUpdates(bool display_message)
{
if (!AutoUpdaterDialog::isSupported())
{
if (display_message)
{
QMessageBox mbox(this);
mbox.setWindowTitle(tr("Updater Error"));
mbox.setTextFormat(Qt::RichText);
QString message;
#ifdef _WIN32
message =
tr("<p>Sorry, you are trying to update a PCSX2 version which is not an official GitHub release. To "
"prevent incompatibilities, the auto-updater is only enabled on official builds.</p>"
"<p>To obtain an official build, please download from the link below:</p>"
"<p><a href=\"https://pcsx2.net/downloads/\">https://pcsx2.net/downloads/</a></p>");
#else
message = tr("Automatic updating is not supported on the current platform.");
#endif
mbox.setText(message);
mbox.setIcon(QMessageBox::Critical);
mbox.exec();
}
return;
}
if (m_auto_updater_dialog)
return;
m_auto_updater_dialog = new AutoUpdaterDialog(this);
connect(m_auto_updater_dialog, &AutoUpdaterDialog::updateCheckCompleted, this, &MainWindow::onUpdateCheckComplete);
m_auto_updater_dialog->queueUpdateCheck(display_message);
}
void MainWindow::onUpdateCheckComplete()
{
if (!m_auto_updater_dialog)
return;
m_auto_updater_dialog->deleteLater();
m_auto_updater_dialog = nullptr;
}
void MainWindow::startupUpdateCheck()
{
if (!QtHost::GetBaseBoolSettingValue("AutoUpdater", "CheckAtStartup", true))
return;
checkForUpdates(false);
}
void MainWindow::onToolsOpenDataDirectoryTriggered()
{

View File

@ -26,6 +26,7 @@
class QProgressBar;
class AutoUpdaterDialog;
class DisplayWidget;
class DisplayContainer;
class GameListWidget;
@ -78,11 +79,13 @@ public:
void initialize();
void connectVMThreadSignals(EmuThread* thread);
void startupUpdateCheck();
/// Locks the VM by pausing it, while a popup dialog is displayed.
VMLock pauseAndLockVM();
public Q_SLOTS:
void checkForUpdates(bool display_message);
void refreshGameList(bool invalidate_cache);
void invalidateSaveStateCache();
void reportError(const QString& title, const QString& message);
@ -91,6 +94,8 @@ public Q_SLOTS:
void requestExit();
private Q_SLOTS:
void onUpdateCheckComplete();
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
void displayResizeRequested(qint32 width, qint32 height);
@ -203,6 +208,7 @@ private:
SettingsDialog* m_settings_dialog = nullptr;
ControllerSettingsDialog* m_controller_settings_dialog = nullptr;
AutoUpdaterDialog* m_auto_updater_dialog = nullptr;
QProgressBar* m_status_progress_widget = nullptr;
QLabel* m_status_gs_widget = nullptr;

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "InterfaceSettingsWidget.h"
#include "AutoUpdaterDialog.h"
#include "MainWindow.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
@ -62,20 +63,19 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
dialog->registerWidgetHelp(m_ui.discordPresence, tr("Enable Discord Presence"), tr("Unchecked"),
tr("Shows the game you are currently playing as part of your profile in Discord."));
if (true)
if (AutoUpdaterDialog::isSupported())
{
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true);
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_ui.autoUpdateTag, "AutoUpdater", "UpdateTag",
// AutoUpdaterDialog::getDefaultTag());
m_ui.autoUpdateTag->addItems(AutoUpdaterDialog::getTagList());
SettingWidgetBinder::BindWidgetToStringSetting(sif, 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); });
m_ui.autoUpdateCurrentVersion->setText(tr("%1 (%2)").arg(AutoUpdaterDialog::getCurrentVersion()).arg(AutoUpdaterDialog::getCurrentVersionDate()));
connect(m_ui.checkForUpdates, &QPushButton::clicked, this, []() { g_main_window->checkForUpdates(true); });
}
else
{

View File

@ -42,6 +42,7 @@
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\lzma\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<!-- Needed for moc pch -->
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList</AdditionalIncludeDirectories>
@ -141,6 +142,9 @@
<ProjectReference Include="$(SolutionDir)3rdparty\glad\glad.vcxproj">
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
</ProjectReference>
<ProjectReference Include="..\3rdparty\lzma\lzma.vcxproj">
<Project>{a4323327-3f2b-4271-83d9-7f9a3c66b6b2}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest" />
@ -171,6 +175,7 @@
<ClCompile Include="GameList\GameListRefreshThread.cpp" />
<ClCompile Include="GameList\GameListWidget.cpp" />
<ClCompile Include="AboutDialog.cpp" />
<ClCompile Include="AutoUpdaterDialog.cpp" />
<ClCompile Include="DisplayWidget.cpp" />
<ClCompile Include="QtHost.cpp" />
<ClCompile Include="Main.cpp" />
@ -212,6 +217,7 @@
<ClInclude Include="QtHost.h" />
<ClInclude Include="PrecompiledHeader.h" />
<QtMoc Include="AboutDialog.h" />
<QtMoc Include="AutoUpdaterDialog.h" />
<QtMoc Include="EmuThread.h" />
<QtMoc Include="MainWindow.h" />
<QtMoc Include="DisplayWidget.h" />
@ -246,6 +252,7 @@
<ClCompile Include="$(IntDir)GameList\moc_GameListRefreshThread.cpp" />
<ClCompile Include="$(IntDir)GameList\moc_GameListWidget.cpp" />
<ClCompile Include="$(IntDir)moc_AboutDialog.cpp" />
<ClCompile Include="$(IntDir)moc_AutoUpdaterDialog.cpp" />
<ClCompile Include="$(IntDir)moc_DisplayWidget.cpp" />
<ClCompile Include="$(IntDir)moc_EmuThread.cpp" />
<ClCompile Include="$(IntDir)moc_MainWindow.cpp" />
@ -317,6 +324,9 @@
<QtUi Include="Settings\GameSummaryWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="AutoUpdaterDialog.ui">
<FileType>Document</FileType>
</QtUi>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />

View File

@ -209,6 +209,10 @@
<ClCompile Include="Settings\HddCreateQt.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="AutoUpdaterDialog.cpp" />
<ClCompile Include="$(IntDir)moc_AutoUpdaterDialog.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -298,6 +302,7 @@
<QtMoc Include="Settings\DEV9SettingsWidget.h">
<Filter>Settings</Filter>
</QtMoc>
<QtMoc Include="AutoUpdaterDialog.h" />
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@ -366,5 +371,6 @@
<QtUi Include="GameList\EmptyGameListWidget.ui">
<Filter>GameList</Filter>
</QtUi>
<QtUi Include="AutoUpdaterDialog.ui" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -235,6 +235,7 @@
<file>icons/star-3.png</file>
<file>icons/star-4.png</file>
<file>icons/star-5.png</file>
<file>icons/update.png</file>
<file>icons/white/16/artboard-2-line.png</file>
<file>icons/white/16/book-open-line.png</file>
<file>icons/white/16/brush-line.png</file>