From 4cd00df5039598e60f21c5a7dfd8f738c91d1c13 Mon Sep 17 00:00:00 2001 From: QxQ <59914293+U-v-U@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:24:02 +0800 Subject: [PATCH] refactor: fixed SingleApplication crash, refactored --- 3rdparty/SingleApplication | 2 +- cmake/components/qv2ray-base.cmake | 1 + makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 50 +-- src/base/Qv2rayBaseApplication.cpp | 373 +++--------------- src/base/Qv2rayBaseApplication.hpp | 66 ++-- src/base/Qv2rayLog.hpp | 6 +- src/base/models/QvComplexConfigModels.hpp | 2 +- src/base/models/QvStartupConfig.hpp | 26 -- src/components/plugins/QvPluginHost.cpp | 4 +- src/components/translations/QvTranslator.cpp | 2 +- src/core/kernel/APIBackend.cpp | 12 - src/core/kernel/V2RayKernelInteractions.cpp | 5 +- src/core/settings/SettingsBackend.cpp | 177 ++++++++- src/core/settings/SettingsBackend.hpp | 1 + src/main.cpp | 89 ++--- src/ui/Qv2rayPlatformApplication.cpp | 221 +++++++++++ src/ui/Qv2rayPlatformApplication.hpp | 92 +---- src/ui/widgets/Qv2rayWidgetApplication.cpp | 316 ++++++++------- src/ui/widgets/Qv2rayWidgetApplication.hpp | 5 +- src/ui/widgets/styles/StyleManager.cpp | 2 +- src/ui/widgets/windows/w_MainWindow_extra.cpp | 2 +- .../widgets/windows/w_PreferencesWindow.cpp | 2 +- test/src/components/latency/TestRealPing.cpp | 10 +- 24 files changed, 710 insertions(+), 758 deletions(-) create mode 100644 src/ui/Qv2rayPlatformApplication.cpp diff --git a/3rdparty/SingleApplication b/3rdparty/SingleApplication index 9a408690..0e42d86c 160000 --- a/3rdparty/SingleApplication +++ b/3rdparty/SingleApplication @@ -1 +1 @@ -Subproject commit 9a408690ceb260a220d50f5070af9bcda58cacc2 +Subproject commit 0e42d86cecf008e850bc1f7540196de1f6bf182d diff --git a/cmake/components/qv2ray-base.cmake b/cmake/components/qv2ray-base.cmake index 3f4b6147..0c24519c 100644 --- a/cmake/components/qv2ray-base.cmake +++ b/cmake/components/qv2ray-base.cmake @@ -6,6 +6,7 @@ set(QV2RAY_BASE_SOURCES ${QV2RAY_BASEDIR_BASE}/Qv2rayBaseApplication.cpp ${QV2RAY_BASEDIR_BASE}/Qv2rayBaseApplication.hpp # Platform Dependent UI + ${CMAKE_SOURCE_DIR}/src/ui/Qv2rayPlatformApplication.cpp ${CMAKE_SOURCE_DIR}/src/ui/Qv2rayPlatformApplication.hpp # ${QV2RAY_BASEDIR_BASE}/JsonHelpers.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a77fd92c..9719c24c 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -6000 +6001 diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index fbc00bcb..fd524d61 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -47,7 +47,7 @@ using namespace Qv2ray::base::objects::transfer; #endif // Get Configured Config Dir Path -#define QV2RAY_CONFIG_DIR (qvApplicationInstance->ConfigPath) +#define QV2RAY_CONFIG_DIR (QvCoreApplication->ConfigPath) #define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf") // #define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/") @@ -67,13 +67,6 @@ using namespace Qv2ray::base::objects::transfer; #error Both QV2RAY_DEFAULT_VCORE_PATH and QV2RAY_DEFAULT_VASSETS_PATH need to be presented when using manually specify the paths. #endif -#define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray" QV2RAY_EXECUTABLE_SUFFIX) -#define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl" QV2RAY_EXECUTABLE_SUFFIX) - -constexpr auto QV2RAY_VCORE_LOG_DIRNAME = "logs/"; -constexpr auto QV2RAY_VCORE_ACCESS_LOG_FILENAME = "access.log"; -constexpr auto QV2RAY_VCORE_ERROR_LOG_FILENAME = "error.log"; - #ifdef Q_OS_MACOS #define ACCESS_OPTIONAL_VALUE(obj) (*obj) #else @@ -85,49 +78,14 @@ constexpr auto QV2RAY_VCORE_ERROR_LOG_FILENAME = "error.log"; #define OUTBOUND_TAG_BLACKHOLE "BLACKHOLE" #define OUTBOUND_TAG_DIRECT "DIRECT" #define OUTBOUND_TAG_PROXY "PROXY" -#define OUTBOUND_TAG_FORWARD_PROXY "_QV2RAY_FORWARD_PROXY_" +#define OUTBOUND_TAG_FORWARD_PROXY "QV2RAY_FORWARD_PROXY" -#define API_TAG_DEFAULT "_QV2RAY_API_" -#define API_TAG_INBOUND "_QV2RAY_API_INBOUND_" +#define API_TAG_DEFAULT "QV2RAY_API" +#define API_TAG_INBOUND "QV2RAY_API_INBOUND" #define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_" namespace Qv2ray { - inline QStringList Qv2rayAssetsPaths(const QString &dirName) - { -#define makeAbs(p) QDir(p).absolutePath() - // Configuration Path - QStringList list; - // This is the default behavior on Windows - list << makeAbs(QCoreApplication::applicationDirPath() + "/" + dirName); - list << makeAbs(QV2RAY_CONFIG_DIR + dirName); - list << ":/" + dirName; - // - list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory); - list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory); - -#ifdef Q_OS_LINUX - // For AppImage? - if (qEnvironmentVariableIsSet("APPIMAGE")) - list << makeAbs(QCoreApplication::applicationDirPath() + "/../share/qv2ray/" + dirName); - // For Snap - if (qEnvironmentVariableIsSet("SNAP")) - list << makeAbs(qEnvironmentVariable("SNAP") + "/usr/share/qv2ray/" + dirName); - // Linux platform directories. - list << makeAbs("/usr/local/lib/qv2ray/" + dirName); - list << makeAbs("/usr/lib/qv2ray/" + dirName); - list << makeAbs("/lib/qv2ray/" + dirName); - // - list << makeAbs("/usr/local/share/qv2ray/" + dirName); - list << makeAbs("/usr/share/qv2ray/" + dirName); -#elif defined(Q_OS_MAC) - // macOS platform directories. - list << QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath(); -#endif - list.removeDuplicates(); - return list; -#undef makeAbs - } } // namespace Qv2ray diff --git a/src/base/Qv2rayBaseApplication.cpp b/src/base/Qv2rayBaseApplication.cpp index e98dc6c1..222de925 100644 --- a/src/base/Qv2rayBaseApplication.cpp +++ b/src/base/Qv2rayBaseApplication.cpp @@ -4,349 +4,68 @@ #include "core/settings/SettingsBackend.hpp" #include "utils/QvHelpers.hpp" -#ifdef QT_DEBUG -const static inline QString QV2RAY_URL_SCHEME = "qv2ray-debug"; -#else -const static inline QString QV2RAY_URL_SCHEME = "qv2ray"; -#endif - #define QV_MODULE_NAME "BaseApplication" -constexpr auto QV2RAY_CONFIG_PATH_ENV_NAME = "QV2RAY_CONFIG_PATH"; +inline QString makeAbs(const QString &p) +{ + return QDir(p).absolutePath(); +} -Qv2rayApplicationManager::Qv2rayApplicationManager() +Qv2rayApplicationInterface::Qv2rayApplicationInterface() { ConfigObject = new Qv2rayConfigObject; - qvApplicationInstance = this; - LOG("Qv2ray " QV2RAY_VERSION_STRING " on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture()); - DEBUG("Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())); - DEBUG(std::string{ "QV2RAY_BUILD_INFO" }, QV2RAY_BUILD_INFO); - DEBUG(std::string{ "QV2RAY_BUILD_EXTRA_INFO" }, QV2RAY_BUILD_EXTRA_INFO); - DEBUG(std::string{ "QV2RAY_BUILD_NUMBER" }, QSTRN(QV2RAY_VERSION_BUILD)); + QvCoreApplication = this; + LOG("Qv2ray", QV2RAY_VERSION_STRING, "on", QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()); + DEBUG("Qv2ray Start Time: ", 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)); + QStringList licenseList; + licenseList << "This program comes with ABSOLUTELY NO WARRANTY."; + licenseList << "This is free software, and you are welcome to redistribute it"; + licenseList << "under certain conditions."; + licenseList << "Copyright (c) 2019-2020 Qv2ray Development Group."; + licenseList << "Third-party libraries that have been used in this program can be found in the About page."; + LOG(licenseList.join(NEWLINE)); } -Qv2rayApplicationManager::~Qv2rayApplicationManager() +Qv2rayApplicationInterface::~Qv2rayApplicationInterface() { delete ConfigObject; - qvApplicationInstance = nullptr; + QvCoreApplication = nullptr; } -bool Qv2rayApplicationManager::LocateConfiguration() +QStringList Qv2rayApplicationInterface::GetAssetsPaths(const QString &dirName) const { - LOG("Application exec path: " + qApp->applicationDirPath()); - // Non-standard paths needs special handing for "_debug" - const auto currentPathConfig = qApp->applicationDirPath() + "/config" QV2RAY_CONFIG_DIR_SUFFIX; - const auto homeQv2ray = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX; - // - // Standard paths already handles the "_debug" suffix for us. - const auto configQv2ray = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); - // - // - // Some built-in search paths for Qv2ray to find configs. (load the first one if possible). - const auto useManualConfigPath = qEnvironmentVariableIsSet(QV2RAY_CONFIG_PATH_ENV_NAME); - const auto manualConfigPath = qEnvironmentVariable(QV2RAY_CONFIG_PATH_ENV_NAME); - // - QStringList configFilePaths; - if (useManualConfigPath) - { - LOG("Using config path from env: " + manualConfigPath); - configFilePaths << manualConfigPath; - } - else - { - configFilePaths << currentPathConfig; - configFilePaths << configQv2ray; - configFilePaths << homeQv2ray; - } + // Configuration Path + QStringList list; - QString configPath = ""; - bool hasExistingConfig = false; - for (const auto &path : configFilePaths) - { - // Verify the config path, check if the config file exists and in the - // correct JSON format. True means we check for config existence as - // well. ---------------------------------------------|HERE| - bool isValidConfigPath = CheckSettingsPathAvailability(path, true); + // Default behavior on Windows + list << makeAbs(QCoreApplication::applicationDirPath() + "/" + dirName); + list << makeAbs(QV2RAY_CONFIG_DIR + dirName); + list << ":/" + dirName; - // If we already found a valid config file. just simply load it... - if (hasExistingConfig) - break; + list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory); + list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory); - if (isValidConfigPath) - { - DEBUG("Path:", path, " is valid."); - configPath = path; - hasExistingConfig = true; - } - else - { - LOG("Path:", path, "does not contain a valid config file."); - } - } +#ifdef Q_OS_UNIX + if (qEnvironmentVariableIsSet("APPIMAGE")) + list << makeAbs(QCoreApplication::applicationDirPath() + "/../share/qv2ray/" + dirName); - if (hasExistingConfig) - { - // Use the config path found by the checks above - SetConfigDirPath(configPath); - LOG("Using ", QV2RAY_CONFIG_DIR, " as the config path."); - } - else - { - // If there's no existing config. - // - // Create new config at these dirs, these are default values for each platform. - if (useManualConfigPath) - { - configPath = manualConfigPath; - } - else - { -#if defined(Q_OS_WIN) && !defined(QV2RAY_NO_ASIDECONFIG) - configPath = currentPathConfig; -#else - configPath = configQv2ray; + if (qEnvironmentVariableIsSet("SNAP")) + list << makeAbs(qEnvironmentVariable("SNAP") + "/usr/share/qv2ray/" + dirName); + + list << makeAbs("/usr/local/share/qv2ray/" + dirName); + list << makeAbs("/usr/local/lib/qv2ray/" + dirName); + list << makeAbs("/usr/share/qv2ray/" + dirName); + list << makeAbs("/usr/lib/qv2ray/" + dirName); + list << makeAbs("/lib/qv2ray/" + dirName); #endif - } - bool hasPossibleNewLocation = QDir().mkpath(configPath) && CheckSettingsPathAvailability(configPath, false); - // Check if the dirs are write-able - if (!hasPossibleNewLocation) - { - // None of the path above can be used as a dir for storing config. - // Even the last folder failed to pass the check. - LOG("FATAL"); - LOG(" ---> CANNOT find a proper place to store Qv2ray config files."); - QvMessageBoxWarn(nullptr, QObject::tr("Cannot Start Qv2ray"), - QObject::tr("Cannot find a place to store config files.") + NEWLINE + // - QObject::tr("Qv2ray has searched these paths below:") + NEWLINE + NEWLINE + // - configFilePaths.join(NEWLINE) + NEWLINE + // - QObject::tr("It usually means you don't have the write permission to all of those locations.") + NEWLINE + // - QObject::tr("Qv2ray will now exit.")); // - return false; - } - - // Found a valid config dir, with write permission, but assume no config is located in it. - LOG("Set " + configPath + " as the config path."); - SetConfigDirPath(configPath); - - if (QFile::exists(QV2RAY_CONFIG_FILE)) - { - // As we already tried to load config from every possible dir. - // - // This condition branch (!hasExistingConfig check) holds the fact that current config dir, - // should NOT contain any valid file (at least in the same name) - // - // It usually means that QV2RAY_CONFIG_FILE here has a corrupted JSON format. - // - // Otherwise Qv2ray would have loaded this config already instead of notifying to create a new config in this folder. - // - LOG("This should not occur: Qv2ray config exists but failed to load."); - QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"), - QObject::tr("Failed to determine the location of config file:") + NEWLINE + // - QObject::tr("Qv2ray has found a config file, but it failed to be loaded due to some errors.") + NEWLINE + // - QObject::tr("A workaround is to remove the this file and restart Qv2ray:") + NEWLINE + // - QV2RAY_CONFIG_FILE + NEWLINE + // - QObject::tr("Qv2ray will now exit.") + NEWLINE + // - QObject::tr("Please report if you think it's a bug.")); // - return false; - } - - GlobalConfig.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH)); - GlobalConfig.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH)); - GlobalConfig.logLevel = 3; - GlobalConfig.uiConfig.language = QLocale::system().name(); - GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "1.1.1.1" }); - GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "8.8.8.8" }); - GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "8.8.4.4" }); - - // Save initial config. - SaveGlobalSettings(); - LOG("Created initial config file."); - } - - if (!QDir(QV2RAY_GENERATED_DIR).exists()) - { - // The dir used to generate final config file, for V2Ray interaction. - QDir().mkdir(QV2RAY_GENERATED_DIR); - LOG("Created config generation dir at: " + QV2RAY_GENERATED_DIR); - } - // - // BEGIN LOAD CONFIGURATIONS - // - { - // Load the config for upgrade, but do not parse it to the struct. - auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE)); - const auto configVersion = conf["config_version"].toInt(); - - if (configVersion > QV2RAY_CONFIG_VERSION) - { - // Config version is larger than the current version... - // This is rare but it may happen.... - QvMessageBoxWarn(nullptr, QObject::tr("Qv2ray Cannot Continue"), // - QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE + // - QObject::tr("Please check if there's an issue explaining about it.") + NEWLINE + // - QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE + // - QObject::tr("Qv2ray will now exit.")); - return false; - } - else if (configVersion < QV2RAY_CONFIG_VERSION) - { - // That is the config file needs to be upgraded. - conf = Qv2ray::UpgradeSettingsVersion(configVersion, QV2RAY_CONFIG_VERSION, conf); - } - - // Let's save the config. - GlobalConfig.loadJson(conf); - const auto allTranslations = Qv2rayTranslator->GetAvailableLanguages(); - const auto osLanguage = QLocale::system().name(); - if (!allTranslations.contains(GlobalConfig.uiConfig.language)) - { - // If we need to reset the language. - if (allTranslations.contains(osLanguage)) - { - GlobalConfig.uiConfig.language = osLanguage; - } - else if (!allTranslations.isEmpty()) - { - GlobalConfig.uiConfig.language = allTranslations.first(); - } - } - - if (!Qv2rayTranslator->InstallTranslation(GlobalConfig.uiConfig.language)) - { - QvMessageBoxWarn(nullptr, "Translation Failed", - "Cannot load translation for " + GlobalConfig.uiConfig.language + NEWLINE + // - "English is now used." + NEWLINE + NEWLINE + // - "Please go to Preferences Window to change language or open an Issue"); - GlobalConfig.uiConfig.language = "en_US"; - } - SaveGlobalSettings(); - return true; - } -} - -Qv2rayPreInitResult Qv2rayApplicationManager::StaticPreInitialize(int argc, char **argv) -{ - QString errorMessage; - QCoreApplication coreApp(argc, argv); - const auto &args = coreApp.arguments(); - Qv2rayProcessArgument.version = QV2RAY_VERSION_STRING; - Qv2rayProcessArgument.fullArgs = args; - auto result = ParseCommandLine(&errorMessage, args); - LOG("Qv2ray PreInitialization: " + errorMessage); - if (result != PRE_INIT_RESULT_CONTINUE) - return result; -#ifdef Q_OS_WIN - const auto appPath = QDir::toNativeSeparators(coreApp.applicationFilePath()); - const auto regPath = "HKEY_CURRENT_USER\\Software\\Classes\\" + QV2RAY_URL_SCHEME; - - QSettings reg(regPath, QSettings::NativeFormat); - - reg.setValue("Default", "Qv2ray"); - reg.setValue("URL Protocol", ""); - - reg.beginGroup("DefaultIcon"); - reg.setValue("Default", QString("%1,1").arg(appPath)); - reg.endGroup(); - - reg.beginGroup("shell"); - reg.beginGroup("open"); - reg.beginGroup("command"); - reg.setValue("Default", appPath + " %1"); +#ifdef Q_OS_MAC + // macOS platform directories. + list << QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath(); #endif - return result; -} -Qv2rayPreInitResult Qv2rayApplicationManager::ParseCommandLine(QString *errorMessage, const QStringList &_argx_) -{ - QStringList filteredArgs; - for (const auto &arg : _argx_) - { -#ifdef Q_OS_MACOS - if (arg.contains("-psn")) - continue; -#endif - filteredArgs << arg; - } - QCommandLineParser parser; - // - QCommandLineOption noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystem")); - QCommandLineOption noPluginsOption("noPlugin", QObject::tr("Disable plugins feature")); - QCommandLineOption noScaleFactorOption("noScaleFactor", QObject::tr("Disable Qt UI scale factor")); - QCommandLineOption debugLogOption("debug", QObject::tr("Enable debug output")); - QCommandLineOption noAutoConnectionOption("noAutoConnection", QObject::tr("Do not automatically connect")); - QCommandLineOption disconnectOption("disconnect", QObject::tr("Stop current connection")); - QCommandLineOption reconnectOption("reconnect", QObject::tr("Reconnect last connection")); - QCommandLineOption exitOption("exit", QObject::tr("Exit Qv2ray")); - // - parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2Ray.")); - parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); - // - parser.addOption(noAPIOption); - parser.addOption(noPluginsOption); - parser.addOption(noScaleFactorOption); - parser.addOption(debugLogOption); - parser.addOption(noAutoConnectionOption); - parser.addOption(disconnectOption); - parser.addOption(reconnectOption); - parser.addOption(exitOption); - // - auto helpOption = parser.addHelpOption(); - auto versionOption = parser.addVersionOption(); - - if (!parser.parse(filteredArgs)) - { - *errorMessage = parser.errorText(); - return PRE_INIT_RESULT_CONTINUE; - } - - if (parser.isSet(versionOption)) - { - parser.showVersion(); - return PRE_INIT_RESULT_QUIT; - } - - if (parser.isSet(helpOption)) - { - parser.showHelp(); - return PRE_INIT_RESULT_QUIT; - } - - for (const auto &arg : parser.positionalArguments()) - { - if (arg.startsWith(QV2RAY_URL_SCHEME + "://")) - { - Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::QV2RAY_LINK; - Qv2rayProcessArgument.links << arg; - } - } - - if (parser.isSet(exitOption)) - { - DEBUG("disconnectOption is set."); - Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::EXIT; - } - - if (parser.isSet(disconnectOption)) - { - DEBUG("disconnectOption is set."); - Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::DISCONNECT; - } - - if (parser.isSet(reconnectOption)) - { - DEBUG("reconnectOption is set."); - Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::RECONNECT; - } - -#define ProcessExtraStartupOptions(option) \ - DEBUG("Startup Options:" A(parser.isSet(option##Option))); \ - StartupOption.option = parser.isSet(option##Option); - - ProcessExtraStartupOptions(noAPI); - ProcessExtraStartupOptions(debugLog); - ProcessExtraStartupOptions(noScaleFactor); - ProcessExtraStartupOptions(noAutoConnection); - ProcessExtraStartupOptions(noPlugins); - - *errorMessage = "OK"; - return PRE_INIT_RESULT_CONTINUE; + + list.removeDuplicates(); + return list; } diff --git a/src/base/Qv2rayBaseApplication.hpp b/src/base/Qv2rayBaseApplication.hpp index a0bce766..075010f0 100644 --- a/src/base/Qv2rayBaseApplication.hpp +++ b/src/base/Qv2rayBaseApplication.hpp @@ -7,17 +7,6 @@ namespace Qv2ray { - enum Qv2rayExitCode - { - QVEXIT_NORMAL = 0, - QVEXIT_SECONDARY_INSTANCE = 0, - QVEXIT_PRE_INITIALIZE_FAIL = -1, - QVEXIT_EARLY_SETUP_FAIL = -2, - QVEXIT_CONFIG_FILE_FAIL = -3, - QVEXIT_SSL_FAIL = -4, - QVEXIT_NEW_VERSION = -5 - }; - enum MessageOpt { OK, @@ -27,21 +16,17 @@ namespace Qv2ray Ignore }; - enum Qv2rayPreInitResult + enum Qv2rayExitReason { - PRE_INIT_RESULT_ERROR, - PRE_INIT_RESULT_QUIT, - PRE_INIT_RESULT_CONTINUE + EXIT_NORMAL = 0, + EXIT_NEW_VERSION_TRIGGER = EXIT_NORMAL, + EXIT_SECONDARY_INSTANCE = EXIT_NORMAL, + EXIT_PREINITIALIZATION_FAILED = -1, + EXIT_PRECONDITION_FAILED = -2, + EXIT_CRASHED = -9, }; - enum Qv2raySetupStatus - { - NORMAL, - SINGLE_APPLICATION, - FAILED - }; - - struct Qv2rayProcessArguments + struct Qv2rayStartupArguments { enum Argument { @@ -53,40 +38,45 @@ namespace Qv2ray }; QList arguments; QString version; + int buildVersion; QString data; QList links; QList fullArgs; // + bool noAPI; + bool noAutoConnection; + bool debugLog; + bool noPlugins; + bool exitQv2ray; + // QString _qvNewVersionPath; - JSONSTRUCT_REGISTER(Qv2rayProcessArguments, F(arguments, version, data, links, fullArgs)) + JSONSTRUCT_REGISTER(Qv2rayStartupArguments, F(arguments, data, version, links, fullArgs, buildVersion)) }; - inline Qv2rayProcessArguments Qv2rayProcessArgument; - class Qv2rayApplicationManager + class Qv2rayApplicationInterface { public: Qv2ray::base::config::Qv2rayConfigObject *ConfigObject; QString ConfigPath; + Qv2rayExitReason ExitReason; + Qv2rayStartupArguments StartupArguments; public: - static Qv2rayPreInitResult StaticPreInitialize(int argc, char **argv); - explicit Qv2rayApplicationManager(); - ~Qv2rayApplicationManager(); - virtual bool LocateConfiguration() final; + explicit Qv2rayApplicationInterface(); + ~Qv2rayApplicationInterface(); + + public: + virtual QStringList GetAssetsPaths(const QString &dirName) const final; // - virtual Qv2raySetupStatus Initialize() = 0; - virtual Qv2rayExitCode RunQv2ray() = 0; + virtual bool Initialize() = 0; + virtual Qv2rayExitReason RunQv2ray() = 0; // virtual void MessageBoxWarn(QWidget *parent, const QString &title, const QString &text, MessageOpt button) = 0; virtual void MessageBoxInfo(QWidget *parent, const QString &title, const QString &text, MessageOpt button) = 0; virtual MessageOpt MessageBoxAsk(QWidget *parent, const QString &title, const QString &text, const QList &buttons) = 0; virtual void OpenURL(const QString &url) = 0; - - private: - static Qv2rayPreInitResult ParseCommandLine(QString *errorMessage, const QStringList &_argx_); }; - inline Qv2rayApplicationManager *qvApplicationInstance = nullptr; + inline Qv2rayApplicationInterface *QvCoreApplication = nullptr; } // namespace Qv2ray -#define QvCoreApplication static_cast(qvApplicationInstance) -#define GlobalConfig (*Qv2ray::qvApplicationInstance->ConfigObject) +#define GlobalConfig (*Qv2ray::QvCoreApplication->ConfigObject) diff --git a/src/base/Qv2rayLog.hpp b/src/base/Qv2rayLog.hpp index 19a9eb13..31edf365 100644 --- a/src/base/Qv2rayLog.hpp +++ b/src/base/Qv2rayLog.hpp @@ -27,8 +27,8 @@ #define _LOG_ARG_(...) QV2RAY_LOG_PREPEND_CONTENT "[" QV_MODULE_NAME "]", __VA_ARGS__ -#define LOG(...) Qv2ray::base::log_concat(_LOG_ARG_(__VA_ARGS__)) -#define DEBUG(...) Qv2ray::base::log_concat(_LOG_ARG_(__VA_ARGS__)) +#define LOG(...) Qv2ray::base::log_internal(_LOG_ARG_(__VA_ARGS__)) +#define DEBUG(...) Qv2ray::base::log_internal(_LOG_ARG_(__VA_ARGS__)) enum QvLogType { @@ -51,7 +51,7 @@ namespace Qv2ray::base } template - inline void log_concat(T... v) + inline void log_internal(T... v) { ((logStream << v << " "), ...); ((tempStream << v << " "), ...); diff --git a/src/base/models/QvComplexConfigModels.hpp b/src/base/models/QvComplexConfigModels.hpp index c04503d6..0ba46fef 100644 --- a/src/base/models/QvComplexConfigModels.hpp +++ b/src/base/models/QvComplexConfigModels.hpp @@ -80,7 +80,7 @@ namespace Qv2ray::base::objects::complex return meta; } explicit OutboundObjectMeta() : metaType(METAOUTBOUND_ORIGINAL){}; - JSONSTRUCT_REGISTER(OutboundObjectMeta, F(metaType, displayName, connectionId, outboundTags)) + JSONSTRUCT_REGISTER(OutboundObjectMeta, F(metaType, displayName, connectionId, outboundTags, chainPortAllocation)) }; inline OutboundObjectMeta make_chained_outbound(const QList &chain, const QString &tag) diff --git a/src/base/models/QvStartupConfig.hpp b/src/base/models/QvStartupConfig.hpp index eef993d8..6f70f09b 100644 --- a/src/base/models/QvStartupConfig.hpp +++ b/src/base/models/QvStartupConfig.hpp @@ -1,27 +1 @@ #pragma once - -namespace Qv2ray::base -{ - struct QvStartupOptions - { - /// No API subsystem - bool noAPI; - - /// Do not automatically connect to a server - bool noAutoConnection; - - /// Enable Debug Log. - bool debugLog; - - /// Disable Qt scale factors support. - bool noScaleFactor; - - /// Disable all plugin features. - bool noPlugins; - - /// Exit existing Qv2ray instance - bool exitQv2ray; - }; -} // namespace Qv2ray::base - -inline Qv2ray::base::QvStartupOptions StartupOption = Qv2ray::base::QvStartupOptions(); diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 2816fa15..96601969 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -12,7 +12,7 @@ namespace Qv2ray::components::plugins { QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { - if (!StartupOption.noPlugins) + if (!QvCoreApplication->StartupArguments.noPlugins) { if (auto dir = QDir(QV2RAY_PLUGIN_SETTINGS_DIR); !dir.exists()) { @@ -30,7 +30,7 @@ namespace Qv2ray::components::plugins { clearPlugins(); LOG("Reloading plugin list"); - for (const auto &pluginDirPath : Qv2rayAssetsPaths("plugins")) + for (const auto &pluginDirPath : QvCoreApplication->GetAssetsPaths("plugins")) { const QStringList entries = QDir(pluginDirPath).entryList(QDir::Files); for (const auto &fileName : entries) diff --git a/src/components/translations/QvTranslator.cpp b/src/components/translations/QvTranslator.cpp index b399340f..3014bf7f 100644 --- a/src/components/translations/QvTranslator.cpp +++ b/src/components/translations/QvTranslator.cpp @@ -11,7 +11,7 @@ using namespace Qv2ray::base; QStringList getLanguageSearchPaths() { // Configuration Path - QStringList list = Qv2rayAssetsPaths("lang"); + QStringList list = QvCoreApplication->GetAssetsPaths("lang"); #ifdef QV2RAY_EMBED_TRANSLATIONS // If the translations have been embedded. list << QString(":/translations/"); diff --git a/src/core/kernel/APIBackend.cpp b/src/core/kernel/APIBackend.cpp index 3530c7f9..150f19b5 100644 --- a/src/core/kernel/APIBackend.cpp +++ b/src/core/kernel/APIBackend.cpp @@ -19,18 +19,6 @@ namespace Qv2ray::core::kernel { API_OUTBOUND_DIRECT, { "freedom" } }, { API_OUTBOUND_BLACKHOLE, { "blackhole" } } }; - // To all contributors: - // - // You may feel it difficult to understand this part of API backend. - // It's been expected that you will take hours to fully understand the tricks and hacks lying deeply in this class. - // - // The API Worker runs as a daemon together with Qv2ray, on a single thread. - // They use a flag, running, to indicate if the API worker should go and fetch the statistics from V2Ray Core. - // - // The flag, running, will be set to true, immediately after the V2Ray core reported that it's been started. - // and will be set to false right before we stopping V2Ray Core. - // - APIWorker::APIWorker() { workThread = new QThread(); diff --git a/src/core/kernel/V2RayKernelInteractions.cpp b/src/core/kernel/V2RayKernelInteractions.cpp index b3837675..b3c347eb 100644 --- a/src/core/kernel/V2RayKernelInteractions.cpp +++ b/src/core/kernel/V2RayKernelInteractions.cpp @@ -286,12 +286,11 @@ namespace Qv2ray::core::kernel if (ValidateConfig(filePath)) { - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + auto env = QProcessEnvironment::systemEnvironment(); env.insert("V2RAY_LOCATION_ASSET", GlobalConfig.kernelConfig.AssetsPath()); vProcess->setProcessEnvironment(env); vProcess->start(GlobalConfig.kernelConfig.KernelPath(), { "-config", filePath }, QIODevice::ReadWrite | QIODevice::Text); vProcess->waitForStarted(); - DEBUG("V2Ray core started."); KernelStarted = true; QMap> tagProtocolMap; @@ -312,7 +311,7 @@ namespace Qv2ray::core::kernel } apiEnabled = false; - if (StartupOption.noAPI) + if (QvCoreApplication->StartupArguments.noAPI) { LOG("API has been disabled by the command line arguments"); } diff --git a/src/core/settings/SettingsBackend.cpp b/src/core/settings/SettingsBackend.cpp index 273182d7..a4d5ec46 100644 --- a/src/core/settings/SettingsBackend.cpp +++ b/src/core/settings/SettingsBackend.cpp @@ -1,8 +1,10 @@ #include "SettingsBackend.hpp" +#include "base/Qv2rayLog.hpp" #include "utils/QvHelpers.hpp" #define QV_MODULE_NAME "SettingsBackend" +constexpr auto QV2RAY_CONFIG_PATH_ENV_NAME = "QV2RAY_CONFIG_PATH"; namespace Qv2ray::core::config { @@ -14,11 +16,11 @@ namespace Qv2ray::core::config void SetConfigDirPath(const QString &path) { - qvApplicationInstance->ConfigPath = path; + QvCoreApplication->ConfigPath = path; if (!path.endsWith("/")) { - qvApplicationInstance->ConfigPath += "/"; + QvCoreApplication->ConfigPath += "/"; } } @@ -90,6 +92,177 @@ namespace Qv2ray::core::config return true; } + bool LocateConfiguration() + { + LOG("Application exec path: " + qApp->applicationDirPath()); + // Non-standard paths needs special handing for "_debug" + const auto currentPathConfig = qApp->applicationDirPath() + "/config" QV2RAY_CONFIG_DIR_SUFFIX; + const auto homeQv2ray = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX; + // + // Standard paths already handles the "_debug" suffix for us. + const auto configQv2ray = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + // + // + // Some built-in search paths for Qv2ray to find configs. (load the first one if possible). + const auto useManualConfigPath = qEnvironmentVariableIsSet(QV2RAY_CONFIG_PATH_ENV_NAME); + const auto manualConfigPath = qEnvironmentVariable(QV2RAY_CONFIG_PATH_ENV_NAME); + // + QStringList configFilePaths; + if (useManualConfigPath) + { + LOG("Using config path from env: " + manualConfigPath); + configFilePaths << manualConfigPath; + } + else + { + configFilePaths << currentPathConfig; + configFilePaths << configQv2ray; + configFilePaths << homeQv2ray; + } + + QString configPath = ""; + bool hasExistingConfig = false; + for (const auto &path : configFilePaths) + { + // Verify the config path, check if the config file exists and in the + // correct JSON format. True means we check for config existence as + // well. ---------------------------------------------|HERE| + bool isValidConfigPath = CheckSettingsPathAvailability(path, true); + + // If we already found a valid config file. just simply load it... + if (hasExistingConfig) + break; + + if (isValidConfigPath) + { + DEBUG("Path:", path, " is valid."); + configPath = path; + hasExistingConfig = true; + } + else + { + LOG("Path:", path, "does not contain a valid config file."); + } + } + + if (hasExistingConfig) + { + // Use the config path found by the checks above + SetConfigDirPath(configPath); + LOG("Using ", QV2RAY_CONFIG_DIR, " as the config path."); + } + else + { + // If there's no existing config. + // + // Create new config at these dirs, these are default values for each platform. + if (useManualConfigPath) + { + configPath = manualConfigPath; + } + else + { +#if defined(Q_OS_WIN) && !defined(QV2RAY_NO_ASIDECONFIG) + configPath = currentPathConfig; +#else + configPath = configQv2ray; +#endif + } + + bool hasPossibleNewLocation = QDir().mkpath(configPath) && CheckSettingsPathAvailability(configPath, false); + // Check if the dirs are write-able + if (!hasPossibleNewLocation) + { + // None of the path above can be used as a dir for storing config. + // Even the last folder failed to pass the check. + LOG("FATAL"); + LOG(" ---> CANNOT find a proper place to store Qv2ray config files."); + QvMessageBoxWarn(nullptr, QObject::tr("Cannot Start Qv2ray"), + QObject::tr("Cannot find a place to store config files.") + NEWLINE + // + QObject::tr("Qv2ray has searched these paths below:") + NEWLINE + NEWLINE + // + configFilePaths.join(NEWLINE) + NEWLINE + // + QObject::tr("It usually means you don't have the write permission to all of those locations.") + NEWLINE + // + QObject::tr("Qv2ray will now exit.")); // + return false; + } + + // Found a valid config dir, with write permission, but assume no config is located in it. + LOG("Set " + configPath + " as the config path."); + SetConfigDirPath(configPath); + + if (QFile::exists(QV2RAY_CONFIG_FILE)) + { + // As we already tried to load config from every possible dir. + // + // This condition branch (!hasExistingConfig check) holds the fact that current config dir, + // should NOT contain any valid file (at least in the same name) + // + // It usually means that QV2RAY_CONFIG_FILE here has a corrupted JSON format. + // + // Otherwise Qv2ray would have loaded this config already instead of notifying to create a new config in this folder. + // + LOG("This should not occur: Qv2ray config exists but failed to load."); + QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"), + QObject::tr("Failed to determine the location of config file:") + NEWLINE + // + QObject::tr("Qv2ray has found a config file, but it failed to be loaded due to some errors.") + NEWLINE + // + QObject::tr("A workaround is to remove the this file and restart Qv2ray:") + NEWLINE + // + QV2RAY_CONFIG_FILE + NEWLINE + // + QObject::tr("Qv2ray will now exit.") + NEWLINE + // + QObject::tr("Please report if you think it's a bug.")); // + return false; + } + + GlobalConfig.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH)); + GlobalConfig.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH)); + GlobalConfig.logLevel = 3; + GlobalConfig.uiConfig.language = QLocale::system().name(); + GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "1.1.1.1" }); + GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "8.8.8.8" }); + GlobalConfig.defaultRouteConfig.dnsConfig.servers.append({ "8.8.4.4" }); + + // Save initial config. + SaveGlobalSettings(); + LOG("Created initial config file."); + } + + if (!QDir(QV2RAY_GENERATED_DIR).exists()) + { + // The dir used to generate final config file, for V2Ray interaction. + QDir().mkdir(QV2RAY_GENERATED_DIR); + LOG("Created config generation dir at: " + QV2RAY_GENERATED_DIR); + } + // + // BEGIN LOAD CONFIGURATIONS + // + { + // Load the config for upgrade, but do not parse it to the struct. + auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE)); + const auto configVersion = conf["config_version"].toInt(); + + if (configVersion > QV2RAY_CONFIG_VERSION) + { + // Config version is larger than the current version... + // This is rare but it may happen.... + QvMessageBoxWarn(nullptr, QObject::tr("Qv2ray Cannot Continue"), // + QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE + // + QObject::tr("Please check if there's an issue explaining about it.") + NEWLINE + // + QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE + // + QObject::tr("Qv2ray will now exit.")); + return false; + } + else if (configVersion < QV2RAY_CONFIG_VERSION) + { + // That is the config file needs to be upgraded. + conf = Qv2ray::UpgradeSettingsVersion(configVersion, QV2RAY_CONFIG_VERSION, conf); + } + + // Let's save the config. + GlobalConfig.loadJson(conf); + SaveGlobalSettings(); + return true; + } + } + } // namespace Qv2ray::core::config using namespace Qv2ray::core::config; diff --git a/src/core/settings/SettingsBackend.hpp b/src/core/settings/SettingsBackend.hpp index 398cae30..bef68cfb 100644 --- a/src/core/settings/SettingsBackend.hpp +++ b/src/core/settings/SettingsBackend.hpp @@ -4,6 +4,7 @@ namespace Qv2ray::core::config { void SaveGlobalSettings(); + bool LocateConfiguration(); void SetConfigDirPath(const QString &path); bool CheckSettingsPathAvailability(const QString &_path, bool checkExistingConfig); } // namespace Qv2ray::core::config diff --git a/src/main.cpp b/src/main.cpp index 575e7dcb..355f2b26 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,9 @@ #define QV_MODULE_NAME "Init" +#define EXITCODE_ERROR 1; +#define EXITCODE_NORMAL 0 + int globalArgc; char **globalArgv; @@ -177,6 +180,30 @@ LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS) } #endif +void ProcessExitReason(Qv2rayExitReason reason) +{ // app.MessageBoxWarn(nullptr, app.tr("Cannot start Qv2ray"), app.tr("Qv2ray early initialization failed.")); + // return QVEXIT_EARLY_SETUP_FAIL; + switch (reason) + { + case EXIT_CRASHED: + { + break; + } + case EXIT_NORMAL: + { + break; + } + case EXIT_PREINITIALIZATION_FAILED: + { + break; + } + case EXIT_PRECONDITION_FAILED: + { + break; + } + } +} + int main(int argc, char *argv[]) { globalArgc = argc; @@ -195,44 +222,20 @@ int main(int argc, char *argv[]) // This line must be called before any other ones, since we are using these // values to identify instances. QCoreApplication::setApplicationVersion(QV2RAY_VERSION_STRING); - // #ifdef QT_DEBUG QCoreApplication::setApplicationName("qv2ray_debug"); - // QApplication::setApplicationDisplayName("Qv2ray - " + QObject::tr("Debug version")); #else QCoreApplication::setApplicationName("qv2ray"); #ifdef QV2RAY_GUI QApplication::setApplicationDisplayName("Qv2ray"); #endif #endif - LOG("LICENCE", NEWLINE // - "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE // - "This is free software, and you are welcome to redistribute it" NEWLINE // - "under certain conditions." NEWLINE NEWLINE // - "Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE // - "Third-party libraries that have been used in Qv2ray can be found in the About page." NEWLINE); #ifdef QT_DEBUG std::cerr << "WARNING: ================ This is a debug build, many features are not stable enough. ================" << std::endl; #endif - // parse the command line before starting as a Qt application - switch (Qv2rayApplicationManager::StaticPreInitialize(argc, argv)) - { - case PRE_INIT_RESULT_QUIT: return QVEXIT_NORMAL; - case PRE_INIT_RESULT_CONTINUE: break; - case PRE_INIT_RESULT_ERROR: - { - BootstrapMessageBox("Cannot Start Qv2ray!", "Early initialization failed!"); - return QVEXIT_PRE_INITIALIZE_FAIL; - } - default: Q_UNREACHABLE(); - } -#ifndef QV2RAY_QT6 - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); -#endif - // noScaleFactors = disable HiDPI - if (StartupOption.noScaleFactor) + if (qEnvironmentVariableIsSet("QV2RAY_NO_SCALE_FACTORS")) { LOG("Force set QT_SCALE_FACTOR to 1."); DEBUG("UI", "Original QT_SCALE_FACTOR was:", qEnvironmentVariable("QT_SCALE_FACTOR")); @@ -251,6 +254,10 @@ int main(int argc, char *argv[]) #endif } +#ifndef QV2RAY_QT6 + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + // Check OpenSSL version for auto-update and subscriptions auto osslReqVersion = QSslSocket::sslLibraryBuildVersionString(); auto osslCurVersion = QSslSocket::sslLibraryVersionString(); @@ -268,28 +275,14 @@ int main(int argc, char *argv[]) "OSsl.Rq.V=" + osslReqVersion + NEWLINE + // "OSsl.Cr.V=" + osslCurVersion); BootstrapMessageBox(QObject::tr("Cannot start Qv2ray"), QObject::tr("Cannot start Qv2ray without OpenSSL")); - return QVEXIT_SSL_FAIL; + return -1; } Qv2rayApplication app(argc, argv); - switch (app.Initialize()) + if (!app.Initialize()) { - case NORMAL: break; - case SINGLE_APPLICATION: return QVEXIT_SECONDARY_INSTANCE; - case FAILED: - { - app.MessageBoxWarn(nullptr, app.tr("Cannot start Qv2ray"), app.tr("Qv2ray early initialization failed.")); - return QVEXIT_EARLY_SETUP_FAIL; - } - } - - // - // Qv2ray Initialize, find possible config paths and verify them. - if (!app.LocateConfiguration()) - { - LOG("Cannot load or create initial configuration file."); - app.MessageBoxWarn(nullptr, app.tr("Cannot start Qv2ray"), app.tr("Cannot load config file.")); - return QVEXIT_CONFIG_FILE_FAIL; + LOG("Qv2ray initialization failed:", app.ExitReason); + ProcessExitReason(app.ExitReason); } #ifndef Q_OS_WIN @@ -297,11 +290,11 @@ int main(int argc, char *argv[]) signal(SIGUSR2, [](int) { ConnectionManager->StopConnection(); }); #endif - const auto rcode = app.RunQv2ray(); - if (rcode == QVEXIT_NEW_VERSION) + app.RunQv2ray(); + if (app.ExitReason == EXIT_NEW_VERSION_TRIGGER) { - LOG("Starting new version of Qv2ray: " + Qv2rayProcessArgument._qvNewVersionPath); - QProcess::startDetached(Qv2rayProcessArgument._qvNewVersionPath, {}); + LOG("Starting new version of Qv2ray: " + app.StartupArguments._qvNewVersionPath); + QProcess::startDetached(app.StartupArguments._qvNewVersionPath, {}); } - return rcode; + return app.ExitReason; } diff --git a/src/ui/Qv2rayPlatformApplication.cpp b/src/ui/Qv2rayPlatformApplication.cpp new file mode 100644 index 00000000..0cac664d --- /dev/null +++ b/src/ui/Qv2rayPlatformApplication.cpp @@ -0,0 +1,221 @@ +#include "Qv2rayPlatformApplication.hpp" + +#include "core/settings/SettingsBackend.hpp" +#define QV_MODULE_NAME "PlatformApplication" + +#ifdef QT_DEBUG +const static inline QString QV2RAY_URL_SCHEME = "qv2ray-debug"; +#else +const static inline QString QV2RAY_URL_SCHEME = "qv2ray"; +#endif + +bool Qv2rayPlatformApplication::initializeInternal() +{ + QString errorMessage; + bool canContinue; + auto hasError = parseCommandLine(&errorMessage, &canContinue); + if (hasError && !canContinue) + return false; +#ifdef Q_OS_WIN + const auto appPath = QDir::toNativeSeparators(applicationFilePath()); + const auto regPath = "HKEY_CURRENT_USER\\Software\\Classes\\" + QV2RAY_URL_SCHEME; + QSettings reg(regPath, QSettings::NativeFormat); + reg.setValue("Default", "Qv2ray"); + reg.setValue("URL Protocol", ""); + reg.beginGroup("DefaultIcon"); + reg.setValue("Default", QString("%1,1").arg(appPath)); + reg.endGroup(); + reg.beginGroup("shell"); + reg.beginGroup("open"); + reg.beginGroup("command"); + reg.setValue("Default", appPath + " %1"); +#endif + + connect(this, &Qv2rayPlatformApplication::aboutToQuit, this, &Qv2rayPlatformApplication::quitInternal); +#ifndef QV2RAY_NO_SINGLEAPPLICATON + connect(this, &SingleApplication::receivedMessage, this, &Qv2rayPlatformApplication::onMessageReceived, Qt::QueuedConnection); + if (isSecondary()) + { + StartupArguments.version = QV2RAY_VERSION_STRING; + StartupArguments.fullArgs = arguments(); + if (StartupArguments.arguments.isEmpty()) + StartupArguments.arguments << Qv2rayStartupArguments::NORMAL; + bool status = sendMessage(JsonToString(StartupArguments.toJson(), QJsonDocument::Compact).toUtf8()); + if (!status) + LOG("Cannot send message."); + return true; + } +#endif +#ifdef QV2RAY_GUI + #ifdef Q_OS_LINUX + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + setFallbackSessionManagementEnabled(false); + #endif + connect(this, &QGuiApplication::commitDataRequest, [] { + RouteManager->SaveRoutes(); + ConnectionManager->SaveConnectionConfig(); + PluginHost->SavePluginSettings(); + SaveGlobalSettings(); + }); + #endif + #ifdef Q_OS_WIN + SetCurrentDirectory(applicationDirPath().toStdWString().c_str()); + // Set special font in Windows + QFont font; + font.setPointSize(9); + font.setFamily("Microsoft YaHei"); + setFont(font); + #endif +#endif + + // Install a default translater. From the OS/DE + Qv2rayTranslator = std::make_unique(); + Qv2rayTranslator->InstallTranslation(QLocale::system().name()); + // + // + LocateConfiguration(); + // + const auto allTranslations = Qv2rayTranslator->GetAvailableLanguages(); + const auto osLanguage = QLocale::system().name(); + if (!allTranslations.contains(GlobalConfig.uiConfig.language)) + { + // If we need to reset the language. + if (allTranslations.contains(osLanguage)) + { + GlobalConfig.uiConfig.language = osLanguage; + } + else if (!allTranslations.isEmpty()) + { + GlobalConfig.uiConfig.language = allTranslations.first(); + } + } + + if (!Qv2rayTranslator->InstallTranslation(GlobalConfig.uiConfig.language)) + { + QvMessageBoxWarn(nullptr, "Translation Failed", + "Cannot load translation for " + GlobalConfig.uiConfig.language + NEWLINE + // + "English is now used." + NEWLINE + NEWLINE + // + "Please go to Preferences Window to change language or open an Issue"); + GlobalConfig.uiConfig.language = "en_US"; + } + + return true; +} + +bool Qv2rayPlatformApplication::runInternal() +{ + PluginHost = new QvPluginHost(); + RouteManager = new RouteHandler(); + ConnectionManager = new QvConfigHandler(); + return true; +} + +void Qv2rayPlatformApplication::quitInternal() +{ + // Do not change the order. + ConnectionManager->StopConnection(); + RouteManager->SaveRoutes(); + ConnectionManager->SaveConnectionConfig(); + PluginHost->SavePluginSettings(); + SaveGlobalSettings(); + TerminateUI(); + delete ConnectionManager; + delete RouteManager; + delete PluginHost; + ConnectionManager = nullptr; + RouteManager = nullptr; + PluginHost = nullptr; +} + +bool Qv2rayPlatformApplication::parseCommandLine(QString *errorMessage, bool *canContinue) +{ + *canContinue = true; + QStringList filteredArgs; + for (const auto &arg : arguments()) + { +#ifdef Q_OS_MACOS + if (arg.contains("-psn")) + continue; +#endif + filteredArgs << arg; + } + QCommandLineParser parser; + // + QCommandLineOption noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystem")); + QCommandLineOption noPluginsOption("noPlugin", QObject::tr("Disable plugins feature")); + QCommandLineOption debugLogOption("debug", QObject::tr("Enable debug output")); + QCommandLineOption noAutoConnectionOption("noAutoConnection", QObject::tr("Do not automatically connect")); + QCommandLineOption disconnectOption("disconnect", QObject::tr("Stop current connection")); + QCommandLineOption reconnectOption("reconnect", QObject::tr("Reconnect last connection")); + QCommandLineOption exitOption("exit", QObject::tr("Exit Qv2ray")); + // + parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2Ray.")); + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + // + parser.addOption(noAPIOption); + parser.addOption(noPluginsOption); + parser.addOption(debugLogOption); + parser.addOption(noAutoConnectionOption); + parser.addOption(disconnectOption); + parser.addOption(reconnectOption); + parser.addOption(exitOption); + // + const auto helpOption = parser.addHelpOption(); + const auto versionOption = parser.addVersionOption(); + + if (!parser.parse(filteredArgs)) + { + *canContinue = true; + *errorMessage = parser.errorText(); + return false; + } + + if (parser.isSet(versionOption)) + { + parser.showVersion(); + return true; + } + + if (parser.isSet(helpOption)) + { + parser.showHelp(); + return true; + } + + for (const auto &arg : parser.positionalArguments()) + { + if (arg.startsWith(QV2RAY_URL_SCHEME + "://")) + { + StartupArguments.arguments << Qv2rayStartupArguments::QV2RAY_LINK; + StartupArguments.links << arg; + } + } + + if (parser.isSet(exitOption)) + { + DEBUG("disconnectOption is set."); + StartupArguments.arguments << Qv2rayStartupArguments::EXIT; + } + + if (parser.isSet(disconnectOption)) + { + DEBUG("disconnectOption is set."); + StartupArguments.arguments << Qv2rayStartupArguments::DISCONNECT; + } + + if (parser.isSet(reconnectOption)) + { + DEBUG("reconnectOption is set."); + StartupArguments.arguments << Qv2rayStartupArguments::RECONNECT; + } + +#define ProcessExtraStartupOptions(option) \ + DEBUG("Startup Options:" A(parser.isSet(option##Option))); \ + StartupArguments.option = parser.isSet(option##Option); + + ProcessExtraStartupOptions(noAPI); + ProcessExtraStartupOptions(debugLog); + ProcessExtraStartupOptions(noAutoConnection); + ProcessExtraStartupOptions(noPlugins); + return true; +} diff --git a/src/ui/Qv2rayPlatformApplication.hpp b/src/ui/Qv2rayPlatformApplication.hpp index c4471a27..bee7ac43 100644 --- a/src/ui/Qv2rayPlatformApplication.hpp +++ b/src/ui/Qv2rayPlatformApplication.hpp @@ -23,7 +23,6 @@ #ifdef QV2RAY_GUI #include - #include #include const static inline QMap MessageBoxButtonMap // = { { No, QMessageBox::No }, @@ -37,97 +36,36 @@ const static inline QMap MessageBoxButt #ifndef QV2RAY_NO_SINGLEAPPLICATON #include - #define QV2RAY_BASE_APPLICATION_CLASS SingleApplication - #define QV2RAY_BASE_CLASS_CONSTRUCTOR_ARGS argc, argv, true, User | ExcludeAppPath | ExcludeAppVersion + #define QVBASEAPPLICATION SingleApplication + #define QVBASEAPPLICATION_CTORARGS argc, argv, true, User | ExcludeAppPath | ExcludeAppVersion #else - #define QV2RAY_BASE_APPLICATION_CLASS QAPPLICATION_CLASS - #define QV2RAY_BASE_CLASS_CONSTRUCTOR_ARGS argc, argv + #define QVBASEAPPLICATION QAPPLICATION_CLASS + #define QVBASEAPPLICATION_CTORARGS argc, argv #endif class Qv2rayPlatformApplication - : public QV2RAY_BASE_APPLICATION_CLASS - , public Qv2rayApplicationManager + : public QVBASEAPPLICATION + , public Qv2rayApplicationInterface { Q_OBJECT public: - Qv2rayPlatformApplication(int &argc, char *argv[]) - : QV2RAY_BASE_APPLICATION_CLASS(QV2RAY_BASE_CLASS_CONSTRUCTOR_ARGS), Qv2rayApplicationManager(){}; + Qv2rayPlatformApplication(int &argc, char *argv[]) : QVBASEAPPLICATION(QVBASEAPPLICATION_CTORARGS), Qv2rayApplicationInterface(){}; virtual ~Qv2rayPlatformApplication(){}; - - void QuitApplication(int retCode = 0) + inline void QuitApplication(int retCode = 0) { QCoreApplication::exit(retCode); } protected: - Qv2raySetupStatus InitializeInternal() - { - connect(this, &Qv2rayPlatformApplication::aboutToQuit, this, &Qv2rayPlatformApplication::QuitInternal); -#ifndef QV2RAY_NO_SINGLEAPPLICATON - connect(this, &SingleApplication::receivedMessage, this, &Qv2rayPlatformApplication::onMessageReceived, Qt::QueuedConnection); - if (isSecondary()) - { - if (Qv2rayProcessArgument.arguments.isEmpty()) - Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::NORMAL; - sendMessage(JsonToString(Qv2rayProcessArgument.toJson(), QJsonDocument::Compact).toUtf8()); - return SINGLE_APPLICATION; - } -#endif -#ifdef QV2RAY_GUI - #ifdef Q_OS_LINUX - #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - setFallbackSessionManagementEnabled(false); - #endif - connect(this, &QGuiApplication::commitDataRequest, [] { - RouteManager->SaveRoutes(); - ConnectionManager->SaveConnectionConfig(); - PluginHost->SavePluginSettings(); - SaveGlobalSettings(); - }); - #endif - #ifdef Q_OS_WIN - SetCurrentDirectory(applicationDirPath().toStdWString().c_str()); - // Set special font in Windows - QFont font; - font.setPointSize(9); - font.setFamily("Microsoft YaHei"); - setFont(font); - #endif -#endif - - // Install a default translater. From the OS/DE - Qv2rayTranslator = std::make_unique(); - Qv2rayTranslator->InstallTranslation(QLocale::system().name()); - return NORMAL; - } - - bool RunInternal() - { - PluginHost = new QvPluginHost(); - RouteManager = new RouteHandler(); - ConnectionManager = new QvConfigHandler(); - return true; - } - - void QuitInternal() - { - // Do not change the order. - ConnectionManager->StopConnection(); - RouteManager->SaveRoutes(); - ConnectionManager->SaveConnectionConfig(); - PluginHost->SavePluginSettings(); - SaveGlobalSettings(); - TerminateUI(); - delete ConnectionManager; - delete RouteManager; - delete PluginHost; - ConnectionManager = nullptr; - RouteManager = nullptr; - PluginHost = nullptr; - } - + bool initializeInternal(); + bool runInternal(); + void quitInternal(); virtual void TerminateUI() = 0; + #ifndef QV2RAY_NO_SINGLEAPPLICATON virtual void onMessageReceived(quint32 clientId, QByteArray msg) = 0; #endif + + private: + bool parseCommandLine(QString *errorMessage, bool *canContinue); }; diff --git a/src/ui/widgets/Qv2rayWidgetApplication.cpp b/src/ui/widgets/Qv2rayWidgetApplication.cpp index 469342b9..f074fd40 100644 --- a/src/ui/widgets/Qv2rayWidgetApplication.cpp +++ b/src/ui/widgets/Qv2rayWidgetApplication.cpp @@ -1,6 +1,5 @@ #include "Qv2rayWidgetApplication.hpp" -#include "3rdparty/libsemver/version.hpp" #include "base/Qv2rayBase.hpp" #include "components/translations/QvTranslator.hpp" #include "core/settings/SettingsBackend.hpp" @@ -22,195 +21,192 @@ constexpr auto QV2RAY_WIDGETUI_STATE_FILENAME = "UIState.json"; -namespace Qv2ray +Qv2rayWidgetApplication::Qv2rayWidgetApplication(int &argc, char *argv[]) : Qv2rayPlatformApplication(argc, argv) { - Qv2rayWidgetApplication::Qv2rayWidgetApplication(int &argc, char *argv[]) : Qv2rayPlatformApplication(argc, argv) - { - } +} - Qv2raySetupStatus Qv2rayWidgetApplication::Initialize() - { - const auto result = InitializeInternal(); - if (result != NORMAL) - return result; - setQuitOnLastWindowClosed(false); - hTray = new QSystemTrayIcon(); - return NORMAL; - } +bool Qv2rayWidgetApplication::Initialize() +{ + const auto result = initializeInternal(); + if (!result) + return false; + setQuitOnLastWindowClosed(false); + hTray = new QSystemTrayIcon(); + return true; +} - void Qv2rayWidgetApplication::TerminateUI() - { - delete mainWindow; - delete hTray; - delete StyleManager; - StringToFile(JsonToString(UIStates), QV2RAY_CONFIG_DIR + QV2RAY_WIDGETUI_STATE_FILENAME); - } +void Qv2rayWidgetApplication::TerminateUI() +{ + delete mainWindow; + delete hTray; + delete StyleManager; + StringToFile(JsonToString(UIStates), QV2RAY_CONFIG_DIR + QV2RAY_WIDGETUI_STATE_FILENAME); +} #ifndef QV2RAY_NO_SINGLEAPPLICATON - void Qv2rayWidgetApplication::onMessageReceived(quint32 clientId, QByteArray _msg) +void Qv2rayWidgetApplication::onMessageReceived(quint32 clientId, QByteArray _msg) +{ + // Sometimes SingleApplication will send message with clientId == 0, ignore them. + if (clientId == instanceId()) + return; + + if (!isInitialized) + return; + + const auto msg = Qv2rayStartupArguments::fromJson(JsonFromString(_msg)); + LOG("Client ID:", clientId, ", message received, version:", msg.buildVersion); + DEBUG(_msg); + // + if (msg.buildVersion > QV2RAY_VERSION_BUILD) { - // Sometimes SingleApplication will send message with clientId == 0, ignore them. - if (clientId == instanceId()) - return; - const auto msg = Qv2rayProcessArguments::fromJson(JsonFromString(_msg)); - LOG("Client ID:", clientId, ", message received, version:", msg.version); - DEBUG(_msg); - // - const auto currentVersion = semver::version::from_string(QV2RAY_VERSION_STRING); - const auto newVersionString = msg.version.isEmpty() ? "0.0.0" : msg.version.toStdString(); - const auto newVersion = semver::version::from_string(newVersionString); - // - if (newVersion > currentVersion) - { - const auto newPath = msg.fullArgs.first(); - QString message; - message += tr("A new version of Qv2ray is starting:") + 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 newPath = msg.fullArgs.first(); + QString message; + message += tr("A new version of Qv2ray is starting:") + NEWLINE; + message += NEWLINE; + message += tr("New version information: ") + NEWLINE; + message += tr("Version: %1:%2").arg(msg.version).arg(msg.buildVersion) + NEWLINE; + message += tr("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 == Yes) - { - Qv2rayProcessArgument._qvNewVersionPath = newPath; - QuitApplication(QVEXIT_NEW_VERSION); - } + const auto result = QvMessageBoxAsk(nullptr, tr("New version detected"), message); + if (result == Yes) + { + StartupArguments._qvNewVersionPath = newPath; + QuitApplication(EXIT_NEW_VERSION_TRIGGER); } + } - for (const auto &argument : msg.arguments) + for (const auto &argument : msg.arguments) + { + switch (argument) { - switch (argument) + case Qv2rayStartupArguments::EXIT: { - case Qv2rayProcessArguments::EXIT: + QuitApplication(); + break; + } + case Qv2rayStartupArguments::NORMAL: + { + mainWindow->show(); + mainWindow->raise(); + mainWindow->activateWindow(); + break; + } + case Qv2rayStartupArguments::RECONNECT: + { + ConnectionManager->RestartConnection(); + break; + } + case Qv2rayStartupArguments::DISCONNECT: + { + ConnectionManager->StopConnection(); + break; + } + case Qv2rayStartupArguments::QV2RAY_LINK: + { + for (const auto &link : msg.links) { - QuitApplication(); - break; - } - case Qv2rayProcessArguments::NORMAL: - { - mainWindow->show(); - mainWindow->raise(); - mainWindow->activateWindow(); - break; - } - case Qv2rayProcessArguments::RECONNECT: - { - ConnectionManager->RestartConnection(); - break; - } - case Qv2rayProcessArguments::DISCONNECT: - { - ConnectionManager->StopConnection(); - break; - } - case Qv2rayProcessArguments::QV2RAY_LINK: - { - for (const auto &link : msg.links) + const auto url = QUrl::fromUserInput(link); + const auto command = url.host(); + auto subcommands = url.path().split("/"); + subcommands.removeAll(""); + QMap args; + for (const auto &kvp : QUrlQuery(url).queryItems()) { - const auto url = QUrl::fromUserInput(link); - const auto command = url.host(); - auto subcommands = url.path().split("/"); - subcommands.removeAll(""); - QMap args; - for (const auto &kvp : QUrlQuery(url).queryItems()) - { - args.insert(kvp.first, kvp.second); - } - if (command == "open") - { - emit mainWindow->ProcessCommand(command, subcommands, args); - } + args.insert(kvp.first, kvp.second); + } + if (command == "open") + { + emit mainWindow->ProcessCommand(command, subcommands, args); } - break; } + break; } } } +} #endif - Qv2rayExitCode Qv2rayWidgetApplication::RunQv2ray() +Qv2rayExitReason Qv2rayWidgetApplication::RunQv2ray() +{ + runInternal(); + StyleManager = new QvStyleManager(); + StyleManager->ApplyStyle(GlobalConfig.uiConfig.theme); + // Show MainWindow + UIStates = JsonFromString(StringFromFile(QV2RAY_CONFIG_DIR + QV2RAY_WIDGETUI_STATE_FILENAME)); + mainWindow = new MainWindow(); + if (StartupArguments.arguments.contains(Qv2rayStartupArguments::QV2RAY_LINK)) { - RunInternal(); - StyleManager = new QvStyleManager(); - StyleManager->ApplyStyle(GlobalConfig.uiConfig.theme); - // Show MainWindow - UIStates = JsonFromString(StringFromFile(QV2RAY_CONFIG_DIR + QV2RAY_WIDGETUI_STATE_FILENAME)); - mainWindow = new MainWindow(); - if (Qv2rayProcessArgument.arguments.contains(Qv2rayProcessArguments::QV2RAY_LINK)) + for (const auto &link : StartupArguments.links) { - for (const auto &link : Qv2rayProcessArgument.links) + const auto url = QUrl::fromUserInput(link); + const auto command = url.host(); + auto subcommands = url.path().split("/"); + subcommands.removeAll(""); + QMap args; + for (const auto &kvp : QUrlQuery(url).queryItems()) { - const auto url = QUrl::fromUserInput(link); - const auto command = url.host(); - auto subcommands = url.path().split("/"); - subcommands.removeAll(""); - QMap args; - for (const auto &kvp : QUrlQuery(url).queryItems()) - { - args.insert(kvp.first, kvp.second); - } - if (command == "open") - { - emit mainWindow->ProcessCommand(command, subcommands, args); - } + args.insert(kvp.first, kvp.second); + } + if (command == "open") + { + emit mainWindow->ProcessCommand(command, subcommands, args); } } + } #ifdef Q_OS_MACOS - connect(this, &QApplication::applicationStateChanged, [this](Qt::ApplicationState state) { - switch (state) - { - case Qt::ApplicationActive: - { - mainWindow->show(); - mainWindow->raise(); - mainWindow->activateWindow(); - break; - } - case Qt::ApplicationHidden: - case Qt::ApplicationInactive: - case Qt::ApplicationSuspended: break; - } - }); -#endif - return (Qv2rayExitCode) exec(); - } - - void Qv2rayWidgetApplication::OpenURL(const QString &url) - { - QDesktopServices::openUrl(url); - } - - void Qv2rayWidgetApplication::MessageBoxWarn(QWidget *parent, const QString &title, const QString &text, MessageOpt button) - { - QMessageBox::warning(parent, title, text, MessageBoxButtonMap[button]); - } - - void Qv2rayWidgetApplication::MessageBoxInfo(QWidget *parent, const QString &title, const QString &text, MessageOpt button) - { - QMessageBox::information(parent, title, text, MessageBoxButtonMap[button]); - } - - MessageOpt Qv2rayWidgetApplication::MessageBoxAsk(QWidget *parent, const QString &title, const QString &text, const QList &buttons) - { - QFlags btns; - for (const auto &b : buttons) + connect(this, &QApplication::applicationStateChanged, [this](Qt::ApplicationState state) { + switch (state) { - btns.setFlag(MessageBoxButtonMap[b]); + case Qt::ApplicationActive: + { + mainWindow->show(); + mainWindow->raise(); + mainWindow->activateWindow(); + break; + } + case Qt::ApplicationHidden: + case Qt::ApplicationInactive: + case Qt::ApplicationSuspended: break; } - return MessageBoxButtonMap.key(QMessageBox::question(parent, title, text, btns)); - } + }); +#endif + isInitialized = true; + return (Qv2rayExitReason) exec(); +} - void Qv2rayWidgetApplication::ShowTrayMessage(const QString &m, const QIcon &icon, int msecs) +void Qv2rayWidgetApplication::OpenURL(const QString &url) +{ + QDesktopServices::openUrl(url); +} + +void Qv2rayWidgetApplication::MessageBoxWarn(QWidget *parent, const QString &title, const QString &text, MessageOpt button) +{ + QMessageBox::warning(parent, title, text, MessageBoxButtonMap[button]); +} + +void Qv2rayWidgetApplication::MessageBoxInfo(QWidget *parent, const QString &title, const QString &text, MessageOpt button) +{ + QMessageBox::information(parent, title, text, MessageBoxButtonMap[button]); +} + +MessageOpt Qv2rayWidgetApplication::MessageBoxAsk(QWidget *parent, const QString &title, const QString &text, const QList &buttons) +{ + QFlags btns; + for (const auto &b : buttons) { - hTray->showMessage("Qv2ray", m, icon, msecs); + btns.setFlag(MessageBoxButtonMap[b]); } + return MessageBoxButtonMap.key(QMessageBox::question(parent, title, text, btns)); +} - void Qv2rayWidgetApplication::ShowTrayMessage(const QString &m, QSystemTrayIcon::MessageIcon icon, int msecs) - { - hTray->showMessage("Qv2ray", m, icon, msecs); - } +void Qv2rayWidgetApplication::ShowTrayMessage(const QString &m, const QIcon &icon, int msecs) +{ + hTray->showMessage("Qv2ray", m, icon, msecs); +} -} // namespace Qv2ray +void Qv2rayWidgetApplication::ShowTrayMessage(const QString &m, QSystemTrayIcon::MessageIcon icon, int msecs) +{ + hTray->showMessage("Qv2ray", m, icon, msecs); +} diff --git a/src/ui/widgets/Qv2rayWidgetApplication.hpp b/src/ui/widgets/Qv2rayWidgetApplication.hpp index 282925df..9f43aeb4 100644 --- a/src/ui/widgets/Qv2rayWidgetApplication.hpp +++ b/src/ui/widgets/Qv2rayWidgetApplication.hpp @@ -13,8 +13,8 @@ namespace Qv2ray Q_OBJECT public: explicit Qv2rayWidgetApplication(int &argc, char *argv[]); - Qv2raySetupStatus Initialize() override; - Qv2rayExitCode RunQv2ray() override; + bool Initialize() override; + Qv2rayExitReason RunQv2ray() override; QJsonObject UIStates; public: @@ -31,6 +31,7 @@ namespace Qv2ray } private: + bool isInitialized; void TerminateUI() override; #ifndef QV2RAY_NO_SINGLEAPPLICATON void onMessageReceived(quint32 clientID, QByteArray msg) override; diff --git a/src/ui/widgets/styles/StyleManager.cpp b/src/ui/widgets/styles/StyleManager.cpp index 878fabc7..e232bbcc 100644 --- a/src/ui/widgets/styles/StyleManager.cpp +++ b/src/ui/widgets/styles/StyleManager.cpp @@ -33,7 +33,7 @@ namespace Qv2ray::ui::styles styles.insert(key, style); } - for (const auto &styleDir : Qv2rayAssetsPaths("uistyles")) + for (const auto &styleDir : QvCoreApplication->GetAssetsPaths("uistyles")) { for (const auto &file : GetFileList(QDir(styleDir))) { diff --git a/src/ui/widgets/windows/w_MainWindow_extra.cpp b/src/ui/widgets/windows/w_MainWindow_extra.cpp index 6d46a0f5..ae7950df 100644 --- a/src/ui/widgets/windows/w_MainWindow_extra.cpp +++ b/src/ui/widgets/windows/w_MainWindow_extra.cpp @@ -101,7 +101,7 @@ void MainWindow::MWClearSystemProxy() bool MainWindow::StartAutoConnectionEntry() { - if (StartupOption.noAutoConnection) + if (QvCoreApplication->StartupArguments.noAutoConnection) return false; switch (GlobalConfig.autoStartBehavior) { diff --git a/src/ui/widgets/windows/w_PreferencesWindow.cpp b/src/ui/widgets/windows/w_PreferencesWindow.cpp index 1c5b54ab..4267ca1e 100644 --- a/src/ui/widgets/windows/w_PreferencesWindow.cpp +++ b/src/ui/widgets/windows/w_PreferencesWindow.cpp @@ -296,7 +296,7 @@ void PreferencesWindow::on_buttonBox_accepted() ports << CurrentConfig.inboundConfig.tProxySettings.port; } - if (!StartupOption.noAPI) + if (!QvCoreApplication->StartupArguments.noAPI) { size++; ports << CurrentConfig.kernelConfig.statsPort; diff --git a/test/src/components/latency/TestRealPing.cpp b/test/src/components/latency/TestRealPing.cpp index cf295c9a..84d2b610 100644 --- a/test/src/components/latency/TestRealPing.cpp +++ b/test/src/components/latency/TestRealPing.cpp @@ -8,15 +8,15 @@ char *fakeArgv[]{}; class QvTestApplication : public QCoreApplication - , public Qv2rayApplicationManager + , public Qv2rayApplicationInterface { public: - explicit QvTestApplication() : QCoreApplication(fakeArgc, fakeArgv), Qv2rayApplicationManager(){}; - virtual Qv2raySetupStatus Initialize() override + explicit QvTestApplication() : QCoreApplication(fakeArgc, fakeArgv), Qv2rayApplicationInterface(){}; + virtual bool Initialize() override { - return {}; + return true; }; - virtual Qv2rayExitCode RunQv2ray() override + virtual Qv2rayExitReason RunQv2ray() override { return {}; };