Qt: Improve updater

This commit is contained in:
spycrab 2018-03-22 12:20:15 +01:00
parent 2e92d95888
commit ee3d64145c
11 changed files with 240 additions and 42 deletions

View File

@ -117,6 +117,7 @@ set(SRCS
TAS/Shared.cpp TAS/Shared.cpp
TAS/StickWidget.cpp TAS/StickWidget.cpp
TAS/IRWidget.cpp TAS/IRWidget.cpp
Updater.cpp
) )
list(APPEND LIBS core uicommon) list(APPEND LIBS core uicommon)

View File

@ -127,6 +127,7 @@
<QtMoc Include="Settings\AdvancedPane.h" /> <QtMoc Include="Settings\AdvancedPane.h" />
<QtMoc Include="Settings\GeneralPane.h" /> <QtMoc Include="Settings\GeneralPane.h" />
<QtMoc Include="ToolBar.h" /> <QtMoc Include="ToolBar.h" />
<QtMoc Include="Updater.h" />
</ItemGroup> </ItemGroup>
<!--TODO figure out how to get QtMoc to add outputs to ClCompile's inputs...--> <!--TODO figure out how to get QtMoc to add outputs to ClCompile's inputs...-->
<ItemGroup> <ItemGroup>
@ -196,6 +197,7 @@
<ClCompile Include="$(QtMocOutPrefix)ToolBar.cpp" /> <ClCompile Include="$(QtMocOutPrefix)ToolBar.cpp" />
<ClCompile Include="$(QtMocOutPrefix)WatchWidget.cpp" /> <ClCompile Include="$(QtMocOutPrefix)WatchWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)USBDeviceAddToWhitelistDialog.cpp" /> <ClCompile Include="$(QtMocOutPrefix)USBDeviceAddToWhitelistDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)Updater.cpp" />
<ClCompile Include="AboutDialog.cpp" /> <ClCompile Include="AboutDialog.cpp" />
<ClCompile Include="Config\CheatCodeEditor.cpp" /> <ClCompile Include="Config\CheatCodeEditor.cpp" />
<ClCompile Include="Config\ARCodeWidget.cpp" /> <ClCompile Include="Config\ARCodeWidget.cpp" />
@ -294,6 +296,7 @@
<ClCompile Include="Settings\USBDeviceAddToWhitelistDialog.cpp" /> <ClCompile Include="Settings\USBDeviceAddToWhitelistDialog.cpp" />
<ClCompile Include="ToolBar.cpp" /> <ClCompile Include="ToolBar.cpp" />
<ClCompile Include="Translation.cpp" /> <ClCompile Include="Translation.cpp" />
<ClCompile Include="Updater.cpp" />
<ClCompile Include="WiiUpdate.cpp" /> <ClCompile Include="WiiUpdate.cpp" />
</ItemGroup> </ItemGroup>
<!--Put standard C/C++ headers here. Headers that are listed in the QtMoc ItemGroup must NOT be listed here.--> <!--Put standard C/C++ headers here. Headers that are listed in the QtMoc ItemGroup must NOT be listed here.-->

View File

@ -21,7 +21,7 @@
#include "DolphinQt2/Resources.h" #include "DolphinQt2/Resources.h"
#include "DolphinQt2/Settings.h" #include "DolphinQt2/Settings.h"
#include "DolphinQt2/Translation.h" #include "DolphinQt2/Translation.h"
#include "UICommon/AutoUpdate.h" #include "DolphinQt2/Updater.h"
#include "UICommon/CommandLineParse.h" #include "UICommon/CommandLineParse.h"
#include "UICommon/UICommon.h" #include "UICommon/UICommon.h"
@ -63,35 +63,6 @@ static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no
}); });
} }
// TODO: This should be replaced with something in a background thread, it performs a blocking
// HTTP query. It also needs a proper UI, and many other things. But right now it needs to be
// manually enabled through INI, so all these problems are ignored :)
class QtAutoUpdateChecker : public AutoUpdateChecker
{
public:
explicit QtAutoUpdateChecker(QWidget* parent) : m_parent(parent) {}
protected:
void OnUpdateAvailable(const NewVersionInformation& info) override
{
QMessageBox prompt(m_parent);
prompt.setIcon(QMessageBox::Question);
prompt.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
prompt.setText(QString::fromUtf8("Update Dolphin to version %1?")
.arg(QString::fromStdString(info.new_shortrev)));
const int answer = prompt.exec();
if (answer == QMessageBox::Yes)
{
TriggerUpdate(info);
m_parent->close();
}
}
private:
QWidget* m_parent;
};
// N.B. On Windows, this should be called from WinMain. Link against qtmain and specify // N.B. On Windows, this should be called from WinMain. Link against qtmain and specify
// /SubSystem:Windows // /SubSystem:Windows
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -188,8 +159,8 @@ int main(int argc, char* argv[])
} }
#endif #endif
QtAutoUpdateChecker updater(&win); auto* updater = new Updater(&win);
updater.CheckForUpdate(); updater->start();
retval = app.exec(); retval = app.exec();
} }

View File

@ -312,3 +312,18 @@ QFont Settings::GetDebugFont() const
return QSettings().value(QStringLiteral("debugger/font"), default_font).value<QFont>(); return QSettings().value(QStringLiteral("debugger/font"), default_font).value<QFont>();
} }
void Settings::SetAutoUpdateTrack(const QString& mode)
{
if (mode == GetAutoUpdateTrack())
return;
SConfig::GetInstance().m_auto_update_track = mode.toStdString();
emit AutoUpdateTrackChanged(mode);
}
QString Settings::GetAutoUpdateTrack() const
{
return QString::fromStdString(SConfig::GetInstance().m_auto_update_track);
}

View File

@ -98,6 +98,10 @@ public:
QFont GetDebugFont() const; QFont GetDebugFont() const;
void SetDebugFont(QFont font); void SetDebugFont(QFont font);
// Auto-Update
QString GetAutoUpdateTrack() const;
void SetAutoUpdateTrack(const QString& mode);
// Other // Other
GameListModel* GetGameListModel() const; GameListModel* GetGameListModel() const;
signals: signals:
@ -119,6 +123,7 @@ signals:
void CodeVisibilityChanged(bool visible); void CodeVisibilityChanged(bool visible);
void DebugModeToggled(bool enabled); void DebugModeToggled(bool enabled);
void DebugFontChanged(QFont font); void DebugFontChanged(QFont font);
void AutoUpdateTrackChanged(const QString& mode);
private: private:
bool m_controller_state_needed = false; bool m_controller_state_needed = false;

View File

@ -20,6 +20,17 @@
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "DolphinQt2/Settings.h" #include "DolphinQt2/Settings.h"
#include "UICommon/AutoUpdate.h"
constexpr int AUTO_UPDATE_DISABLE_INDEX = 0;
constexpr int AUTO_UPDATE_STABLE_INDEX = 1;
constexpr int AUTO_UPDATE_BETA_INDEX = 2;
constexpr int AUTO_UPDATE_DEV_INDEX = 3;
constexpr const char* AUTO_UPDATE_DISABLE_STRING = "";
constexpr const char* AUTO_UPDATE_STABLE_STRING = "stable";
constexpr const char* AUTO_UPDATE_BETA_STRING = "beta";
constexpr const char* AUTO_UPDATE_DEV_STRING = "dev";
GeneralPane::GeneralPane(QWidget* parent) : QWidget(parent) GeneralPane::GeneralPane(QWidget* parent) : QWidget(parent)
{ {
@ -34,6 +45,10 @@ void GeneralPane::CreateLayout()
m_main_layout = new QVBoxLayout; m_main_layout = new QVBoxLayout;
// Create layout here // Create layout here
CreateBasic(); CreateBasic();
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
CreateAutoUpdate();
#if defined(USE_ANALYTICS) && USE_ANALYTICS #if defined(USE_ANALYTICS) && USE_ANALYTICS
CreateAnalytics(); CreateAnalytics();
#endif #endif
@ -48,6 +63,16 @@ void GeneralPane::ConnectLayout()
{ {
connect(m_checkbox_dualcore, &QCheckBox::clicked, this, &GeneralPane::OnSaveConfig); connect(m_checkbox_dualcore, &QCheckBox::clicked, this, &GeneralPane::OnSaveConfig);
connect(m_checkbox_cheats, &QCheckBox::clicked, this, &GeneralPane::OnSaveConfig); connect(m_checkbox_cheats, &QCheckBox::clicked, this, &GeneralPane::OnSaveConfig);
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
{
connect(m_combobox_update_track,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&GeneralPane::OnSaveConfig);
connect(&Settings::Instance(), &Settings::AutoUpdateTrackChanged, this,
&GeneralPane::LoadConfig);
}
// Advanced // Advanced
connect(m_combobox_speedlimit, connect(m_combobox_speedlimit,
static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::activated), static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::activated),
@ -96,6 +121,22 @@ void GeneralPane::CreateBasic()
speed_limit_layout->addRow(tr("&Speed Limit:"), m_combobox_speedlimit); speed_limit_layout->addRow(tr("&Speed Limit:"), m_combobox_speedlimit);
} }
void GeneralPane::CreateAutoUpdate()
{
auto* auto_update_group = new QGroupBox(tr("Auto Update Settings"));
auto* layout = new QFormLayout;
auto_update_group->setLayout(layout);
m_main_layout->addWidget(auto_update_group);
m_combobox_update_track = new QComboBox(this);
layout->addRow(tr("&Auto Update:"), m_combobox_update_track);
for (const QString& option : {tr("Don't Update"), tr("Stable (once a year)"),
tr("Beta (once a month)"), tr("Dev (multiple times a day)")})
m_combobox_update_track->addItem(option);
}
#if defined(USE_ANALYTICS) && USE_ANALYTICS #if defined(USE_ANALYTICS) && USE_ANALYTICS
void GeneralPane::CreateAnalytics() void GeneralPane::CreateAnalytics()
{ {
@ -135,6 +176,20 @@ void GeneralPane::CreateAdvanced()
void GeneralPane::LoadConfig() void GeneralPane::LoadConfig()
{ {
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
{
const auto track = Settings::Instance().GetAutoUpdateTrack().toStdString();
if (track == AUTO_UPDATE_DISABLE_STRING)
m_combobox_update_track->setCurrentIndex(AUTO_UPDATE_DISABLE_INDEX);
else if (track == AUTO_UPDATE_STABLE_STRING)
m_combobox_update_track->setCurrentIndex(AUTO_UPDATE_STABLE_INDEX);
else if (track == AUTO_UPDATE_BETA_STRING)
m_combobox_update_track->setCurrentIndex(AUTO_UPDATE_BETA_INDEX);
else
m_combobox_update_track->setCurrentIndex(AUTO_UPDATE_DEV_INDEX);
}
#if defined(USE_ANALYTICS) && USE_ANALYTICS #if defined(USE_ANALYTICS) && USE_ANALYTICS
m_checkbox_enable_analytics->setChecked(SConfig::GetInstance().m_analytics_enabled); m_checkbox_enable_analytics->setChecked(SConfig::GetInstance().m_analytics_enabled);
#endif #endif
@ -164,8 +219,37 @@ void GeneralPane::LoadConfig()
} }
} }
static QString UpdateTrackFromIndex(int index)
{
QString value;
switch (index)
{
case AUTO_UPDATE_DISABLE_INDEX:
value = QString::fromStdString(AUTO_UPDATE_DISABLE_STRING);
break;
case AUTO_UPDATE_STABLE_INDEX:
value = QString::fromStdString(AUTO_UPDATE_STABLE_STRING);
break;
case AUTO_UPDATE_BETA_INDEX:
value = QString::fromStdString(AUTO_UPDATE_BETA_STRING);
break;
case AUTO_UPDATE_DEV_INDEX:
value = QString::fromStdString(AUTO_UPDATE_DEV_STRING);
break;
}
return value;
}
void GeneralPane::OnSaveConfig() void GeneralPane::OnSaveConfig()
{ {
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
{
Settings::Instance().SetAutoUpdateTrack(
UpdateTrackFromIndex(m_combobox_update_track->currentIndex()));
}
#if defined(USE_ANALYTICS) && USE_ANALYTICS #if defined(USE_ANALYTICS) && USE_ANALYTICS
SConfig::GetInstance().m_analytics_enabled = m_checkbox_enable_analytics->isChecked(); SConfig::GetInstance().m_analytics_enabled = m_checkbox_enable_analytics->isChecked();
#endif #endif

View File

@ -24,6 +24,7 @@ private:
void CreateLayout(); void CreateLayout();
void ConnectLayout(); void ConnectLayout();
void CreateBasic(); void CreateBasic();
void CreateAutoUpdate();
void CreateAdvanced(); void CreateAdvanced();
void LoadConfig(); void LoadConfig();
@ -32,6 +33,7 @@ private:
// Widgets // Widgets
QVBoxLayout* m_main_layout; QVBoxLayout* m_main_layout;
QComboBox* m_combobox_speedlimit; QComboBox* m_combobox_speedlimit;
QComboBox* m_combobox_update_track;
QCheckBox* m_checkbox_dualcore; QCheckBox* m_checkbox_dualcore;
QCheckBox* m_checkbox_cheats; QCheckBox* m_checkbox_cheats;
QLabel* m_label_speedlimit; QLabel* m_label_speedlimit;

View File

@ -0,0 +1,88 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt2/Updater.h"
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QTextBrowser>
#include <QVBoxLayout>
#include "Common/Version.h"
#include "DolphinQt2/QtUtils/RunOnObject.h"
#include "DolphinQt2/Settings.h"
Updater::Updater(QWidget* parent) : m_parent(parent)
{
connect(this, &QThread::finished, this, &QObject::deleteLater);
}
void Updater::run()
{
CheckForUpdate();
}
void Updater::OnUpdateAvailable(const NewVersionInformation& info)
{
bool later = false;
int choice = RunOnObject(m_parent, [&] {
QDialog* dialog = new QDialog(m_parent);
dialog->setWindowTitle(tr("Update available"));
auto* label = new QLabel(
tr("<h2>A new version of Dolphin is available!</h2>Dolphin %1 is available for "
"download. "
"You are running %2.<br> Would you like to update?<br><h4>Release Notes:</h4>")
.arg(QString::fromStdString(info.new_shortrev))
.arg(QString::fromStdString(Common::scm_desc_str)));
label->setTextFormat(Qt::RichText);
auto* changelog = new QTextBrowser;
changelog->setHtml(QString::fromStdString(info.changelog_html));
changelog->setOpenExternalLinks(true);
changelog->setMinimumWidth(400);
auto* update_later_check = new QCheckBox(tr("Update after closing Dolphin"));
connect(update_later_check, &QCheckBox::toggled, [&](bool checked) { later = checked; });
auto* buttons = new QDialogButtonBox;
auto* never_btn =
buttons->addButton(tr("Never Auto-Update"), QDialogButtonBox::DestructiveRole);
buttons->addButton(tr("Remind Me Later"), QDialogButtonBox::RejectRole);
buttons->addButton(tr("Install Update"), QDialogButtonBox::AcceptRole);
auto* layout = new QVBoxLayout;
dialog->setLayout(layout);
layout->addWidget(label);
layout->addWidget(changelog);
layout->addWidget(update_later_check);
layout->addWidget(buttons);
connect(never_btn, &QPushButton::pressed, [dialog] {
Settings::Instance().SetAutoUpdateTrack(QStringLiteral(""));
dialog->reject();
});
connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
return dialog->exec();
});
if (choice == QDialog::Accepted)
{
TriggerUpdate(info);
if (!later)
m_parent->close();
}
}

View File

@ -0,0 +1,24 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QThread>
#include "UICommon/AutoUpdate.h"
class QWidget;
class Updater : public QThread, public AutoUpdateChecker
{
Q_OBJECT
public:
explicit Updater(QWidget* parent);
void run() override;
void OnUpdateAvailable(const NewVersionInformation& info) override;
private:
QWidget* m_parent;
};

View File

@ -20,15 +20,6 @@
namespace namespace
{ {
bool SystemSupportsAutoUpdates()
{
#ifdef _WIN32
return true;
#else
return false;
#endif
}
#ifdef _WIN32 #ifdef _WIN32
const char UPDATER_FILENAME[] = "Updater.exe"; const char UPDATER_FILENAME[] = "Updater.exe";
@ -57,10 +48,19 @@ void CleanupFromPreviousUpdate()
#endif #endif
} // namespace } // namespace
bool AutoUpdateChecker::SystemSupportsAutoUpdates()
{
#ifdef _WIN32
return true;
#else
return false;
#endif
}
void AutoUpdateChecker::CheckForUpdate() void AutoUpdateChecker::CheckForUpdate()
{ {
// Don't bother checking if updates are not supported or not enabled. // Don't bother checking if updates are not supported or not enabled.
if (SConfig::GetInstance().m_auto_update_track.empty() || !SystemSupportsAutoUpdates()) if (!SystemSupportsAutoUpdates() || SConfig::GetInstance().m_auto_update_track.empty())
return; return;
#ifdef _WIN32 #ifdef _WIN32
@ -104,7 +104,10 @@ void AutoUpdateChecker::CheckForUpdate()
nvi.content_store_url = obj["content-store"].get<std::string>(); nvi.content_store_url = obj["content-store"].get<std::string>();
nvi.new_shortrev = obj["new"].get<picojson::object>()["name"].get<std::string>(); nvi.new_shortrev = obj["new"].get<picojson::object>()["name"].get<std::string>();
nvi.new_hash = obj["new"].get<picojson::object>()["hash"].get<std::string>(); nvi.new_hash = obj["new"].get<picojson::object>()["hash"].get<std::string>();
// TODO: generate the HTML changelog from the JSON information. // TODO: generate the HTML changelog from the JSON information.
nvi.changelog_html = "<h2>TBD</h2>";
OnUpdateAvailable(nvi); OnUpdateAvailable(nvi);
} }

View File

@ -15,6 +15,8 @@ public:
// update is available, does "nothing" otherwise. // update is available, does "nothing" otherwise.
void CheckForUpdate(); void CheckForUpdate();
static bool SystemSupportsAutoUpdates();
struct NewVersionInformation struct NewVersionInformation
{ {
// Name (5.0-1234) and revision hash of the new version. // Name (5.0-1234) and revision hash of the new version.