From b7d7079ea6f63adbfbf6f9a620410f6bd9cba9c5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 19 Jun 2020 14:00:39 +0800 Subject: [PATCH] add: added new version auto restart --- makespec/BUILDVERSION | 2 +- src/Qv2rayApplication.cpp | 89 +++++++++++++++++++++++++++++++-------- src/Qv2rayApplication.hpp | 8 ++-- src/base/Qv2rayBase.hpp | 6 ++- src/main.cpp | 83 +++++++++++++++++------------------- 5 files changed, 119 insertions(+), 69 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 532278a0..c98c1b47 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5619 +5620 diff --git a/src/Qv2rayApplication.cpp b/src/Qv2rayApplication.cpp index c2791362..fad89f73 100644 --- a/src/Qv2rayApplication.cpp +++ b/src/Qv2rayApplication.cpp @@ -1,5 +1,6 @@ #include "Qv2rayApplication.hpp" +#include "3rdparty/libsemver/version.hpp" #include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" #include "common/QvTranslator.hpp" @@ -23,13 +24,12 @@ namespace Qv2ray Qv2rayApplication::Qv2rayApplication(int &argc, char *argv[]) : SingleApplication(argc, argv, true, User | ExcludeAppPath | ExcludeAppVersion) { - LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) LOG(MODULE_INIT, "Qv2ray " QV2RAY_VERSION_STRING " on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture()) + DEBUG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) DEBUG("QV2RAY_BUILD_INFO", QV2RAY_BUILD_INFO) DEBUG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO) DEBUG("QV2RAY_BUILD_NUMBER", QSTRN(QV2RAY_VERSION_BUILD)) - // - hTray = new QSystemTrayIcon(this); + hTray = new QSystemTrayIcon(); } bool Qv2rayApplication::SetupQv2ray() @@ -37,6 +37,7 @@ namespace Qv2ray #ifdef Q_OS_WIN SetCurrentDirectory(applicationDirPath().toStdWString().c_str()); #endif + // Install a default translater. From the OS/DE Qv2rayTranslator = std::make_unique(); const auto &systemLang = QLocale::system().name(); @@ -45,19 +46,72 @@ namespace Qv2ray // setQuitOnLastWindowClosed(false); connect(this, &SingleApplication::receivedMessage, this, &Qv2rayApplication::onMessageReceived); + connect(this, &SingleApplication::aboutToQuit, this, &Qv2rayApplication::aboutToQuitSlot); if (isSecondary()) { sendMessage(JsonToString(Qv2rayProcessArgument.toJson(), QJsonDocument::Compact).toUtf8()); return false; } + +#ifdef Q_OS_WIN + // Set special font in Windows + QFont font; + font.setPointSize(9); + font.setFamily("Microsoft YaHei"); + app.application->setFont(font); +#endif + +#ifdef Q_OS_LINUX + setFallbackSessionManagementEnabled(false); + connect(this, &QGuiApplication::commitDataRequest, [] { + ConnectionManager->SaveConnectionConfig(); + LOG(MODULE_INIT, "Quit triggered by session manager.") + }); +#endif return true; } + void Qv2rayApplication::aboutToQuitSlot() + { + delete mainWindow; + delete hTray; + delete ConnectionManager; + delete RouteManager; + delete PluginHost; + delete StyleManager; + } + void Qv2rayApplication::onMessageReceived(quint32 clientId, QByteArray _msg) { const auto msg = Qv2rayProcessArguments::fromJson(JsonFromString(_msg)); LOG(MODULE_INIT, "Client ID: " + QSTRN(clientId) + ", message received, version: " + msg.version) DEBUG(MODULE_INIT, _msg) + // + const auto currentVersion = semver::version::from_string(QV2RAY_VERSION_STRING); + const auto newVersion = semver::version::from_string(msg.version.toStdString()); + // + if (newVersion > currentVersion) + { + QTimer::singleShot(0, [=]() { + const auto newPath = msg.fullArgs.first(); + QString message; + message += tr("A new version of Qv2ray is attemping to start:") + NEWLINE; + message += NEWLINE; + message += tr("New version information: ") + NEWLINE; + message += tr("Qv2ray version: %1").arg(msg.version) + NEWLINE; + message += tr("Qv2ray path: %1").arg(newPath) + NEWLINE; + message += NEWLINE; + message += tr("Do you want to exit and launch that new version?"); + + const auto result = QvMessageBoxAsk(nullptr, tr("New version detected"), message); + if (result == QMessageBox::Yes) + { + Qv2rayProcessArgument._qvNewVersionPath = newPath; + ExitQv2ray(QV2RAY_EXITCODE_NEWVERSION); + } + }); + } + for (const auto &argument : msg.arguments) { switch (argument) @@ -112,7 +166,15 @@ namespace Qv2ray { // Show MainWindow mainWindow = new MainWindow(); - return exec(); + exec(); + if (Qv2rayProcessArgument._qvNewVersionPath.isEmpty()) + { + return 0; + } + else + { + return QV2RAY_EXITCODE_NEWVERSION; + } } bool Qv2rayApplication::FindAndCreateInitialConfiguration() @@ -318,15 +380,6 @@ namespace Qv2ray StyleManager->ApplyStyle(GlobalConfig.uiConfig.theme); } - void Qv2rayApplication::DeallocateGlobalVariables() - { - delete mainWindow; - delete ConnectionManager; - delete RouteManager; - delete PluginHost; - delete StyleManager; - } - bool Qv2rayApplication::PreInitilize(int argc, char *argv[]) { QString errorMessage; @@ -336,15 +389,15 @@ namespace Qv2ray const auto &args = coreApp.arguments(); Qv2rayProcessArgument.version = QV2RAY_VERSION_STRING; Qv2rayProcessArgument.fullArgs = args; - switch (ParseCommandLine(&errorMessage)) + switch (ParseCommandLine(&errorMessage, args)) { case QV2RAY_QUIT: return false; case QV2RAY_ERROR: LOG(MODULE_INIT, errorMessage) return false; default: break; } #ifdef Q_OS_WIN - const auto urlScheme = applicationName(); - const auto appPath = QDir::toNativeSeparators(applicationFilePath()); + const auto urlScheme = coreApp.applicationName(); + const auto appPath = QDir::toNativeSeparators(coreApp.applicationFilePath()); const auto regPath = "HKEY_CURRENT_USER\\Software\\Classes\\" + urlScheme; QSettings reg(regPath, QSettings::NativeFormat); @@ -381,7 +434,7 @@ namespace Qv2ray return true; } - Qv2rayApplication::commandline_status Qv2rayApplication::ParseCommandLine(QString *errorMessage) + Qv2rayApplication::commandline_status Qv2rayApplication::ParseCommandLine(QString *errorMessage, const QStringList &args) { QCommandLineParser parser; // @@ -407,7 +460,7 @@ namespace Qv2ray auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); - if (!parser.parse(arguments())) + if (!parser.parse(args)) { *errorMessage = parser.errorText(); return QV2RAY_ERROR; diff --git a/src/Qv2rayApplication.hpp b/src/Qv2rayApplication.hpp index 013a561a..90f45384 100644 --- a/src/Qv2rayApplication.hpp +++ b/src/Qv2rayApplication.hpp @@ -24,7 +24,9 @@ namespace Qv2ray QString data; QList links; QList fullArgs; - + // + int _exitCode; + QString _qvNewVersionPath; JSONSTRUCT_REGISTER(Qv2rayProcessArguments, F(arguments, version, data, links, fullArgs)) }; @@ -48,7 +50,6 @@ namespace Qv2ray bool LoadConfiguration(); void InitilizeGlobalVariables(); int RunQv2ray(); - void DeallocateGlobalVariables(); public: QSystemTrayIcon **GetTrayIcon() @@ -65,12 +66,13 @@ namespace Qv2ray } private slots: + void aboutToQuitSlot(); void onMessageReceived(quint32 clientID, QByteArray msg); private: QSystemTrayIcon *hTray; MainWindow *mainWindow; - static commandline_status ParseCommandLine(QString *errorMessage); + static commandline_status ParseCommandLine(QString *errorMessage, const QStringList &args); bool initilized = false; }; } // namespace Qv2ray diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 3dcb6028..39be9691 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -112,15 +112,17 @@ using namespace Qv2ray::base::objects::transfer; namespace Qv2ray { + constexpr auto QV2RAY_EXITCODE_NORMAL = 0; + constexpr auto QV2RAY_EXITCODE_NEWVERSION = -9; // Qv2ray runtime config inline bool isExiting = false; inline QString Qv2rayConfigPath = ""; inline base::config::Qv2rayConfigObject GlobalConfig = base::config::Qv2rayConfigObject(); // - inline void ExitQv2ray() + inline void ExitQv2ray(int retcode = QV2RAY_EXITCODE_NORMAL) { isExiting = true; - QCoreApplication::quit(); + QCoreApplication::exit(retcode); } inline QStringList Qv2rayAssetsPaths(const QString &dirName) diff --git a/src/main.cpp b/src/main.cpp index 458d9729..3affc079 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,11 @@ #include "Qv2rayApplication.hpp" #include "common/QvHelpers.hpp" #include "core/handler/ConfigHandler.hpp" -#include "core/settings/SettingsBackend.hpp" -#include #include #include -#include +#include #include -#include #include #include @@ -19,32 +16,8 @@ void signalHandler(int signum) qvApp->exit(-99); } -int main(int argc, char *argv[]) +int RunQv2rayApplicationScoped(int argc, char *argv[]) { -#ifndef Q_OS_WIN - // Register signal handlers. - signal(SIGINT, signalHandler); - signal(SIGHUP, signalHandler); - signal(SIGKILL, signalHandler); - signal(SIGTERM, signalHandler); -#endif - // - // This line must be called before any other ones, since we are using these - // values to identify instances. - Qv2rayApplication::setApplicationVersion(QV2RAY_VERSION_STRING); - // -#ifdef QT_DEBUG - Qv2rayApplication::setApplicationName("qv2ray_debug"); - Qv2rayApplication::setApplicationDisplayName("Qv2ray - " + QObject::tr("Debug version")); -#else - Qv2rayApplication::setApplicationName("qv2ray"); - Qv2rayApplication::setApplicationDisplayName("Qv2ray"); -#endif - // - // parse the command line before starting as a Qt application - if (!Qv2rayApplication::PreInitilize(argc, argv)) - return -1; - Qv2rayApplication app(argc, argv); if (!app.SetupQv2ray()) @@ -108,27 +81,47 @@ int main(int argc, char *argv[]) app.InitilizeGlobalVariables(); -#ifdef Q_OS_WIN - // Set special font in Windows - QFont font; - font.setPointSize(9); - font.setFamily("Microsoft YaHei"); - app.setFont(font); -#endif - #ifndef Q_OS_WIN signal(SIGUSR1, [](int) { ConnectionManager->RestartConnection(); }); signal(SIGUSR2, [](int) { ConnectionManager->StopConnection(); }); #endif + return app.RunQv2ray(); +} -#ifdef Q_OS_LINUX - qvApp->setFallbackSessionManagementEnabled(false); - QObject::connect(qvApp, &QGuiApplication::commitDataRequest, [] { - ConnectionManager->SaveConnectionConfig(); - LOG(MODULE_INIT, "Quit triggered by session manager.") - }); +int main(int argc, char *argv[]) +{ +#ifndef Q_OS_WIN + // Register signal handlers. + signal(SIGINT, signalHandler); + signal(SIGHUP, signalHandler); + signal(SIGKILL, signalHandler); + signal(SIGTERM, signalHandler); #endif - auto rcode = app.RunQv2ray(); - app.DeallocateGlobalVariables(); + // + // This line must be called before any other ones, since we are using these + // values to identify instances. + QApplication::setApplicationVersion(QV2RAY_VERSION_STRING); + // +#ifdef QT_DEBUG + QApplication::setApplicationName("qv2ray_debug"); + QApplication::setApplicationDisplayName("Qv2ray - " + QObject::tr("Debug version")); +#else + QApplication::setApplicationName("qv2ray"); + QApplication::setApplicationDisplayName("Qv2ray"); +#endif + // + // parse the command line before starting as a Qt application + if (!Qv2rayApplication::PreInitilize(argc, argv)) + return -1; + const auto rcode = RunQv2rayApplicationScoped(argc, argv); + switch (rcode) + { + case QV2RAY_EXITCODE_NEWVERSION: + { + QProcess::startDetached(Qv2rayProcessArgument._qvNewVersionPath, {}); + break; + } + default: break; + } return rcode; }