feat: activate window when multiple open

This commit is contained in:
arm64v8a
2022-11-27 18:24:21 +09:00
parent db6569ffb0
commit 69390afb9a
14 changed files with 120 additions and 35 deletions

6
.gitignore vendored
View File

@@ -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

View File

@@ -1,5 +1,6 @@
#pragma once
#include <future>
#include <QThread>
// FOR OLD QT

27
3rdparty/RunGuard.hpp vendored
View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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());

View File

@@ -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");
}

View File

@@ -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
View 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
View File

@@ -0,0 +1,5 @@
#pragma once
class QWidget;
void Windows_QWidget_SetForegroundWindow(QWidget* w);

View 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

View File

@@ -1333,10 +1333,6 @@ As of January 1, 2022, compatibility with MD5 authentication information will be
<source>This profile is cleartext, don&apos;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>

View File

@@ -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();

View File

@@ -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;