mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-16 20:17:07 +03:00
feat: activate window when multiple open
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -70,8 +70,8 @@ Thumbs.db
|
||||
*.exe
|
||||
|
||||
# Custom
|
||||
/nekoray/
|
||||
/build/
|
||||
/nekoray
|
||||
/build
|
||||
CMakeLists.txt.user*
|
||||
/cmake-build-*
|
||||
/build-*
|
||||
@@ -81,4 +81,4 @@ CMakeLists.txt.user*
|
||||
# Deploy
|
||||
/deployment
|
||||
/neko*.sh
|
||||
/qtsdk/
|
||||
/qtsdk
|
||||
|
||||
1
3rdparty/QThreadCreateThread.hpp
vendored
1
3rdparty/QThreadCreateThread.hpp
vendored
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <QThread>
|
||||
|
||||
// FOR OLD QT
|
||||
|
||||
27
3rdparty/RunGuard.hpp
vendored
27
3rdparty/RunGuard.hpp
vendored
@@ -7,15 +7,14 @@
|
||||
#include <QCryptographicHash>
|
||||
|
||||
class RunGuard {
|
||||
|
||||
public:
|
||||
RunGuard(const QString &key);
|
||||
|
||||
~RunGuard();
|
||||
|
||||
bool isAnotherRunning();
|
||||
bool isAnotherRunning(quint64 *data_out);
|
||||
|
||||
bool tryToRun();
|
||||
bool tryToRun(quint64 *data_in);
|
||||
|
||||
void release();
|
||||
|
||||
@@ -42,15 +41,13 @@ namespace {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RunGuard::RunGuard(const QString &key)
|
||||
: key(key), memLockKey(generateKeyHash(key, "_memLockKey")),
|
||||
sharedmemKey(generateKeyHash(key, "_sharedmemKey")), sharedMem(sharedmemKey), memLock(memLockKey, 1) {
|
||||
: 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/
|
||||
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
|
||||
fix.attach();
|
||||
}
|
||||
memLock.release();
|
||||
@@ -60,26 +57,32 @@ RunGuard::~RunGuard() {
|
||||
release();
|
||||
}
|
||||
|
||||
bool RunGuard::isAnotherRunning() {
|
||||
bool RunGuard::isAnotherRunning(quint64 *data_out) {
|
||||
if (sharedMem.isAttached())
|
||||
return false;
|
||||
|
||||
memLock.acquire();
|
||||
const bool isRunning = sharedMem.attach();
|
||||
if (isRunning)
|
||||
if (isRunning) {
|
||||
if (data_out != nullptr) {
|
||||
memcpy(data_out, sharedMem.data(), sizeof(quint64));
|
||||
}
|
||||
sharedMem.detach();
|
||||
}
|
||||
memLock.release();
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
bool RunGuard::tryToRun() {
|
||||
if (isAnotherRunning()) // Extra check
|
||||
bool RunGuard::tryToRun(quint64 *data_in) {
|
||||
if (isAnotherRunning(nullptr)) // Extra check
|
||||
return false;
|
||||
|
||||
memLock.acquire();
|
||||
const bool result = sharedMem.create(sizeof(quint64));
|
||||
if (result) memcpy(sharedMem.data(), data_in, sizeof(quint64));
|
||||
memLock.release();
|
||||
|
||||
if (!result) {
|
||||
release();
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
if (WIN32)
|
||||
set(PLATFORM_FUCKING_SOURCES 3rdparty/WinCommander.cpp)
|
||||
set(PLATFORM_FUCKING_SOURCES 3rdparty/WinCommander.cpp sys/windows/guihelper.cpp)
|
||||
set(PLATFORM_FUCKING_LIBRARIES wininet wsock32 ws2_32 user32 rasapi32 iphlpapi)
|
||||
|
||||
include(cmake/fuck_windows/generate_product_version.cmake)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "sys/windows/guihelper.h"
|
||||
#endif
|
||||
|
||||
// Dialogs
|
||||
|
||||
#define Dialog_DialogBasicSettings "DialogBasicSettings"
|
||||
@@ -58,3 +62,21 @@
|
||||
CACHE.a = QJsonObject2QString(result, true); \
|
||||
if (result.isEmpty()) CACHE.a = ""; \
|
||||
editor->deleteLater();
|
||||
|
||||
// System
|
||||
|
||||
#define _ACTIVE_THIS_WINDOW_COMMON \
|
||||
hide(); \
|
||||
showMinimized(); \
|
||||
showNormal(); \
|
||||
activateWindow(); \
|
||||
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); \
|
||||
raise();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#define ACTIVE_THIS_WINDOW \
|
||||
_ACTIVE_THIS_WINDOW_COMMON \
|
||||
Windows_QWidget_SetForegroundWindow(this);
|
||||
#else
|
||||
#define ACTIVE_THIS_WINDOW _ACTIVE_THIS_WINDOW_COMMON
|
||||
#endif
|
||||
|
||||
@@ -69,6 +69,13 @@ QString GetRandomString(int randomStringLength) {
|
||||
return randomString;
|
||||
}
|
||||
|
||||
quint64 GetRandomUint64() {
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
std::uniform_int_distribution<quint64> dist;
|
||||
return dist(mt);
|
||||
}
|
||||
|
||||
// QString >> QJson
|
||||
QJsonObject QString2QJsonObject(const QString &jsonString) {
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString.toUtf8());
|
||||
|
||||
@@ -55,6 +55,8 @@ QString GetQueryValue(const QUrlQuery &q, const QString &key, const QString &def
|
||||
|
||||
QString GetRandomString(int randomStringLength);
|
||||
|
||||
quint64 GetRandomUint64();
|
||||
|
||||
inline QString UrlSafe_encode(const QString &s) {
|
||||
return s.toUtf8().toPercentEncoding().replace(" ", "%20");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <QTranslator>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
|
||||
#include "3rdparty/RunGuard.hpp"
|
||||
#include "main/NekoRay.hpp"
|
||||
@@ -22,6 +24,8 @@ void signal_handler(int signum) {
|
||||
}
|
||||
}
|
||||
|
||||
#define LOCAL_SERVER_PREFIX "nekoraylocalserver-"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Core dump
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -72,11 +76,25 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// RunGuard
|
||||
RunGuard guard("nekoray" + wd.absolutePath());
|
||||
if (!NekoRay::dataStore->flag_many) {
|
||||
if (!guard.tryToRun()) {
|
||||
QMessageBox::warning(nullptr, "NekoRay", QObject::tr("Another program is running."));
|
||||
quint64 guard_data_in = GetRandomUint64();
|
||||
quint64 guard_data_out = 0;
|
||||
if (!NekoRay::dataStore->flag_many && !guard.tryToRun(&guard_data_in)) {
|
||||
// Some Good System
|
||||
if (guard.isAnotherRunning(&guard_data_out)) {
|
||||
// Wake up a running instance
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(LOCAL_SERVER_PREFIX + Int2String(guard_data_out));
|
||||
qDebug() << socket.fullServerName();
|
||||
if (!socket.waitForConnected(500)) {
|
||||
qDebug() << "Failed to wake a running instance.";
|
||||
return 0;
|
||||
}
|
||||
qDebug() << "connected to local server, try to raise another program";
|
||||
return 0;
|
||||
}
|
||||
// Some Bad System
|
||||
QMessageBox::warning(nullptr, "NekoRay", "RunGuard disallow to run, use -many to force start.");
|
||||
return 0;
|
||||
}
|
||||
MF_release_runguard = [&] { guard.release(); };
|
||||
|
||||
@@ -159,6 +177,19 @@ int main(int argc, char *argv[]) {
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
// QLocalServer
|
||||
QLocalServer server;
|
||||
auto server_name = LOCAL_SERVER_PREFIX + Int2String(guard_data_in);
|
||||
QLocalServer::removeServer(server_name);
|
||||
server.listen(server_name);
|
||||
QObject::connect(&server, &QLocalServer::newConnection, &a, [&] {
|
||||
auto socket = server.nextPendingConnection();
|
||||
qDebug() << "nextPendingConnection:" << server_name << socket;
|
||||
socket->deleteLater();
|
||||
// raise main window
|
||||
MW_dialog_message("", "Raise");
|
||||
});
|
||||
|
||||
UI_InitMainWindow();
|
||||
return QApplication::exec();
|
||||
}
|
||||
|
||||
15
sys/windows/guihelper.cpp
Normal file
15
sys/windows/guihelper.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
#include "guihelper.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void Windows_QWidget_SetForegroundWindow(QWidget* w) {
|
||||
HWND hForgroundWnd = GetForegroundWindow();
|
||||
DWORD dwForeID = ::GetWindowThreadProcessId(hForgroundWnd, NULL);
|
||||
DWORD dwCurID = ::GetCurrentThreadId();
|
||||
::AttachThreadInput(dwCurID, dwForeID, TRUE);
|
||||
::SetForegroundWindow((HWND) w->winId());
|
||||
::AttachThreadInput(dwCurID, dwForeID, FALSE);
|
||||
}
|
||||
5
sys/windows/guihelper.h
Normal file
5
sys/windows/guihelper.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
class QWidget;
|
||||
|
||||
void Windows_QWidget_SetForegroundWindow(QWidget* w);
|
||||
7
test/test-qt512-sdk-build.sh
Normal file
7
test/test-qt512-sdk-build.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
QT=$PWD/qtsdk/5.12.12/gcc_64
|
||||
BUILD=build-sdk-qt512
|
||||
rm -rf $BUILD
|
||||
mkdir -p $BUILD
|
||||
cd $BUILD
|
||||
cmake -GNinja -DCMAKE_PREFIX_PATH=$QT ..
|
||||
ninja
|
||||
@@ -1333,10 +1333,6 @@ As of January 1, 2022, compatibility with MD5 authentication information will be
|
||||
<source>This profile is cleartext, don't use it if the server is not in your local network.</source>
|
||||
<translation>该配置为明文传输,如果服务器不在本地局域网,请不要使用。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Another program is running.</source>
|
||||
<translation>另一个 Nekoray 实例正在运行。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation>选择</translation>
|
||||
|
||||
@@ -22,19 +22,13 @@
|
||||
#include "qv2ray/v2/ui/LogHighlighter.hpp"
|
||||
|
||||
#ifndef NKR_NO_EXTERNAL
|
||||
|
||||
#include "3rdparty/ZxingQtReader.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include "3rdparty/WinCommander.hpp"
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <QClipboard>
|
||||
@@ -238,9 +232,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
if (this->isVisible()) {
|
||||
hide();
|
||||
} else {
|
||||
this->showNormal();
|
||||
this->raise();
|
||||
this->activateWindow();
|
||||
ACTIVE_THIS_WINDOW
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -445,6 +437,7 @@ void MainWindow::show_group(int gid) {
|
||||
// callback
|
||||
|
||||
void MainWindow::dialog_message_impl(const QString &sender, const QString &info) {
|
||||
// info
|
||||
if (info.contains("UpdateIcon")) {
|
||||
icon_status = -1;
|
||||
refresh_status();
|
||||
@@ -464,8 +457,10 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
|
||||
if (info == "RestartProgram") {
|
||||
this->exit_reason = 2;
|
||||
on_menu_exit_triggered();
|
||||
} else if (info == "Raise") {
|
||||
ACTIVE_THIS_WINDOW
|
||||
}
|
||||
//
|
||||
// sender
|
||||
if (sender == Dialog_DialogEditProfile) {
|
||||
if (info == "accept") {
|
||||
refresh_proxy_list();
|
||||
|
||||
@@ -310,19 +310,20 @@ void MainWindow::CheckUpdate() {
|
||||
}
|
||||
|
||||
runOnUiThread([=] {
|
||||
auto allow_updater = !NekoRay::dataStore->flag_use_appdata;
|
||||
auto note_pre_release = response.is_pre_release() ? " (Pre-release)" : "";
|
||||
QMessageBox box(QMessageBox::Question, QObject::tr("Update") + note_pre_release,
|
||||
QObject::tr("Update found: %1\nRelease note:\n%2").arg(response.assets_name().c_str(), response.release_note().c_str()));
|
||||
//
|
||||
QAbstractButton *btn1 = nullptr;
|
||||
if (!NekoRay::dataStore->flag_use_appdata) {
|
||||
if (allow_updater) {
|
||||
btn1 = box.addButton(QObject::tr("Update"), QMessageBox::AcceptRole);
|
||||
}
|
||||
QAbstractButton *btn2 = box.addButton(QObject::tr("Open in browser"), QMessageBox::AcceptRole);
|
||||
box.addButton(QObject::tr("Close"), QMessageBox::RejectRole);
|
||||
box.exec();
|
||||
//
|
||||
if (btn1 == box.clickedButton()) {
|
||||
if (btn1 == box.clickedButton() && allow_updater) {
|
||||
// Download Update
|
||||
runOnNewThread([=] {
|
||||
bool ok2;
|
||||
|
||||
Reference in New Issue
Block a user