summary refs log tree commit diff
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2017-12-14 21:55:00 +0200
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2017-12-14 21:55:00 +0200
commit65672d3dfbdf179df520bbaa5e7e7ba28a27db20 (patch)
tree3db1f2f563f989ecbc64af9048c2285268c2b59f
parentUpgrade appveyor to Qt 5.9.2 (diff)
downloadnheko-65672d3dfbdf179df520bbaa5e7e7ba28a27db20.tar.xz
Allow only one application instance
fixes #141
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/RunGuard.h31
-rw-r--r--src/RunGuard.cc84
-rw-r--r--src/main.cc69
4 files changed, 180 insertions, 5 deletions
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 <QObject>
+#include <QSharedMemory>
+#include <QSystemSemaphore>
+
+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 <QCryptographicHash>
+
+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 <QDesktopWidget>
 #include <QFile>
 #include <QFontDatabase>
+#include <QLabel>
+#include <QLayout>
 #include <QLibraryInfo>
 #include <QNetworkProxy>
+#include <QPalette>
+#include <QPoint>
+#include <QPushButton>
 #include <QSettings>
 #include <QTranslator>
 
+#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);