From 65672d3dfbdf179df520bbaa5e7e7ba28a27db20 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 14 Dec 2017 21:55:00 +0200 Subject: [PATCH] Allow only one application instance fixes #141 --- CMakeLists.txt | 1 + include/RunGuard.h | 31 +++++++++++++++++ src/RunGuard.cc | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.cc | 69 ++++++++++++++++++++++++++++++++++--- 4 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 include/RunGuard.h create mode 100644 src/RunGuard.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a0a1360..de45f864 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,7 @@ set(SRC_FILES src/RoomList.cc src/RoomMessages.cc src/RoomState.cc + src/RunGuard.cc src/SideBarActions.cc src/Splitter.cc src/TextInputWidget.cc diff --git a/include/RunGuard.h b/include/RunGuard.h new file mode 100644 index 00000000..f9a9641a --- /dev/null +++ b/include/RunGuard.h @@ -0,0 +1,31 @@ +#pragma once + +// +// Taken from +// https://stackoverflow.com/questions/5006547/qt-best-practice-for-a-single-instance-app-protection +// + +#include +#include +#include + +class RunGuard +{ +public: + RunGuard(const QString &key); + ~RunGuard(); + + bool isAnotherRunning(); + bool tryToRun(); + void release(); + +private: + const QString key; + const QString memLockKey; + const QString sharedmemKey; + + QSharedMemory sharedMem; + QSystemSemaphore memLock; + + Q_DISABLE_COPY(RunGuard) +}; diff --git a/src/RunGuard.cc b/src/RunGuard.cc new file mode 100644 index 00000000..75833eb7 --- /dev/null +++ b/src/RunGuard.cc @@ -0,0 +1,84 @@ +#include "RunGuard.h" + +#include + +namespace { + +QString +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(); + { + // Fix for *nix: http://habrahabr.ru/post/173281/ + QSharedMemory fix(sharedmemKey); + 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::tryToRun() +{ + // Extra check + if (isAnotherRunning()) + 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(); +} diff --git a/src/main.cc b/src/main.cc index 89731ec7..5138ec1b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,12 +19,31 @@ #include #include #include +#include +#include #include #include +#include +#include +#include #include #include +#include "Config.h" #include "MainWindow.h" +#include "RaisedButton.h" +#include "RunGuard.h" + +QPoint +screenCenter(int width, int height) +{ + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + + int x = (screenGeometry.width() - width) / 2; + int y = (screenGeometry.height() - height) / 2; + + return QPoint(x, y); +} void setupProxy() @@ -55,6 +74,50 @@ setupProxy() int main(int argc, char *argv[]) { + RunGuard guard("run_guard"); + + if (!guard.tryToRun()) { + QApplication a(argc, argv); + + QFont font; + font.setPointSize(15); + font.setWeight(60); + + QWidget widget; + QVBoxLayout layout(&widget); + layout.setContentsMargins(20, 10, 20, 20); + layout.setSpacing(0); + + QHBoxLayout btnLayout; + + QLabel msg("Another instance of nheko is currently running."); + msg.setWordWrap(true); + msg.setFont(font); + + QPalette pal; + + RaisedButton submitBtn("OK"); + submitBtn.setBackgroundColor(pal.color(QPalette::Button)); + submitBtn.setForegroundColor(pal.color(QPalette::ButtonText)); + submitBtn.setMinimumSize(120, 35); + submitBtn.setFontSize(conf::btn::fontSize); + submitBtn.setCornerRadius(conf::btn::cornerRadius); + + btnLayout.addStretch(1); + btnLayout.addWidget(&submitBtn); + + layout.addWidget(&msg); + layout.addLayout(&btnLayout); + + widget.setFixedSize(480, 180); + widget.move(screenCenter(widget.width(), widget.height())); + widget.show(); + + QObject::connect(&submitBtn, &QPushButton::clicked, &widget, &QWidget::close); + + return a.exec(); + } + QCoreApplication::setApplicationName("nheko"); QCoreApplication::setApplicationVersion("0.1.0"); QCoreApplication::setOrganizationName("nheko"); @@ -95,11 +158,7 @@ main(int argc, char *argv[]) MainWindow w; // Move the MainWindow to the center - QRect screenGeometry = QApplication::desktop()->screenGeometry(); - int x = (screenGeometry.width() - w.width()) / 2; - int y = (screenGeometry.height() - w.height()) / 2; - - w.move(x, y); + w.move(screenCenter(w.width(), w.height())); w.show(); QObject::connect(&app, &QApplication::aboutToQuit, &w, &MainWindow::saveCurrentWindowSize);