mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 02:40:20 +08:00
refactor: fixed SingleApplication crash, refactored
This commit is contained in:
parent
94d701ce91
commit
4cd00df503
2
3rdparty/SingleApplication
vendored
2
3rdparty/SingleApplication
vendored
@ -1 +1 @@
|
||||
Subproject commit 9a408690ceb260a220d50f5070af9bcda58cacc2
|
||||
Subproject commit 0e42d86cecf008e850bc1f7540196de1f6bf182d
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
6000
|
||||
6001
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Argument> arguments;
|
||||
QString version;
|
||||
int buildVersion;
|
||||
QString data;
|
||||
QList<QString> links;
|
||||
QList<QString> 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<MessageOpt> &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<Qv2rayApplicationManager *>(qvApplicationInstance)
|
||||
#define GlobalConfig (*Qv2ray::qvApplicationInstance->ConfigObject)
|
||||
#define GlobalConfig (*Qv2ray::QvCoreApplication->ConfigObject)
|
||||
|
@ -27,8 +27,8 @@
|
||||
|
||||
#define _LOG_ARG_(...) QV2RAY_LOG_PREPEND_CONTENT "[" QV_MODULE_NAME "]", __VA_ARGS__
|
||||
|
||||
#define LOG(...) Qv2ray::base::log_concat<QV2RAY_LOG_NORMAL>(_LOG_ARG_(__VA_ARGS__))
|
||||
#define DEBUG(...) Qv2ray::base::log_concat<QV2RAY_LOG_DEBUG>(_LOG_ARG_(__VA_ARGS__))
|
||||
#define LOG(...) Qv2ray::base::log_internal<QV2RAY_LOG_NORMAL>(_LOG_ARG_(__VA_ARGS__))
|
||||
#define DEBUG(...) Qv2ray::base::log_internal<QV2RAY_LOG_DEBUG>(_LOG_ARG_(__VA_ARGS__))
|
||||
|
||||
enum QvLogType
|
||||
{
|
||||
@ -51,7 +51,7 @@ namespace Qv2ray::base
|
||||
}
|
||||
|
||||
template<QvLogType t, typename... T>
|
||||
inline void log_concat(T... v)
|
||||
inline void log_internal(T... v)
|
||||
{
|
||||
((logStream << v << " "), ...);
|
||||
((tempStream << v << " "), ...);
|
||||
|
@ -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<QString> &chain, const QString &tag)
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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/");
|
||||
|
@ -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();
|
||||
|
@ -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<bool, QMap<QString, QString>> 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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
89
src/main.cpp
89
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;
|
||||
}
|
||||
|
221
src/ui/Qv2rayPlatformApplication.cpp
Normal file
221
src/ui/Qv2rayPlatformApplication.cpp
Normal file
@ -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<QvTranslator>();
|
||||
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;
|
||||
}
|
@ -23,7 +23,6 @@
|
||||
|
||||
#ifdef QV2RAY_GUI
|
||||
#include <QApplication>
|
||||
#include <QFont>
|
||||
#include <QMessageBox>
|
||||
const static inline QMap<MessageOpt, QMessageBox::StandardButton> MessageBoxButtonMap //
|
||||
= { { No, QMessageBox::No },
|
||||
@ -37,97 +36,36 @@ const static inline QMap<MessageOpt, QMessageBox::StandardButton> MessageBoxButt
|
||||
|
||||
#ifndef QV2RAY_NO_SINGLEAPPLICATON
|
||||
#include <SingleApplication>
|
||||
#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<QvTranslator>();
|
||||
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);
|
||||
};
|
||||
|
@ -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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<MessageOpt> &buttons)
|
||||
{
|
||||
QFlags<QMessageBox::StandardButton> 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<MessageOpt> &buttons)
|
||||
{
|
||||
QFlags<QMessageBox::StandardButton> 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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)))
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ void MainWindow::MWClearSystemProxy()
|
||||
|
||||
bool MainWindow::StartAutoConnectionEntry()
|
||||
{
|
||||
if (StartupOption.noAutoConnection)
|
||||
if (QvCoreApplication->StartupArguments.noAutoConnection)
|
||||
return false;
|
||||
switch (GlobalConfig.autoStartBehavior)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 {};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user