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