mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-21 11:20:49 +08:00
[fix] Some UI fixes, and added a Singleapplication model, Fixed #139
Former-commit-id: 7ea337d1f2
This commit is contained in:
parent
90ccfea5a1
commit
0af45dc678
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "3rdparty/qhttpserver"]
|
||||
path = 3rdparty/qhttpserver
|
||||
url = https://github.com/nikhilm/qhttpserver
|
||||
[submodule "3rdparty/SingleApplication"]
|
||||
path = 3rdparty/SingleApplication
|
||||
url = https://github.com/itay-grudev/SingleApplication
|
||||
|
1
3rdparty/SingleApplication
vendored
Submodule
1
3rdparty/SingleApplication
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 16ea64b2548b02f59bf49b255466278c3ff0ace8
|
@ -1 +1 @@
|
||||
1776
|
||||
1796
|
||||
|
19
Qv2ray.pro
19
Qv2ray.pro
@ -9,20 +9,21 @@ QT += core gui widgets network charts
|
||||
TARGET = qv2ray
|
||||
TEMPLATE = app
|
||||
|
||||
# Don't merge those configs with below.
|
||||
CONFIG += enable_decoder_qr_code enable_encoder_qr_code qt c++11 openssl-linked
|
||||
include(3rdparty/qzxing_noTests/QZXing-components.pri)
|
||||
|
||||
# Main config
|
||||
CONFIG += lrelease embed_translations
|
||||
|
||||
# Now read build number file.
|
||||
_BUILD_NUMBER=$$cat(Build.Counter)
|
||||
VERSION = 1.99.2.$$_BUILD_NUMBER
|
||||
_BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1)
|
||||
write_file("Build.Counter", _BUILD_NUMBER)
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\"
|
||||
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
|
||||
|
||||
# Don't merge those configs with below.
|
||||
CONFIG += enable_decoder_qr_code enable_encoder_qr_code qt c++11 openssl-linked
|
||||
include(3rdparty/qzxing_noTests/QZXing-components.pri)
|
||||
include(3rdparty/SingleApplication/singleapplication.pri)
|
||||
|
||||
# Main config
|
||||
CONFIG += lrelease embed_translations
|
||||
|
||||
SOURCES += \
|
||||
src/components/QvComponentsHandler.cpp \
|
||||
@ -39,7 +40,6 @@ SOURCES += \
|
||||
src/QvCoreConfigOperations_Convertion.cpp \
|
||||
src/QvCoreConfigOperations_Generation.cpp \
|
||||
src/QvUtils.cpp \
|
||||
src/utils/QvRunguard.cpp \
|
||||
src/utils/QJsonModel.cpp \
|
||||
src/ui/w_ExportConfig.cpp \
|
||||
src/ui/w_InboundEditor.cpp \
|
||||
@ -89,7 +89,6 @@ HEADERS += \
|
||||
src/utils/QvTinyLog.hpp \
|
||||
src/utils/QJsonModel.hpp \
|
||||
src/utils/QJsonObjectInsertMacros.h \
|
||||
src/utils/QvRunguard.hpp \
|
||||
libs/gen/v2ray_api_commands.pb.h \
|
||||
libs/gen/v2ray_api_commands.grpc.pb.h
|
||||
|
||||
|
31
src/main.cpp
31
src/main.cpp
@ -3,10 +3,13 @@
|
||||
#include <QTranslator>
|
||||
#include <QStyle>
|
||||
#include <QLocale>
|
||||
#include <QObject>
|
||||
#include <QStyleFactory>
|
||||
|
||||
#include <QApplication>
|
||||
#include <singleapplication.h>
|
||||
|
||||
#include "w_MainWindow.hpp"
|
||||
#include "QvRunguard.hpp"
|
||||
|
||||
bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
|
||||
{
|
||||
@ -157,7 +160,13 @@ bool initialiseQv2ray()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// This line must be called before any other ones.
|
||||
QApplication _qApp(argc, argv);
|
||||
// ----------------------------> For debug build...
|
||||
SingleApplication _qApp(argc, argv);
|
||||
// Early initialisation
|
||||
#ifdef QT_DEBUG
|
||||
_qApp.setApplicationName(_qApp.applicationName() + " - DEBUG");
|
||||
#endif
|
||||
//
|
||||
//
|
||||
// Install a default translater. From the OS/DE
|
||||
auto _lang = QLocale::system().name().replace("_", "-");
|
||||
@ -208,18 +217,6 @@ int main(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
RunGuard guard("Qv2ray-Instance-Identifier-DEBUG_VERSION");
|
||||
#else
|
||||
RunGuard guard("Qv2ray-Instance-Identifier");
|
||||
#endif
|
||||
|
||||
if (!guard.isSingleInstance()) {
|
||||
LOG(MODULE_INIT, "Another Instance running, Quit.")
|
||||
QvMessageBox(nullptr, "Qv2ray", QObject::tr("Another instance of Qv2ray is already running."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto conf = CONFIGROOT(JsonFromString(StringFromFile(new QFile(QV2RAY_CONFIG_FILE))));
|
||||
//
|
||||
auto confVersion = conf["config_version"].toVariant().toString();
|
||||
@ -322,6 +319,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
#else
|
||||
QStringList themes = QStyleFactory::keys();
|
||||
//_qApp.setDesktopFileName("qv2ray.desktop");
|
||||
|
||||
if (themes.contains(QSTRING(confObject.uiConfig.theme))) {
|
||||
_qApp.setStyle(QSTRING(confObject.uiConfig.theme));
|
||||
@ -335,6 +333,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
try {
|
||||
#endif
|
||||
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&w]() {
|
||||
w.show();
|
||||
w.raise();
|
||||
w.activateWindow();
|
||||
});
|
||||
auto rcode = _qApp.exec();
|
||||
LOG(MODULE_INIT, "Quitting normally")
|
||||
return rcode;
|
||||
|
@ -306,7 +306,6 @@ void MainWindow::VersionUpdate(QByteArray &data)
|
||||
} else if (result == QMessageBox::Ignore) {
|
||||
currentConfig.ignoredVersion = newversion.toString().toStdString();
|
||||
SetGlobalConfig(currentConfig);
|
||||
OnConfigListChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,7 +313,16 @@ void MainWindow::ReloadConnections()
|
||||
{
|
||||
LOG(MODULE_UI, "Loading new GlobalConfig")
|
||||
currentConfig = GetGlobalConfig();
|
||||
//
|
||||
// Store the latency test value.
|
||||
QMap<QString, double> latencyValueCache;
|
||||
|
||||
for (auto i = 0; i < connections.count(); i++) {
|
||||
latencyValueCache[connections.keys()[i]] = connections.values()[i].latency;
|
||||
}
|
||||
|
||||
connections.clear();
|
||||
SetEditWidgetEnable(false);
|
||||
//
|
||||
connectionListWidget->clear();
|
||||
auto _regularConnections = GetRegularConnections(currentConfig.configs);
|
||||
@ -325,7 +333,8 @@ void MainWindow::ReloadConnections()
|
||||
_o.subscriptionName = "";
|
||||
_o.connectionName = _regularConnections.keys()[i];
|
||||
_o.config = _regularConnections.values()[i];
|
||||
_o.latency = 0;
|
||||
// Restore latency from cache.
|
||||
_o.latency = latencyValueCache.contains(_o.connectionName) ? latencyValueCache[_o.connectionName] : 0;
|
||||
connections[_o.connectionName] = _o;
|
||||
connectionListWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << _o.connectionName));
|
||||
}
|
||||
@ -343,17 +352,16 @@ void MainWindow::ReloadConnections()
|
||||
_o.subscriptionName = subName;
|
||||
_o.connectionName = _subsConnections.values()[i].keys()[j];
|
||||
_o.config = _subsConnections.values()[i].values()[j];
|
||||
_o.latency = 0;
|
||||
//
|
||||
auto connName = _o.connectionName + " (" + tr("Subscription:") + " " + _o.subscriptionName + ")";
|
||||
_o.latency = latencyValueCache.contains(connName) ? latencyValueCache[connName] : 0;
|
||||
connections[connName] = _o;
|
||||
subTopLevel->addChild(new QTreeWidgetItem(QStringList() << connName));
|
||||
}
|
||||
}
|
||||
|
||||
SetEditWidgetEnable(false);
|
||||
|
||||
//// We set the current item back...
|
||||
if (!CurrentConnectionName.isEmpty()) {
|
||||
if (!CurrentConnectionName.isEmpty() && connections.contains(CurrentConnectionName)) {
|
||||
auto items = connectionListWidget->findItems(CurrentConnectionName, Qt::MatchExactly | Qt::MatchRecursive);
|
||||
|
||||
if (items.count() > 0 && IsConnectableItem(items.first())) {
|
||||
@ -516,7 +524,7 @@ void MainWindow::on_startButton_clicked()
|
||||
}
|
||||
|
||||
if (canSetSystemProxy) {
|
||||
LOG(MODULE_UI, "Setting system proxy for simple config")
|
||||
LOG(MODULE_UI, "Setting system proxy for simple config, HTTP only")
|
||||
// --------------------We only use HTTP here->>|=========|
|
||||
SetSystemProxy(proxyAddress, currentConfig.inboundConfig.http_port, usePAC);
|
||||
}
|
||||
@ -532,6 +540,8 @@ void MainWindow::on_startButton_clicked()
|
||||
//
|
||||
startButton->setEnabled(!startFlag);
|
||||
stopButton->setEnabled(startFlag);
|
||||
} else {
|
||||
LOG(MODULE_UI, "vCore already started.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -875,6 +885,7 @@ void MainWindow::on_importConfigButton_clicked()
|
||||
ImportConfigWindow *w = new ImportConfigWindow(this);
|
||||
auto configs = w->OpenImport();
|
||||
|
||||
if (!configs.isEmpty()) {
|
||||
for (auto conf : configs) {
|
||||
auto name = configs.key(conf, "");
|
||||
|
||||
@ -887,6 +898,7 @@ void MainWindow::on_importConfigButton_clicked()
|
||||
|
||||
SetGlobalConfig(currentConfig);
|
||||
OnConfigListChanged(false);
|
||||
}
|
||||
}
|
||||
void MainWindow::on_editConfigButton_clicked()
|
||||
{
|
||||
@ -897,6 +909,8 @@ void MainWindow::on_editConfigButton_clicked()
|
||||
}
|
||||
|
||||
auto alias = connectionListWidget->selectedItems().first()->text(0);
|
||||
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
|
||||
//
|
||||
auto outBoundRoot = connections[alias].config;
|
||||
CONFIGROOT root;
|
||||
bool isChanged = false;
|
||||
@ -917,11 +931,15 @@ void MainWindow::on_editConfigButton_clicked()
|
||||
}
|
||||
|
||||
if (isChanged) {
|
||||
if (IsSubscription(alias)) {
|
||||
SaveSubscriptionConfig(root, connections[alias].subscriptionName, connections[alias].connectionName);
|
||||
} else {
|
||||
connections[alias].config = root;
|
||||
// true indicates the alias will NOT change
|
||||
SaveConnectionConfig(root, &alias, true);
|
||||
}
|
||||
|
||||
OnConfigListChanged(alias == CurrentConnectionName);
|
||||
ShowAndSetConnection(CurrentConnectionName, false, false);
|
||||
}
|
||||
}
|
||||
void MainWindow::on_reconnectButton_clicked()
|
||||
@ -939,16 +957,8 @@ void MainWindow::on_action_RCM_ConvToComplex_triggered()
|
||||
}
|
||||
|
||||
auto alias = connectionListWidget->currentItem()->text(0);
|
||||
|
||||
if (connections[alias].configType == CON_SUBSCRIPTION) {
|
||||
if (QvMessageBoxAsk(this, tr("Editing a subscription config"), tr("You are trying to edit a config loaded from subscription.") +
|
||||
NEWLINE + tr("All changes will be overwritten when the subscriptions are updated next time.") +
|
||||
NEWLINE + tr("Are you still going to do so?")) != QMessageBox::Yes) {
|
||||
// Not changing, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
|
||||
//
|
||||
auto outBoundRoot = connections[alias].config;
|
||||
CONFIGROOT root;
|
||||
bool isChanged = false;
|
||||
@ -1004,9 +1014,9 @@ void MainWindow::on_pingTestBtn_clicked()
|
||||
latencyLabel->setText(tr("Testing..."));
|
||||
// We get data from UI?
|
||||
auto alias = connectionListWidget->currentItem()->text(0);
|
||||
int port = -1;
|
||||
|
||||
try {
|
||||
int port = -1;
|
||||
port = stoi(_portLabel->text().isEmpty() ? "0" : _portLabel->text().toStdString());
|
||||
tcpingModel->StartPing(alias, _hostLabel->text(), port);
|
||||
} catch (...) {
|
||||
@ -1133,6 +1143,7 @@ void MainWindow::on_connectionListWidget_itemSelectionChanged()
|
||||
_OutBoundTypeLabel->setText(tr("N/A"));
|
||||
_hostLabel->setText(tr("N/A"));
|
||||
_portLabel->setText(tr("N/A"));
|
||||
latencyLabel->setText(tr("N/A"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
|
||||
#if QV2RAY_USE_BUILTIN_DARKTHEME
|
||||
// If we use built in theme, it should always be fusion.
|
||||
themeCombo->setEnabled(!CurrentConfig.uiConfig.useDarkTheme);
|
||||
darkThemeLabel->setText(tr("Use Dark Theme"));
|
||||
darkThemeLabel->setText(tr("Use Darkmode Theme"));
|
||||
#endif
|
||||
languageComboBox->setCurrentText(QSTRING(CurrentConfig.uiConfig.language));
|
||||
logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel);
|
||||
|
@ -135,14 +135,14 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="darkThemeLabel">
|
||||
<property name="text">
|
||||
<string>Dark UI Icons</string>
|
||||
<string>Darkmode UI Icons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_36">
|
||||
<property name="text">
|
||||
<string>Dark Tray Icon</string>
|
||||
<string>Darkmode Tray Icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -1042,7 +1042,14 @@
|
||||
<string>Network Toolbar Settings</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0" rowspan="3">
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="applyNSBarSettingsBtn">
|
||||
<property name="text">
|
||||
<string>Apply Network Speed Bar UI Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" rowspan="3">
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="title">
|
||||
<string>Items</string>
|
||||
@ -1187,7 +1194,14 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_33">
|
||||
<property name="text">
|
||||
<string>You can config how the network speed toolbar looks like in this panel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QFrame" name="nsBarVerticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
@ -1356,17 +1370,20 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_33">
|
||||
<property name="text">
|
||||
<string>You can config how the network speed toolbar looks like in this panel</string>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_64">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: red</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="applyNSBarSettingsBtn">
|
||||
<property name="text">
|
||||
<string>Apply Network Speed Bar UI Settings</string>
|
||||
<string>This feature for Windows is not stable yet.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1,81 +0,0 @@
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include "QvRunguard.hpp"
|
||||
namespace Qv2ray
|
||||
{
|
||||
|
||||
//from https://stackoverflow.com/a/28172162
|
||||
QString RunGuard::generateKeyHash(const QString &key, const QString &salt)
|
||||
{
|
||||
QByteArray data;
|
||||
data.append(key.toUtf8());
|
||||
data.append(salt.toUtf8());
|
||||
data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex();
|
||||
return data;
|
||||
}
|
||||
RunGuard::RunGuard(const QString &key)
|
||||
: key(key)
|
||||
, memLockKey(generateKeyHash(key, "_memLockKey"))
|
||||
, sharedmemKey(generateKeyHash(key, "_sharedmemKey"))
|
||||
, sharedMem(sharedmemKey)
|
||||
, memLock(memLockKey, 1)
|
||||
{
|
||||
memLock.acquire();
|
||||
{
|
||||
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
|
||||
fix.attach();
|
||||
}
|
||||
memLock.release();
|
||||
}
|
||||
|
||||
RunGuard::~RunGuard()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
bool RunGuard::isAnotherRunning()
|
||||
{
|
||||
if (sharedMem.isAttached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memLock.acquire();
|
||||
const bool isRunning = sharedMem.attach();
|
||||
|
||||
if (isRunning) {
|
||||
sharedMem.detach();
|
||||
}
|
||||
|
||||
memLock.release();
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
bool RunGuard::isSingleInstance()
|
||||
{
|
||||
if (isAnotherRunning()) { // Extra check
|
||||
return false;
|
||||
}
|
||||
|
||||
memLock.acquire();
|
||||
const bool result = sharedMem.create(sizeof(quint64));
|
||||
memLock.release();
|
||||
|
||||
if (!result) {
|
||||
release();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunGuard::release()
|
||||
{
|
||||
memLock.acquire();
|
||||
|
||||
if (sharedMem.isAttached()) {
|
||||
sharedMem.detach();
|
||||
}
|
||||
|
||||
memLock.release();
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#ifndef RUNGUARD_H
|
||||
#define RUNGUARD_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
// From https://stackoverflow.com/a/28172162
|
||||
class RunGuard
|
||||
{
|
||||
public:
|
||||
explicit RunGuard(const QString &key);
|
||||
~RunGuard();
|
||||
|
||||
bool isAnotherRunning();
|
||||
bool isSingleInstance();
|
||||
void release();
|
||||
|
||||
private:
|
||||
QString generateKeyHash(const QString &key, const QString &salt);
|
||||
const QString key;
|
||||
const QString memLockKey;
|
||||
const QString sharedmemKey;
|
||||
|
||||
QSharedMemory sharedMem;
|
||||
QSystemSemaphore memLock;
|
||||
|
||||
Q_DISABLE_COPY(RunGuard)
|
||||
};
|
||||
}
|
||||
#endif // RUNGUARD_H
|
Loading…
Reference in New Issue
Block a user