mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
Add update checking system to Qt UI
* This only runs on windows - on other platforms we rely on system distribution or user local builds.
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QApplication>
|
||||
#include <QElapsedTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFileSystemModel>
|
||||
#include <QFontDatabase>
|
||||
#include <QGridLayout>
|
||||
@@ -38,6 +39,7 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QProcess>
|
||||
#include <QProgressBar>
|
||||
#include <QProgressDialog>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
@@ -1558,6 +1560,75 @@ void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinis
|
||||
progressTickerThread.wait();
|
||||
}
|
||||
|
||||
void UpdateTransferProgress(qint64 xfer, qint64 total, QElapsedTimer *timer,
|
||||
QProgressBar *progressBar, QLabel *progressLabel, QString progressText)
|
||||
{
|
||||
if(xfer >= total)
|
||||
{
|
||||
progressBar->setMaximum(10000);
|
||||
progressBar->setValue(10000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(total <= 0)
|
||||
{
|
||||
progressBar->setMaximum(10000);
|
||||
progressBar->setValue(0);
|
||||
return;
|
||||
}
|
||||
|
||||
progressBar->setMaximum(10000);
|
||||
progressBar->setValue(int(10000.0 * (double(xfer) / double(total))));
|
||||
|
||||
double xferMB = double(xfer) / 1000000.0;
|
||||
double totalMB = double(total) / 1000000.0;
|
||||
|
||||
double secondsElapsed = double(timer->nsecsElapsed()) * 1.0e-9;
|
||||
|
||||
double speedMBS = xferMB / secondsElapsed;
|
||||
|
||||
qulonglong secondsRemaining = qulonglong(double(totalMB - xferMB) / speedMBS);
|
||||
|
||||
if(secondsElapsed > 1.0)
|
||||
{
|
||||
QString remainString;
|
||||
|
||||
qulonglong minutesRemaining = (secondsRemaining / 60) % 60;
|
||||
qulonglong hoursRemaining = (secondsRemaining / 3600);
|
||||
secondsRemaining %= 60;
|
||||
|
||||
if(hoursRemaining > 0)
|
||||
remainString = QFormatStr("%1:%2:%3")
|
||||
.arg(hoursRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(minutesRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(secondsRemaining, 2, 10, QLatin1Char('0'));
|
||||
else if(minutesRemaining > 0)
|
||||
remainString = QFormatStr("%1:%2")
|
||||
.arg(minutesRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(secondsRemaining, 2, 10, QLatin1Char('0'));
|
||||
else
|
||||
remainString = QApplication::translate("qrenderdoc", "%1 seconds").arg(secondsRemaining);
|
||||
|
||||
double speed = speedMBS;
|
||||
|
||||
bool MBs = true;
|
||||
if(speedMBS < 1)
|
||||
{
|
||||
MBs = false;
|
||||
speed *= 1000;
|
||||
}
|
||||
|
||||
progressLabel->setText(
|
||||
QApplication::translate("qrenderdoc", "%1\n%2 MB / %3 MB. %4 remaining (%5 %6)")
|
||||
.arg(progressText)
|
||||
.arg(xferMB, 0, 'f', 2)
|
||||
.arg(totalMB, 0, 'f', 2)
|
||||
.arg(remainString)
|
||||
.arg(speed, 0, 'f', 2)
|
||||
.arg(MBs ? lit("MB/s") : lit("KB/s")));
|
||||
}
|
||||
}
|
||||
|
||||
void setEnabledMultiple(const QList<QWidget *> &widgets, bool enabled)
|
||||
{
|
||||
for(QWidget *w : widgets)
|
||||
|
||||
@@ -515,6 +515,8 @@ class QGridLayout;
|
||||
void addGridLines(QGridLayout *grid, QColor gridColor);
|
||||
|
||||
class QProgressDialog;
|
||||
class QProgressBar;
|
||||
class QElapsedTimer;
|
||||
|
||||
typedef std::function<float()> ProgressUpdateMethod;
|
||||
typedef std::function<bool()> ProgressFinishedMethod;
|
||||
@@ -527,6 +529,9 @@ bool RunProcessAsAdmin(const QString &fullExecutablePath, const QStringList &par
|
||||
void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished,
|
||||
ProgressUpdateMethod update = ProgressUpdateMethod());
|
||||
|
||||
void UpdateTransferProgress(qint64 xfer, qint64 total, QElapsedTimer *timer,
|
||||
QProgressBar *progressBar, QLabel *progressLabel, QString progressText);
|
||||
|
||||
void setEnabledMultiple(const QList<QWidget *> &widgets, bool enabled);
|
||||
|
||||
QString GetSystemUsername();
|
||||
|
||||
@@ -121,6 +121,29 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
bool updateApplied = false;
|
||||
|
||||
for(int i = 0; i < argc; i++)
|
||||
{
|
||||
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--updatefailed"), Qt::CaseInsensitive))
|
||||
{
|
||||
if(i < argc - 1)
|
||||
RDDialog::critical(NULL, QApplication::translate("qrenderdoc", "Error updating"),
|
||||
QApplication::translate("qrenderdoc", "Error applying update: %1")
|
||||
.arg(QString::fromUtf8(argv[i + 1])));
|
||||
else
|
||||
RDDialog::critical(NULL, QApplication::translate("qrenderdoc", "Error updating"),
|
||||
QApplication::translate("qrenderdoc", "Unknown error applying update"));
|
||||
}
|
||||
|
||||
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--updatedone"), Qt::CaseInsensitive))
|
||||
{
|
||||
updateApplied = true;
|
||||
|
||||
RENDERDOC_UpdateInstalledVersionNumber();
|
||||
}
|
||||
}
|
||||
|
||||
QString remoteHost;
|
||||
uint remoteIdent = 0;
|
||||
|
||||
@@ -327,6 +350,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if(updateApplied)
|
||||
{
|
||||
config.CheckUpdate_UpdateAvailable = false;
|
||||
config.CheckUpdate_UpdateResponse = "";
|
||||
config.Save();
|
||||
}
|
||||
|
||||
while(ctx.isRunning())
|
||||
{
|
||||
application.processEvents(QEventLoop::WaitForMoreEvents);
|
||||
|
||||
@@ -144,7 +144,6 @@ void CrashDialog::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
recentre();
|
||||
}
|
||||
|
||||
void CrashDialog::recentre()
|
||||
{
|
||||
QRect scr = QApplication::desktop()->screenGeometry();
|
||||
@@ -187,10 +186,11 @@ void CrashDialog::on_send_clicked()
|
||||
if(ui->captureUpload->isChecked())
|
||||
{
|
||||
QMessageBox::StandardButton result = RDDialog::question(
|
||||
this, tr("Are you sure?"),
|
||||
tr("Uploading your capture file will send it privately to the RenderDoc server where I can "
|
||||
"use it to reproduce your problem.\n\nAre you sure you are OK with sending the capture "
|
||||
"securely to RenderDoc's website?"));
|
||||
this, tr("Are you sure?"), tr("Uploading your capture file will send it privately to the "
|
||||
"RenderDoc server where I can "
|
||||
"use it to reproduce your problem.\n\nAre you sure you are "
|
||||
"OK with sending the capture "
|
||||
"securely to RenderDoc's website?"));
|
||||
|
||||
if(result != QMessageBox::Yes)
|
||||
{
|
||||
@@ -334,7 +334,6 @@ void CrashDialog::sendReport()
|
||||
ui->uploadRetry->setEnabled(true);
|
||||
});
|
||||
|
||||
ui->progressBar->setMaximum(10000);
|
||||
ui->progressBar->setValue(0);
|
||||
ui->progressText->setText(tr("Uploading report...\nCalculating time remaining"));
|
||||
|
||||
@@ -344,46 +343,8 @@ void CrashDialog::sendReport()
|
||||
m_UploadTimer->start();
|
||||
|
||||
QObject::connect(m_Request, &QNetworkReply::uploadProgress, [this](qint64 sent, qint64 total) {
|
||||
if(total > 0 && total > sent)
|
||||
{
|
||||
ui->progressBar->setValue(int(10000.0 * (double(sent) / double(total))));
|
||||
|
||||
double sentMB = double(sent) / 1000000.0;
|
||||
double totalMB = double(total) / 1000000.0;
|
||||
|
||||
double secondsElapsed = double(m_UploadTimer->nsecsElapsed()) * 1.0e-9;
|
||||
|
||||
double speedMBS = sentMB / secondsElapsed;
|
||||
|
||||
qulonglong secondsRemaining = qulonglong(double(totalMB - sentMB) / speedMBS);
|
||||
|
||||
if(secondsElapsed > 1.0)
|
||||
{
|
||||
QString remainString;
|
||||
|
||||
qulonglong minutesRemaining = (secondsRemaining / 60) % 60;
|
||||
qulonglong hoursRemaining = (secondsRemaining / 3600);
|
||||
secondsRemaining %= 60;
|
||||
|
||||
if(hoursRemaining > 0)
|
||||
remainString = QFormatStr("%1:%2:%3")
|
||||
.arg(hoursRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(minutesRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(secondsRemaining, 2, 10, QLatin1Char('0'));
|
||||
else if(minutesRemaining > 0)
|
||||
remainString = QFormatStr("%1:%2")
|
||||
.arg(minutesRemaining, 2, 10, QLatin1Char('0'))
|
||||
.arg(secondsRemaining, 2, 10, QLatin1Char('0'));
|
||||
else
|
||||
remainString = tr("%1 seconds").arg(secondsRemaining);
|
||||
|
||||
ui->progressText->setText(tr("Uploading report...\n%1 MB / %2 MB. %3 remaining (%4 MB/s)")
|
||||
.arg(sentMB, 0, 'f', 2)
|
||||
.arg(totalMB, 0, 'f', 2)
|
||||
.arg(remainString)
|
||||
.arg(speedMBS, 0, 'f', 2));
|
||||
}
|
||||
}
|
||||
UpdateTransferProgress(sent, total, m_UploadTimer, ui->progressBar, ui->progressText,
|
||||
tr("Uploading report..."));
|
||||
});
|
||||
|
||||
QObject::connect(m_Request, &QNetworkReply::finished, [this]() {
|
||||
|
||||
@@ -222,6 +222,12 @@ void SettingsDialog::on_CheckUpdate_AllowChecks_toggled(bool checked)
|
||||
{
|
||||
m_Ctx.Config().CheckUpdate_AllowChecks = ui->CheckUpdate_AllowChecks->isChecked();
|
||||
|
||||
if(!m_Ctx.Config().CheckUpdate_AllowChecks)
|
||||
{
|
||||
m_Ctx.Config().CheckUpdate_UpdateAvailable = false;
|
||||
m_Ctx.Config().CheckUpdate_UpdateResponse = "";
|
||||
}
|
||||
|
||||
m_Ctx.Config().Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "UpdateDialog.h"
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDateTime>
|
||||
#include <QDesktopServices>
|
||||
#include <QElapsedTimer>
|
||||
#include <QLabel>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QString>
|
||||
#include "Code/QRDUtils.h"
|
||||
#include "ui_UpdateDialog.h"
|
||||
#include "version.h"
|
||||
|
||||
UpdateDialog::UpdateDialog(QString updateResponse, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::UpdateDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->updateText->setBackgroundRole(QPalette::Base);
|
||||
ui->updateText->setForegroundRole(QPalette::Text);
|
||||
|
||||
m_NetManager = new QNetworkAccessManager(this);
|
||||
|
||||
setWindowFlags((windowFlags() | Qt::MSWindowsFixedSizeDialogHint) &
|
||||
~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
QStringList lines = updateResponse.split(QLatin1Char('\n'), QString::SkipEmptyParts);
|
||||
|
||||
m_NewVer = lines[0];
|
||||
m_URL = lines[1];
|
||||
m_Size = lines[2].toUInt();
|
||||
|
||||
QString notes;
|
||||
|
||||
for(int i = 3; i < lines.count(); i++)
|
||||
notes += lines[i];
|
||||
|
||||
ui->progressText->setVisible(false);
|
||||
ui->progressBar->setVisible(false);
|
||||
|
||||
QString text = tr("Update Available - v%1").arg(m_NewVer);
|
||||
ui->updateVer->setText(text);
|
||||
setWindowTitle(text);
|
||||
|
||||
ui->updateText->setText(notes);
|
||||
|
||||
ui->currentVersion->setText(lit(FULL_VERSION_STRING));
|
||||
ui->newVersion->setText(QFormatStr("v%1").arg(m_NewVer));
|
||||
ui->downloadSize->setText(QFormatStr("%1 MB").arg(double(m_Size) / 1000000.0, 0, 'f', 2));
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
UpdateDialog::~UpdateDialog()
|
||||
{
|
||||
delete m_DownloadTimer;
|
||||
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void UpdateDialog::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if(e->key() == Qt::Key_Escape)
|
||||
return;
|
||||
|
||||
QDialog::keyPressEvent(e);
|
||||
}
|
||||
|
||||
void UpdateDialog::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
if(ui->close->isEnabled())
|
||||
{
|
||||
QDialog::closeEvent(e);
|
||||
return;
|
||||
}
|
||||
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
void UpdateDialog::on_releaseNotes_clicked()
|
||||
{
|
||||
QDesktopServices::openUrl(
|
||||
QUrl(lit("https://github.com/baldurk/renderdoc/releases/tag/v%1").arg(m_NewVer)));
|
||||
}
|
||||
|
||||
void UpdateDialog::on_close_clicked()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
|
||||
void UpdateDialog::on_update_clicked()
|
||||
{
|
||||
QMessageBox::StandardButton res = RDDialog::question(
|
||||
this, tr("RenderDoc Update"), tr("This will close RenderDoc immediately - if you have any "
|
||||
"unsaved work, save it first!\n"
|
||||
"Continue?"),
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
{
|
||||
QString runningPrograms;
|
||||
int running = 0;
|
||||
|
||||
uint32_t nextIdent = 0;
|
||||
|
||||
QString localhost = lit("localhost");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// just a sanity check to make sure we don't hit some unexpected case and infinite loop
|
||||
uint32_t prevIdent = nextIdent;
|
||||
|
||||
nextIdent = RENDERDOC_EnumerateRemoteTargets("localhost", nextIdent);
|
||||
|
||||
if(nextIdent == 0 || prevIdent >= nextIdent)
|
||||
break;
|
||||
|
||||
running++;
|
||||
|
||||
ITargetControl *conn = RENDERDOC_CreateTargetControl("localhost", nextIdent, "updater", false);
|
||||
|
||||
if(conn)
|
||||
{
|
||||
if(!runningPrograms.isEmpty())
|
||||
runningPrograms += lit("\n");
|
||||
|
||||
QString target =
|
||||
conn->GetTarget() ? QString::fromUtf8(conn->GetTarget()) : lit("<unknown>");
|
||||
if(conn->GetAPI())
|
||||
runningPrograms += tr("%1 running %2").arg(target).arg(QString::fromUtf8(conn->GetAPI()));
|
||||
else
|
||||
runningPrograms += target;
|
||||
|
||||
conn->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
if(running > 0)
|
||||
{
|
||||
RDDialog::critical(
|
||||
this, tr("RenderDoc in use"),
|
||||
tr("RenderDoc is currently capturing, cannot update until the program%1 closed:\n\n")
|
||||
.arg(running > 1 ? lit("s are") : lit(" is")) +
|
||||
runningPrograms);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->metadataFrame->setVisible(false);
|
||||
ui->progressBar->setVisible(true);
|
||||
ui->progressText->setVisible(true);
|
||||
|
||||
ui->progressBar->setMaximum(10000);
|
||||
ui->progressBar->setValue(0);
|
||||
ui->progressText->setText(tr("Preparing Download"));
|
||||
|
||||
ui->close->setEnabled(false);
|
||||
ui->update->setEnabled(false);
|
||||
|
||||
delete m_DownloadTimer;
|
||||
m_DownloadTimer = new QElapsedTimer();
|
||||
|
||||
m_DownloadTimer->start();
|
||||
|
||||
QNetworkReply *req = m_NetManager->get(QNetworkRequest(QUrl(m_URL)));
|
||||
|
||||
QObject::connect(req, &QNetworkReply::downloadProgress, [this](qint64 recvd, qint64 total) {
|
||||
UpdateTransferProgress(recvd, total, m_DownloadTimer, ui->progressBar, ui->progressText,
|
||||
tr("Downloading update..."));
|
||||
});
|
||||
|
||||
QObject::connect(req, OverloadedSlot<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
|
||||
[this, req](QNetworkReply::NetworkError err) {
|
||||
ui->progressBar->setValue(0);
|
||||
ui->progressText->setText(tr("Network error:\n%1").arg(req->errorString()));
|
||||
ui->update->setEnabled(true);
|
||||
ui->close->setEnabled(true);
|
||||
ui->update->setText(tr("Retry Update"));
|
||||
});
|
||||
|
||||
QObject::connect(req, &QNetworkReply::finished, [this, req]() {
|
||||
|
||||
// don't do anything if we're finished after an error
|
||||
if(ui->update->isEnabled())
|
||||
return;
|
||||
|
||||
QDir dir(QDir::tempPath());
|
||||
|
||||
dir.mkdir(lit("RenderDocUpdate"));
|
||||
dir.cd(lit("RenderDocUpdate"));
|
||||
|
||||
QString path = dir.absoluteFilePath(lit("update.zip"));
|
||||
|
||||
{
|
||||
QFile file(path);
|
||||
if(file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
file.write(req->readAll());
|
||||
}
|
||||
else
|
||||
{
|
||||
RDDialog::critical(this, tr("Error saving file"),
|
||||
tr("Couldn't save update file to: %1").arg(path));
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
QDir appDir = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
|
||||
bool success = true;
|
||||
|
||||
QString dll = lit("renderdoc.dll");
|
||||
QString cmd = lit("renderdoccmd.exe");
|
||||
|
||||
QFile::remove(dir.absoluteFilePath(dll));
|
||||
QFile::remove(dir.absoluteFilePath(cmd));
|
||||
|
||||
success &= QFile::copy(appDir.absoluteFilePath(dll), dir.absoluteFilePath(dll));
|
||||
success &= QFile::copy(appDir.absoluteFilePath(cmd), dir.absoluteFilePath(cmd));
|
||||
|
||||
if(!success)
|
||||
{
|
||||
RDDialog::critical(this, tr("Error running updated"),
|
||||
tr("Couldn't copy updater files to temporary path"));
|
||||
reject();
|
||||
}
|
||||
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
|
||||
success = RunProcessAsAdmin(dir.absoluteFilePath(cmd), QStringList()
|
||||
<< lit("upgrade") << lit("--path")
|
||||
<< appDir.absolutePath());
|
||||
|
||||
exit(0);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class UpdateDialog;
|
||||
}
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QElapsedTimer;
|
||||
|
||||
struct Thumbnail;
|
||||
|
||||
class UpdateDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateDialog(QString updateResponse, QWidget *parent = 0);
|
||||
~UpdateDialog();
|
||||
|
||||
private slots:
|
||||
// automatic slots
|
||||
void on_releaseNotes_clicked();
|
||||
void on_close_clicked();
|
||||
void on_update_clicked();
|
||||
|
||||
private:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void closeEvent(QCloseEvent *) override;
|
||||
|
||||
QString m_NewVer;
|
||||
QString m_URL;
|
||||
uint32_t m_Size = 0;
|
||||
|
||||
Ui::UpdateDialog *ui;
|
||||
|
||||
QElapsedTimer *m_DownloadTimer = NULL;
|
||||
|
||||
QNetworkAccessManager *m_NetManager;
|
||||
QNetworkReply *m_Request = NULL;
|
||||
};
|
||||
@@ -0,0 +1,423 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UpdateDialog</class>
|
||||
<widget class="QDialog" name="UpdateDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>465</width>
|
||||
<height>505</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Available</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>59</red>
|
||||
<green>183</green>
|
||||
<blue>121</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>59</red>
|
||||
<green>183</green>
|
||||
<blue>121</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>106</red>
|
||||
<green>104</green>
|
||||
<blue>100</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>59</red>
|
||||
<green>183</green>
|
||||
<blue>121</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>59</red>
|
||||
<green>183</green>
|
||||
<blue>121</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="RDLabel" name="rdocLogo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../Resources/resources.qrc">:/logo.svg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateVer">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active/>
|
||||
<inactive/>
|
||||
<disabled/>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update Available - vX.YZ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="RDLabel" name="updateText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="metadataFrame">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="newLabel">
|
||||
<property name="text">
|
||||
<string>New Version:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="sizeLabel">
|
||||
<property name="text">
|
||||
<string>Download Size:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="currentVersion">
|
||||
<property name="text">
|
||||
<string>vX.YY</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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 row="1" column="2">
|
||||
<widget class="QLabel" name="newVersion">
|
||||
<property name="text">
|
||||
<string>vX.YZ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="downloadSize">
|
||||
<property name="text">
|
||||
<string>12.34 MB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="curLabel">
|
||||
<property name="text">
|
||||
<string>Current Version:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="progressText">
|
||||
<property name="text">
|
||||
<string>Downloading</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonLayout">
|
||||
<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="releaseNotes">
|
||||
<property name="text">
|
||||
<string>Full Release Notes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="update">
|
||||
<property name="text">
|
||||
<string>Install Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RDLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>Widgets/Extended/RDLabel.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../Resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "Windows/Dialogs/SettingsDialog.h"
|
||||
#include "Windows/Dialogs/SuggestRemoteDialog.h"
|
||||
#include "Windows/Dialogs/TipsDialog.h"
|
||||
#include "Windows/Dialogs/UpdateDialog.h"
|
||||
#include "ui_MainWindow.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -180,11 +181,30 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai
|
||||
ui->action_Send_Error_Report->setEnabled(false);
|
||||
#endif
|
||||
|
||||
m_NetManager = new QNetworkAccessManager(this);
|
||||
|
||||
#if !defined(Q_OS_WIN32)
|
||||
// update checks only happen on windows
|
||||
{
|
||||
QList<QAction *> actions = ui->menu_Help->actions();
|
||||
int idx = actions.indexOf(ui->action_Update_Available);
|
||||
idx++;
|
||||
if(idx < actions.count() && actions[idx]->isSeparator())
|
||||
delete actions[idx];
|
||||
|
||||
delete ui->action_Update_Available;
|
||||
ui->action_Update_Available = NULL;
|
||||
|
||||
delete ui->action_Check_for_Updates;
|
||||
ui->action_Check_for_Updates = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
PopulateRecentCaptureFiles();
|
||||
PopulateRecentCaptureSettings();
|
||||
PopulateReportedBugs();
|
||||
|
||||
m_NetManager = new QNetworkAccessManager(this);
|
||||
CheckUpdates();
|
||||
|
||||
rdcarray<BugReport> bugs = m_Ctx.Config().CrashReport_ReportedBugs;
|
||||
LambdaThread *bugupdate = new LambdaThread([this, bugs]() {
|
||||
@@ -854,6 +874,47 @@ void MainWindow::SetTitle()
|
||||
SetTitle(m_Ctx.GetCaptureFilename());
|
||||
}
|
||||
|
||||
bool MainWindow::HandleMismatchedVersions()
|
||||
{
|
||||
if(IsVersionMismatched())
|
||||
{
|
||||
qCritical() << "Version mismatch between UI (" << lit(MAJOR_MINOR_VERSION_STRING) << ")"
|
||||
<< "and core"
|
||||
<< "(" << QString::fromUtf8(RENDERDOC_GetVersionString()) << ")";
|
||||
|
||||
#if !RENDERDOC_OFFICIAL_BUILD
|
||||
RDDialog::critical(
|
||||
this, tr("Unofficial build - mismatched versions"),
|
||||
tr("You are running an unofficial build with mismatched core and UI versions.\n"
|
||||
"Double check where you got your build from and do a sanity check!"));
|
||||
#else
|
||||
QMessageBox::StandardButton res = RDDialog::critical(
|
||||
this, tr("Mismatched versions"),
|
||||
tr("RenderDoc has detected mismatched versions between its internal module and UI.\n"
|
||||
"This is likely caused by a buggy update in the past which partially updated your "
|
||||
"install."
|
||||
"Likely because a program was running with renderdoc while the update happened.\n"
|
||||
"You should reinstall RenderDoc immediately as this configuration is almost guaranteed "
|
||||
"to crash.\n\n"
|
||||
"Would you like to open the downloads page to reinstall?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
QDesktopServices::openUrl(QUrl(lit("https://renderdoc.org/builds")));
|
||||
|
||||
SetUpdateAvailable();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::IsVersionMismatched()
|
||||
{
|
||||
return QString::fromLatin1(RENDERDOC_GetVersionString()) != lit(MAJOR_MINOR_VERSION_STRING);
|
||||
}
|
||||
|
||||
void MainWindow::ClearRecentCaptureFiles()
|
||||
{
|
||||
m_Ctx.Config().RecentCaptureFiles.clear();
|
||||
@@ -952,16 +1013,152 @@ void MainWindow::PopulateReportedBugs()
|
||||
|
||||
if(unread)
|
||||
{
|
||||
ui->menu_Help->setIcon(Icons::bug());
|
||||
if(!m_Ctx.Config().CheckUpdate_UpdateAvailable)
|
||||
ui->menu_Help->setIcon(Icons::bug());
|
||||
ui->menu_Reported_Bugs->setIcon(Icons::bug());
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->menu_Help->setIcon(QIcon());
|
||||
if(!m_Ctx.Config().CheckUpdate_UpdateAvailable)
|
||||
ui->menu_Help->setIcon(QIcon());
|
||||
ui->menu_Reported_Bugs->setIcon(QIcon());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::CheckUpdates(bool forceCheck, UpdateResultMethod callback)
|
||||
{
|
||||
if(!ui->action_Update_Available)
|
||||
return;
|
||||
|
||||
bool mismatch = HandleMismatchedVersions();
|
||||
if(mismatch)
|
||||
return;
|
||||
|
||||
if(!forceCheck && !m_Ctx.Config().CheckUpdate_AllowChecks)
|
||||
{
|
||||
ui->action_Update_Available->setText(tr("Update checks disabled"));
|
||||
ui->action_Update_Available->setEnabled(false);
|
||||
if(callback)
|
||||
callback(UpdateResult::Disabled);
|
||||
return;
|
||||
}
|
||||
|
||||
#if RENDERDOC_OFFICIAL_BUILD
|
||||
if(m_Ctx.Config().CheckUpdate_UpdateAvailable)
|
||||
{
|
||||
if(m_Ctx.Config().CheckUpdate_UpdateResponse.isEmpty())
|
||||
{
|
||||
forceCheck = true;
|
||||
}
|
||||
else if(!forceCheck)
|
||||
{
|
||||
SetUpdateAvailable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime today = QDateTime::currentDateTime();
|
||||
QDateTime compare = today.addDays(-2);
|
||||
|
||||
qint64 diff = compare.secsTo(m_Ctx.Config().CheckUpdate_LastUpdate);
|
||||
|
||||
if(!forceCheck && diff > 0)
|
||||
{
|
||||
if(callback)
|
||||
callback(UpdateResult::Toosoon);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Ctx.Config().CheckUpdate_LastUpdate = today;
|
||||
m_Ctx.Config().Save();
|
||||
|
||||
#if QT_POINTER_SIZE == 4
|
||||
QString bitness = lit("32");
|
||||
#else
|
||||
QString bitness = lit("64");
|
||||
#endif
|
||||
QString versionCheck = lit(MAJOR_MINOR_VERSION_STRING);
|
||||
|
||||
statusText->setText(tr("Checking for updates..."));
|
||||
|
||||
statusProgress->setVisible(true);
|
||||
statusProgress->setMinimumSize(QSize(200, 0));
|
||||
statusProgress->setMinimum(0);
|
||||
statusProgress->setMaximum(0);
|
||||
|
||||
// call out to the status-check to see when the bug report was last updated
|
||||
QNetworkReply *req = m_NetManager->get(QNetworkRequest(QUrl(
|
||||
lit("https://renderdoc.org/getupdateurl/%1/%2?htmlnotes=1").arg(bitness).arg(versionCheck))));
|
||||
|
||||
QObject::connect(req, OverloadedSlot<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
|
||||
[this, req](QNetworkReply::NetworkError) {
|
||||
qCritical() << "Network error:" << req->errorString();
|
||||
});
|
||||
|
||||
QObject::connect(req, &QNetworkReply::finished, [this, req, callback]() {
|
||||
QString response = QString::fromUtf8(req->readAll());
|
||||
|
||||
statusText->setText(QString());
|
||||
statusProgress->setVisible(false);
|
||||
statusProgress->setMaximum(1000);
|
||||
|
||||
if(response.isEmpty())
|
||||
{
|
||||
m_Ctx.Config().CheckUpdate_UpdateAvailable = false;
|
||||
m_Ctx.Config().CheckUpdate_UpdateResponse = "";
|
||||
m_Ctx.Config().Save();
|
||||
SetNoUpdate();
|
||||
|
||||
if(callback)
|
||||
callback(UpdateResult::Latest);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_Ctx.Config().CheckUpdate_UpdateAvailable = true;
|
||||
m_Ctx.Config().CheckUpdate_UpdateResponse = response;
|
||||
m_Ctx.Config().Save();
|
||||
SetUpdateAvailable();
|
||||
UpdatePopup();
|
||||
});
|
||||
#else //! RENDERDOC_OFFICIAL_BUILD
|
||||
{
|
||||
if(callback)
|
||||
callback(UpdateResult::Unofficial);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::SetUpdateAvailable()
|
||||
{
|
||||
if(!ui->action_Update_Available)
|
||||
return;
|
||||
|
||||
ui->menu_Help->setIcon(Icons::hourglass());
|
||||
ui->action_Update_Available->setEnabled(true);
|
||||
ui->action_Update_Available->setText(tr("An update is available"));
|
||||
}
|
||||
|
||||
void MainWindow::SetNoUpdate()
|
||||
{
|
||||
if(!ui->action_Update_Available)
|
||||
return;
|
||||
|
||||
ui->menu_Help->setIcon(QIcon());
|
||||
ui->action_Update_Available->setEnabled(false);
|
||||
ui->action_Update_Available->setText(tr("No update available"));
|
||||
}
|
||||
|
||||
void MainWindow::UpdatePopup()
|
||||
{
|
||||
if(!m_Ctx.Config().CheckUpdate_UpdateAvailable || !m_Ctx.Config().CheckUpdate_AllowChecks)
|
||||
return;
|
||||
|
||||
UpdateDialog update((QString)m_Ctx.Config().CheckUpdate_UpdateResponse);
|
||||
RDDialog::show(&update);
|
||||
}
|
||||
|
||||
void MainWindow::ShowLiveCapture(LiveCapture *live)
|
||||
{
|
||||
m_LiveCaptures.push_back(live);
|
||||
@@ -1124,6 +1321,7 @@ void MainWindow::setProgress(float val)
|
||||
else
|
||||
{
|
||||
statusProgress->setVisible(true);
|
||||
statusProgress->setMaximum(1000);
|
||||
statusProgress->setValue(1000 * val);
|
||||
}
|
||||
}
|
||||
@@ -1972,6 +2170,54 @@ void MainWindow::on_action_Send_Error_Report_triggered()
|
||||
QFile::remove(QString(report));
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Check_for_Updates_triggered()
|
||||
{
|
||||
CheckUpdates(true, [this](UpdateResult updateResult) {
|
||||
switch(updateResult)
|
||||
{
|
||||
case UpdateResult::Disabled:
|
||||
case UpdateResult::Toosoon:
|
||||
{
|
||||
// won't happen, we forced the check
|
||||
break;
|
||||
}
|
||||
case UpdateResult::Unofficial:
|
||||
{
|
||||
QMessageBox::StandardButton res =
|
||||
RDDialog::question(this, tr("Unofficial build"),
|
||||
tr("You are running an unofficial build, not a stable release.\n"
|
||||
"Updates are only available for installed release builds\n\n"
|
||||
"Would you like to open the builds list in a browser?"));
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
QDesktopServices::openUrl(lit("https://renderdoc.org/builds"));
|
||||
break;
|
||||
}
|
||||
case UpdateResult::Latest:
|
||||
{
|
||||
RDDialog::information(this, tr("Latest version"),
|
||||
tr("You are running the latest version."));
|
||||
break;
|
||||
}
|
||||
case UpdateResult::Upgrade:
|
||||
{
|
||||
// CheckUpdates() will have shown a dialog for this
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Update_Available_triggered()
|
||||
{
|
||||
bool mismatch = HandleMismatchedVersions();
|
||||
if(mismatch)
|
||||
return;
|
||||
|
||||
SetUpdateAvailable();
|
||||
UpdatePopup();
|
||||
}
|
||||
|
||||
void MainWindow::saveLayout_triggered()
|
||||
{
|
||||
LoadSaveLayout(qobject_cast<QAction *>(QObject::sender()), true);
|
||||
|
||||
@@ -138,6 +138,8 @@ private slots:
|
||||
void on_action_Counter_Viewer_triggered();
|
||||
void on_action_Resource_Inspector_triggered();
|
||||
void on_action_Send_Error_Report_triggered();
|
||||
void on_action_Check_for_Updates_triggered();
|
||||
void on_action_Update_Available_triggered();
|
||||
|
||||
// manual slots
|
||||
void saveLayout_triggered();
|
||||
@@ -162,6 +164,17 @@ private:
|
||||
|
||||
QString dragFilename(const QMimeData *mimeData);
|
||||
|
||||
enum class UpdateResult
|
||||
{
|
||||
Disabled,
|
||||
Unofficial,
|
||||
Toosoon,
|
||||
Latest,
|
||||
Upgrade,
|
||||
};
|
||||
|
||||
typedef std::function<void(UpdateResult)> UpdateResultMethod;
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
ICaptureContext &m_Ctx;
|
||||
|
||||
@@ -188,6 +201,13 @@ private:
|
||||
|
||||
QString m_LastSaveCapturePath;
|
||||
|
||||
void CheckUpdates(bool forceCheck = false, UpdateResultMethod callback = UpdateResultMethod());
|
||||
void SetUpdateAvailable();
|
||||
void SetNoUpdate();
|
||||
void UpdatePopup();
|
||||
bool HandleMismatchedVersions();
|
||||
bool IsVersionMismatched();
|
||||
|
||||
void setCaptureHasErrors(bool errors);
|
||||
|
||||
void SetTitle(const QString &filename);
|
||||
|
||||
@@ -154,6 +154,7 @@
|
||||
<addaction name="action_Send_Error_Report"/>
|
||||
<addaction name="menu_Reported_Bugs"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Check_for_Updates"/>
|
||||
<addaction name="action_Update_Available"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Source_on_GitHub"/>
|
||||
@@ -364,8 +365,15 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Update_Available">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/hourglass.png</normaloff>:/hourglass.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update Available</string>
|
||||
<string>No update available</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Source_on_GitHub">
|
||||
@@ -443,6 +451,11 @@
|
||||
<string>&Clear Reported Bugs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Check_for_Updates">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
||||
@@ -172,6 +172,7 @@ SOURCES += Code/qrenderdoc.cpp \
|
||||
Styles/RDTweakedNativeStyle/RDTweakedNativeStyle.cpp \
|
||||
Windows/Dialogs/AboutDialog.cpp \
|
||||
Windows/Dialogs/CrashDialog.cpp \
|
||||
Windows/Dialogs/UpdateDialog.cpp \
|
||||
Windows/MainWindow.cpp \
|
||||
Windows/EventBrowser.cpp \
|
||||
Windows/TextureViewer.cpp \
|
||||
@@ -244,6 +245,7 @@ HEADERS += Code/CaptureContext.h \
|
||||
Styles/RDTweakedNativeStyle/RDTweakedNativeStyle.h \
|
||||
Windows/Dialogs/AboutDialog.h \
|
||||
Windows/Dialogs/CrashDialog.h \
|
||||
Windows/Dialogs/UpdateDialog.h \
|
||||
Windows/MainWindow.h \
|
||||
Windows/EventBrowser.h \
|
||||
Windows/TextureViewer.h \
|
||||
@@ -299,6 +301,7 @@ HEADERS += Code/CaptureContext.h \
|
||||
Windows/Dialogs/AnalyticsPromptDialog.h
|
||||
FORMS += Windows/Dialogs/AboutDialog.ui \
|
||||
Windows/Dialogs/CrashDialog.ui \
|
||||
Windows/Dialogs/UpdateDialog.ui \
|
||||
Windows/MainWindow.ui \
|
||||
Windows/EventBrowser.ui \
|
||||
Windows/TextureViewer.ui \
|
||||
|
||||
@@ -578,6 +578,7 @@
|
||||
<ClCompile Include="$(IntDir)generated\moc_RDTweakedNativeStyle.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_AboutDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_CrashDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_UpdateDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_AnalyticsConfirmDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_AnalyticsPromptDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_APIInspector.cpp" />
|
||||
@@ -690,6 +691,7 @@
|
||||
<ClCompile Include="Windows\CommentView.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\AboutDialog.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\CrashDialog.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\UpdateDialog.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\AnalyticsConfirmDialog.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\AnalyticsPromptDialog.cpp" />
|
||||
<ClCompile Include="Windows\Dialogs\CaptureDialog.cpp" />
|
||||
@@ -888,6 +890,7 @@
|
||||
<ClInclude Include="Code\Resources.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_AboutDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_CrashDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_UpdateDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_AnalyticsConfirmDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_AnalyticsPromptDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_APIInspector.h" />
|
||||
@@ -1128,6 +1131,12 @@
|
||||
<Message>MOC %(Filename).h</Message>
|
||||
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\UpdateDialog.h">
|
||||
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
|
||||
<Message>MOC %(Filename).h</Message>
|
||||
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\AnalyticsConfirmDialog.h">
|
||||
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui" -I"$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
|
||||
@@ -1374,6 +1383,12 @@
|
||||
<Message>UIC %(Filename).ui</Message>
|
||||
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\UpdateDialog.ui">
|
||||
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
|
||||
<Message>UIC %(Filename).ui</Message>
|
||||
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\AnalyticsConfirmDialog.ui">
|
||||
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
|
||||
|
||||
@@ -696,6 +696,12 @@
|
||||
<ClCompile Include="Windows\Dialogs\CrashDialog.cpp">
|
||||
<Filter>Windows\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)generated\moc_UpdateDialog.cpp">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Windows\Dialogs\UpdateDialog.cpp">
|
||||
<Filter>Windows\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
|
||||
@@ -1049,6 +1055,9 @@
|
||||
<ClInclude Include="$(IntDir)generated\ui_CrashDialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(IntDir)generated\ui_UpdateDialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Code\pyrenderdoc\pyconversion.h">
|
||||
@@ -1415,6 +1424,12 @@
|
||||
<CustomBuild Include="Windows\Dialogs\CrashDialog.ui">
|
||||
<Filter>Windows\Dialogs</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\UpdateDialog.h">
|
||||
<Filter>Windows\Dialogs</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\Dialogs\UpdateDialog.ui">
|
||||
<Filter>Windows\Dialogs</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="Resources\action.png">
|
||||
|
||||
@@ -1876,6 +1876,9 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_UpdateVulkanLayerRegistrati
|
||||
// Miscellaneous!
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DOCUMENT("Internal function for updating installed version number in windows registry.");
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_UpdateInstalledVersionNumber();
|
||||
|
||||
DOCUMENT("Internal function for initialising global process environment in a replay program.");
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_InitGlobalEnv(GlobalEnvironment env,
|
||||
const rdcarray<rdcstr> &args);
|
||||
|
||||
@@ -498,6 +498,98 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_UpdateVulkanLayerRegistrati
|
||||
RenderDoc::Inst().UpdateVulkanLayerRegistration(systemLevel);
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_UpdateInstalledVersionNumber()
|
||||
{
|
||||
#if ENABLED(RDOC_WIN32)
|
||||
HKEY key = NULL;
|
||||
|
||||
LSTATUS ret =
|
||||
RegCreateKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
||||
0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, NULL);
|
||||
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
if(key)
|
||||
RegCloseKey(key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
||||
char guidName[256] = {};
|
||||
for(DWORD idx = 0; ret == ERROR_SUCCESS && !done; idx++)
|
||||
{
|
||||
// enumerate all the uninstall keys
|
||||
ret = RegEnumKeyA(key, idx, guidName, sizeof(guidName) - 1);
|
||||
|
||||
if(ret == ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// open the key as we'll need it for RegSetValueExA
|
||||
HKEY subkey = NULL;
|
||||
ret = RegCreateKeyExA(key, guidName, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &subkey, NULL);
|
||||
|
||||
if(ret == ERROR_SUCCESS && subkey)
|
||||
{
|
||||
char DisplayName[256] = {};
|
||||
char Publisher[256] = {};
|
||||
DWORD len = sizeof(DisplayName) - 1;
|
||||
|
||||
// fetch DisplayName and Publisher values
|
||||
ret = RegGetValueA(subkey, NULL, "DisplayName", RRF_RT_ANY, NULL, DisplayName, &len);
|
||||
|
||||
// allow the value to silently not exist
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
DisplayName[0] = 0;
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
len = sizeof(Publisher) - 1;
|
||||
ret = RegGetValueA(subkey, NULL, "Publisher", RRF_RT_ANY, NULL, Publisher, &len);
|
||||
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
Publisher[0] = 0;
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// if this is our key, set the version number
|
||||
if(!strcmp(DisplayName, "RenderDoc") && !strcmp(Publisher, "Baldur Karlsson"))
|
||||
{
|
||||
DWORD Version = (RENDERDOC_VERSION_MAJOR << 24) | (RENDERDOC_VERSION_MINOR << 16);
|
||||
DWORD VersionMajor = RENDERDOC_VERSION_MAJOR;
|
||||
DWORD VersionMinor = RENDERDOC_VERSION_MINOR;
|
||||
std::string DisplayVersion = MAJOR_MINOR_VERSION_STRING ".0";
|
||||
|
||||
RegSetValueExA(subkey, "Version", 0, REG_DWORD, (const BYTE *)&Version, sizeof(Version));
|
||||
RegSetValueExA(subkey, "VersionMajor", 0, REG_DWORD, (const BYTE *)&VersionMajor,
|
||||
sizeof(VersionMajor));
|
||||
RegSetValueExA(subkey, "VersionMinor", 0, REG_DWORD, (const BYTE *)&VersionMinor,
|
||||
sizeof(VersionMinor));
|
||||
RegSetValueExA(subkey, "DisplayVersion", 0, REG_SZ, (const BYTE *)DisplayVersion.c_str(),
|
||||
(DWORD)DisplayVersion.size() + 1);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(subkey)
|
||||
RegCloseKey(subkey);
|
||||
}
|
||||
|
||||
if(key)
|
||||
RegCloseKey(key);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::string ResourceFormatName(const ResourceFormat &fmt)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
Reference in New Issue
Block a user