summary refs log tree commit diff
path: root/third_party
diff options
context:
space:
mode:
authorLorenDB <computersemiexpert@outlook.com>2020-11-09 21:28:41 -0500
committerLoren Burkholder <computersemiexpert@outlook.com>2020-12-24 21:16:46 -0500
commit53f45bdb1c9ee2b42b88ca4587775756f8daa124 (patch)
treeab8b89b8016d37dfb4a1e9e0aaae50a9d86b2d6f /third_party
parentMerge pull request #357 from LorenDB/qkchdocs (diff)
downloadnheko-53f45bdb1c9ee2b42b88ca4587775756f8daa124.tar.xz
Switch profile code to a more flexible method
This introduces a new version of SingleApplication as well.
Diffstat (limited to 'third_party')
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication.cpp201
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/.gitignore (renamed from third_party/SingleApplication-3.1.3.1/.gitignore)0
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/CHANGELOG.md (renamed from third_party/SingleApplication-3.1.3.1/CHANGELOG.md)30
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/CMakeLists.txt (renamed from third_party/SingleApplication-3.1.3.1/CMakeLists.txt)22
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/LICENSE (renamed from third_party/SingleApplication-3.1.3.1/LICENSE)0
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/README.md (renamed from third_party/SingleApplication-3.1.3.1/README.md)15
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/SingleApplication (renamed from third_party/SingleApplication-3.1.3.1/SingleApplication)0
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/Windows.md (renamed from third_party/SingleApplication-3.1.3.1/Windows.md)0
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp274
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/singleapplication.h (renamed from third_party/SingleApplication-3.1.3.1/singleapplication.h)9
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/singleapplication.pri (renamed from third_party/SingleApplication-3.1.3.1/singleapplication.pri)0
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.cpp (renamed from third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp)202
-rw-r--r--third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.h (renamed from third_party/SingleApplication-3.1.3.1/singleapplication_p.h)18
13 files changed, 473 insertions, 298 deletions
diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication.cpp b/third_party/SingleApplication-3.1.3.1/singleapplication.cpp
deleted file mode 100644
index 9af38804..00000000
--- a/third_party/SingleApplication-3.1.3.1/singleapplication.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) Itay Grudev 2015 - 2020
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#include <QtCore/QElapsedTimer>
-#include <QtCore/QThread>
-#include <QtCore/QByteArray>
-#include <QtCore/QSharedMemory>
-#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
-#include <QtCore/QRandomGenerator>
-#else
-#include <QtCore/QDateTime>
-#endif
-
-#include "singleapplication.h"
-#include "singleapplication_p.h"
-
-/**
- * @brief Constructor. Checks and fires up LocalServer or closes the program
- * if another instance already exists
- * @param argc
- * @param argv
- * @param {bool} allowSecondaryInstances
- */
-SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
-    : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
-{
-    Q_D(SingleApplication);
-
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
-    // On Android and iOS since the library is not supported fallback to
-    // standard QApplication behaviour by simply returning at this point.
-    qWarning() << "SingleApplication is not supported on Android and iOS systems.";
-    return;
-#endif
-
-    // Store the current mode of the program
-    d->options = options;
-
-    // Generating an application ID used for identifying the shared memory
-    // block and QLocalServer
-    d->genBlockServerName();
-
-#ifdef Q_OS_UNIX
-    // By explicitly attaching it and then deleting it we make sure that the
-    // memory is deleted even after the process has crashed on Unix.
-    d->memory = new QSharedMemory( d->blockServerName );
-    d->memory->attach();
-    delete d->memory;
-#endif
-    // Guarantee thread safe behaviour with a shared memory block.
-    d->memory = new QSharedMemory( d->blockServerName );
-
-    // Create a shared memory block
-    if( d->memory->create( sizeof( InstancesInfo ) ) ) {
-        // Initialize the shared memory block
-        d->memory->lock();
-        d->initializeMemoryBlock();
-        d->memory->unlock();
-    } else {
-        // Attempt to attach to the memory segment
-        if( ! d->memory->attach() ) {
-            qCritical() << "SingleApplication: Unable to attach to shared memory block.";
-            qCritical() << d->memory->errorString();
-            delete d;
-            ::exit( EXIT_FAILURE );
-        }
-    }
-
-    auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
-    QElapsedTimer time;
-    time.start();
-
-    // Make sure the shared memory block is initialised and in consistent state
-    while( true ) {
-        d->memory->lock();
-
-        if( d->blockChecksum() == inst->checksum ) break;
-
-        if( time.elapsed() > 5000 ) {
-            qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
-            d->initializeMemoryBlock();
-        }
-
-        d->memory->unlock();
-
-        // Random sleep here limits the probability of a collision between two racing apps
-#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
-        QThread::sleep( QRandomGenerator::global()->bounded( 8u, 18u ) );
-#else
-        qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
-        QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
-#endif
-    }
-
-    if( inst->primary == false) {
-        d->startPrimary();
-        d->memory->unlock();
-        return;
-    }
-
-    // Check if another instance can be started
-    if( allowSecondary ) {
-        inst->secondary += 1;
-        inst->checksum = d->blockChecksum();
-        d->instanceNumber = inst->secondary;
-        d->startSecondary();
-        if( d->options & Mode::SecondaryNotification ) {
-            d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
-        }
-        d->memory->unlock();
-        return;
-    }
-
-    d->memory->unlock();
-
-    d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
-
-    delete d;
-
-    ::exit( EXIT_SUCCESS );
-}
-
-/**
- * @brief Destructor
- */
-SingleApplication::~SingleApplication()
-{
-    Q_D(SingleApplication);
-    delete d;
-}
-
-bool SingleApplication::isPrimary()
-{
-    Q_D(SingleApplication);
-    return d->server != nullptr;
-}
-
-bool SingleApplication::isSecondary()
-{
-    Q_D(SingleApplication);
-    return d->server == nullptr;
-}
-
-quint32 SingleApplication::instanceId()
-{
-    Q_D(SingleApplication);
-    return d->instanceNumber;
-}
-
-qint64 SingleApplication::primaryPid()
-{
-    Q_D(SingleApplication);
-    return d->primaryPid();
-}
-
-QString SingleApplication::primaryUser()
-{
-    Q_D(SingleApplication);
-    return d->primaryUser();
-}
-
-QString SingleApplication::currentUser()
-{
-    Q_D(SingleApplication);
-    return d->getUsername();
-}
-
-bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
-{
-    Q_D(SingleApplication);
-
-    // Nobody to connect to
-    if( isPrimary() ) return false;
-
-    // Make sure the socket is connected
-    d->connectToPrimary( timeout,  SingleApplicationPrivate::Reconnect );
-
-    d->socket->write( message );
-    bool dataWritten = d->socket->waitForBytesWritten( timeout );
-    d->socket->flush();
-    return dataWritten;
-}
diff --git a/third_party/SingleApplication-3.1.3.1/.gitignore b/third_party/SingleApplication-3.2.0-dc8042b/.gitignore
index 35533fe8..35533fe8 100644
--- a/third_party/SingleApplication-3.1.3.1/.gitignore
+++ b/third_party/SingleApplication-3.2.0-dc8042b/.gitignore
diff --git a/third_party/SingleApplication-3.1.3.1/CHANGELOG.md b/third_party/SingleApplication-3.2.0-dc8042b/CHANGELOG.md
index 3662b0b2..e2ba290e 100644
--- a/third_party/SingleApplication-3.1.3.1/CHANGELOG.md
+++ b/third_party/SingleApplication-3.2.0-dc8042b/CHANGELOG.md
@@ -3,6 +3,30 @@ Changelog
 
 If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it.
 
+__3.2.0__
+---------
+
+* Added support for Qt 6  - _Jonas Kvinge_
+* Fixed warning in `Qt 5.9` with `min`/`max` functions on Windows - _Nick Korotysh_
+* Fix return value of connectToPrimary() when connect is successful - _Jonas Kvinge_
+* Fix build issue with MinGW GCC pedantic mode - _Iakov Kirilenko_
+* Fixed conversion from `int` to `quint32` and Clang Tidy warnings - _Hennadii Chernyshchyk_
+
+__3.1.5__
+---------
+
+* Improved library stability in edge cases and very rapid process initialisation
+* Fixed Bug where the shared memory block may have been modified without a lock
+* Fixed Bug causing `instanceStarted()` to not get emitted when a second instance
+  has been started before the primary has initiated it's `QLocalServer`.
+
+__3.1.4__
+---------
+* Officially supporting and build-testing against Qt 5.15
+* Fixed an MSVC C4996 warning that suggests using `strncpy_s`.
+
+    _Hennadii Chernyshchyk_
+
 __3.1.3.1__
 ---------
 * CMake build system improvements
@@ -38,18 +62,18 @@ __3.1.0a__
 __3.0.19__
 ----------
 
-* Fixed code warning for depricated functions in Qt 5.10 related to `QTime` and `qrand()`. 
+* Fixed code warning for depricated functions in Qt 5.10 related to `QTime` and `qrand()`.
 
    _Hennadii Chernyshchyk_  
    _Anton Filimonov_  
    _Jonas Kvinge_
-   
+
 __3.0.18__
 ----------
 
 * Fallback to standard QApplication class on iOS and Android systems where
   the library is not supported.
-  
+
 * Added Build CI tests to verify the library builds successfully on Linux, Windows and MacOS  across multiple Qt versions.
 
   _Anton Filimonov_
diff --git a/third_party/SingleApplication-3.1.3.1/CMakeLists.txt b/third_party/SingleApplication-3.2.0-dc8042b/CMakeLists.txt
index 85dba84c..ae1b1439 100644
--- a/third_party/SingleApplication-3.1.3.1/CMakeLists.txt
+++ b/third_party/SingleApplication-3.2.0-dc8042b/CMakeLists.txt
@@ -10,22 +10,28 @@ add_library(${PROJECT_NAME} STATIC
 )
 add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
 
+if(NOT QT_DEFAULT_MAJOR_VERSION)
+    set(QT_DEFAULT_MAJOR_VERSION 5 CACHE STRING "Qt version to use (5 or 6), defaults to 5")
+endif()
+
 # Find dependencies
-find_package(Qt5 COMPONENTS Network REQUIRED)
-target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network)
+set(QT_COMPONENTS Core Network)
+set(QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Core Qt${QT_DEFAULT_MAJOR_VERSION}::Network)
 
 if(QAPPLICATION_CLASS STREQUAL QApplication)
-    find_package(Qt5 COMPONENTS Widgets REQUIRED)
-    target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)
+    list(APPEND QT_COMPONENTS Widgets)
+    list(APPEND QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Widgets)
 elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication)
-    find_package(Qt5 COMPONENTS Gui REQUIRED)
-    target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Gui)
+    list(APPEND QT_COMPONENTS Gui)
+    list(APPEND QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Gui)
 else()
     set(QAPPLICATION_CLASS QCoreApplication)
-    find_package(Qt5 COMPONENTS Core REQUIRED)
-    target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core)
 endif()
 
+find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED)
+
+target_link_libraries(${PROJECT_NAME} PUBLIC ${QT_LIBRARIES})
+
 if(WIN32)
     target_link_libraries(${PROJECT_NAME} PRIVATE advapi32)
 endif()
diff --git a/third_party/SingleApplication-3.1.3.1/LICENSE b/third_party/SingleApplication-3.2.0-dc8042b/LICENSE
index a82e5a68..a82e5a68 100644
--- a/third_party/SingleApplication-3.1.3.1/LICENSE
+++ b/third_party/SingleApplication-3.2.0-dc8042b/LICENSE
diff --git a/third_party/SingleApplication-3.1.3.1/README.md b/third_party/SingleApplication-3.2.0-dc8042b/README.md
index 3c36b557..457ab339 100644
--- a/third_party/SingleApplication-3.1.3.1/README.md
+++ b/third_party/SingleApplication-3.2.0-dc8042b/README.md
@@ -2,7 +2,7 @@ SingleApplication
 =================
 [![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions)
 
-This is a replacement of the QtSingleApplication for `Qt5`.
+This is a replacement of the QtSingleApplication for `Qt5` and `Qt6`.
 
 Keeps the Primary Instance of your Application and kills each subsequent
 instances. It can (if enabled) spawn secondary (non-related to the primary)
@@ -139,13 +139,22 @@ app.isSecondary();
 *__Note:__ If your Primary Instance is terminated a newly launched instance
 will replace the Primary one even if the Secondary flag has been set.*
 
+Examples
+--------
+
+There are three examples provided in this repository:
+
+* Basic example that prevents a secondary instance from starting [`examples/basic`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/basic)
+* An example of a graphical application raising it's parent window [`examples/calculator`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/calculator)
+* A console application sending the primary instance it's command line parameters [`examples/sending_arguments`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/sending_arguments)
+
 API
 ---
 
 ### Members
 
 ```cpp
-SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100, QString userData = QString() )
 ```
 
 Depending on whether `allowSecondary` is set, this constructor may terminate
@@ -154,7 +163,7 @@ can be specified to set whether the SingleApplication block should work
 user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
 used to notify the primary instance whenever a secondary instance had been
 started (disabled by default). `timeout` specifies the maximum time in
-milliseconds to wait for blocking operations.
+milliseconds to wait for blocking operations. Setting `userData` provides additional data that will isolate this instance from other instances that do not have the same (or any) user data set.
 
 *__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
 recognizes.*
diff --git a/third_party/SingleApplication-3.1.3.1/SingleApplication b/third_party/SingleApplication-3.2.0-dc8042b/SingleApplication
index 8ead1a42..8ead1a42 100644
--- a/third_party/SingleApplication-3.1.3.1/SingleApplication
+++ b/third_party/SingleApplication-3.2.0-dc8042b/SingleApplication
diff --git a/third_party/SingleApplication-3.1.3.1/Windows.md b/third_party/SingleApplication-3.2.0-dc8042b/Windows.md
index 13c52da0..13c52da0 100644
--- a/third_party/SingleApplication-3.1.3.1/Windows.md
+++ b/third_party/SingleApplication-3.2.0-dc8042b/Windows.md
diff --git a/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp
new file mode 100644
index 00000000..276ceee9
--- /dev/null
+++ b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.cpp
@@ -0,0 +1,274 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2020
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QByteArray>
+#include <QtCore/QSharedMemory>
+
+#include "singleapplication.h"
+#include "singleapplication_p.h"
+
+/**
+ * @brief Constructor. Checks and fires up LocalServer or closes the program
+ * if another instance already exists
+ * @param argc
+ * @param argv
+ * @param allowSecondary Whether to enable secondary instance support
+ * @param options Optional flags to toggle specific behaviour
+ * @param timeout Maximum time blocking functions are allowed during app load
+ */
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, QString userData )
+    : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
+{
+    Q_D( SingleApplication );
+
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+    // On Android and iOS since the library is not supported fallback to
+    // standard QApplication behaviour by simply returning at this point.
+    qWarning() << "SingleApplication is not supported on Android and iOS systems.";
+    return;
+#endif
+
+    // Store the current mode of the program
+    d->options = options;
+
+    // Add any unique user data
+    if ( ! userData.isEmpty() )
+        d->addAppData( userData );
+
+    // Generating an application ID used for identifying the shared memory
+    // block and QLocalServer
+    d->genBlockServerName();
+
+    // To mitigate QSharedMemory issues with large amount of processes
+    // attempting to attach at the same time
+    SingleApplicationPrivate::randomSleep();
+
+#ifdef Q_OS_UNIX
+    // By explicitly attaching it and then deleting it we make sure that the
+    // memory is deleted even after the process has crashed on Unix.
+    d->memory = new QSharedMemory( d->blockServerName );
+    d->memory->attach();
+    delete d->memory;
+#endif
+    // Guarantee thread safe behaviour with a shared memory block.
+    d->memory = new QSharedMemory( d->blockServerName );
+
+    // Create a shared memory block
+    if( d->memory->create( sizeof( InstancesInfo ) )){
+        // Initialize the shared memory block
+        if( ! d->memory->lock() ){
+          qCritical() << "SingleApplication: Unable to lock memory block after create.";
+          abortSafely();
+        }
+        d->initializeMemoryBlock();
+    } else {
+        if( d->memory->error() == QSharedMemory::AlreadyExists ){
+          // Attempt to attach to the memory segment
+          if( ! d->memory->attach() ){
+              qCritical() << "SingleApplication: Unable to attach to shared memory block.";
+              abortSafely();
+          }
+          if( ! d->memory->lock() ){
+            qCritical() << "SingleApplication: Unable to lock memory block after attach.";
+            abortSafely();
+          }
+        } else {
+          qCritical() << "SingleApplication: Unable to create block.";
+          abortSafely();
+        }
+    }
+
+    auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
+    QElapsedTimer time;
+    time.start();
+
+    // Make sure the shared memory block is initialised and in consistent state
+    while( true ){
+      // If the shared memory block's checksum is valid continue
+      if( d->blockChecksum() == inst->checksum ) break;
+
+      // If more than 5s have elapsed, assume the primary instance crashed and
+      // assume it's position
+      if( time.elapsed() > 5000 ){
+          qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
+          d->initializeMemoryBlock();
+      }
+
+      // Otherwise wait for a random period and try again. The random sleep here
+      // limits the probability of a collision between two racing apps and
+      // allows the app to initialise faster
+      if( ! d->memory->unlock() ){
+        qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
+        qDebug() << d->memory->errorString();
+      }
+      SingleApplicationPrivate::randomSleep();
+      if( ! d->memory->lock() ){
+        qCritical() << "SingleApplication: Unable to lock memory after random wait.";
+        abortSafely();
+      }
+    }
+
+    if( inst->primary == false ){
+        d->startPrimary();
+        if( ! d->memory->unlock() ){
+          qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
+          qDebug() << d->memory->errorString();
+        }
+        return;
+    }
+
+    // Check if another instance can be started
+    if( allowSecondary ){
+        d->startSecondary();
+        if( d->options & Mode::SecondaryNotification ){
+            d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
+        }
+        if( ! d->memory->unlock() ){
+          qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
+          qDebug() << d->memory->errorString();
+        }
+        return;
+    }
+
+    if( ! d->memory->unlock() ){
+      qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
+      qDebug() << d->memory->errorString();
+    }
+
+    d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
+
+    delete d;
+
+    ::exit( EXIT_SUCCESS );
+}
+
+SingleApplication::~SingleApplication()
+{
+    Q_D( SingleApplication );
+    delete d;
+}
+
+/**
+ * Checks if the current application instance is primary.
+ * @return Returns true if the instance is primary, false otherwise.
+ */
+bool SingleApplication::isPrimary()
+{
+    Q_D( SingleApplication );
+    return d->server != nullptr;
+}
+
+/**
+ * Checks if the current application instance is secondary.
+ * @return Returns true if the instance is secondary, false otherwise.
+ */
+bool SingleApplication::isSecondary()
+{
+    Q_D( SingleApplication );
+    return d->server == nullptr;
+}
+
+/**
+ * Allows you to identify an instance by returning unique consecutive instance
+ * ids. It is reset when the first (primary) instance of your app starts and
+ * only incremented afterwards.
+ * @return Returns a unique instance id.
+ */
+quint32 SingleApplication::instanceId()
+{
+    Q_D( SingleApplication );
+    return d->instanceNumber;
+}
+
+/**
+ * Returns the OS PID (Process Identifier) of the process running the primary
+ * instance. Especially useful when SingleApplication is coupled with OS.
+ * specific APIs.
+ * @return Returns the primary instance PID.
+ */
+qint64 SingleApplication::primaryPid()
+{
+    Q_D( SingleApplication );
+    return d->primaryPid();
+}
+
+/**
+ * Returns the username the primary instance is running as.
+ * @return Returns the username the primary instance is running as.
+ */
+QString SingleApplication::primaryUser()
+{
+    Q_D( SingleApplication );
+    return d->primaryUser();
+}
+
+/**
+ * Returns the username the current instance is running as.
+ * @return Returns the username the current instance is running as.
+ */
+QString SingleApplication::currentUser()
+{
+    return SingleApplicationPrivate::getUsername();
+}
+
+/**
+ * Sends message to the Primary Instance.
+ * @param message The message to send.
+ * @param timeout the maximum timeout in milliseconds for blocking functions.
+ * @return true if the message was sent successfuly, false otherwise.
+ */
+bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
+{
+    Q_D( SingleApplication );
+
+    // Nobody to connect to
+    if( isPrimary() ) return false;
+
+    // Make sure the socket is connected
+    if( ! d->connectToPrimary( timeout,  SingleApplicationPrivate::Reconnect ) )
+      return false;
+
+    d->socket->write( message );
+    bool dataWritten = d->socket->waitForBytesWritten( timeout );
+    d->socket->flush();
+    return dataWritten;
+}
+
+/**
+ * Cleans up the shared memory block and exits with a failure.
+ * This function halts program execution.
+ */
+void SingleApplication::abortSafely()
+{
+    Q_D( SingleApplication );
+
+    qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
+    delete d;
+    ::exit( EXIT_FAILURE );
+}
+
+QStringList SingleApplication::userData()
+{
+    Q_D( SingleApplication );
+    return d->appData();
+}
diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication.h b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.h
index fd806a3d..d39a6614 100644
--- a/third_party/SingleApplication-3.1.3.1/singleapplication.h
+++ b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.h
@@ -85,7 +85,7 @@ public:
      * Usually 4*timeout would be the worst case (fail) scenario.
      * @see See the corresponding QAPPLICATION_CLASS constructor for reference
      */
-    explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
+    explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, QString userData = QString() );
     ~SingleApplication() override;
 
     /**
@@ -133,6 +133,12 @@ public:
      */
     bool sendMessage( const QByteArray &message, int timeout = 100 );
 
+    /**
+     * @brief Get the set user data.
+     * @returns {QStringList}
+     */
+    QStringList userData();
+
 Q_SIGNALS:
     void instanceStarted();
     void receivedMessage( quint32 instanceId, QByteArray message );
@@ -140,6 +146,7 @@ Q_SIGNALS:
 private:
     SingleApplicationPrivate *d_ptr;
     Q_DECLARE_PRIVATE(SingleApplication)
+    void abortSafely();
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication.pri b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.pri
index ae81f599..ae81f599 100644
--- a/third_party/SingleApplication-3.1.3.1/singleapplication.pri
+++ b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication.pri
diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.cpp
index 705609f2..1ab58c23 100644
--- a/third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp
+++ b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.cpp
@@ -33,12 +33,20 @@
 #include <cstddef>
 
 #include <QtCore/QDir>
+#include <QtCore/QThread>
 #include <QtCore/QByteArray>
 #include <QtCore/QDataStream>
+#include <QtCore/QElapsedTimer>
 #include <QtCore/QCryptographicHash>
 #include <QtNetwork/QLocalServer>
 #include <QtNetwork/QLocalSocket>
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+#include <QtCore/QRandomGenerator>
+#else
+#include <QtCore/QDateTime>
+#endif
+
 #include "singleapplication.h"
 #include "singleapplication_p.h"
 
@@ -49,6 +57,9 @@
 #endif
 
 #ifdef Q_OS_WIN
+    #ifndef NOMINMAX
+        #define NOMINMAX 1
+    #endif
     #include <windows.h>
     #include <lmcons.h>
 #endif
@@ -59,20 +70,20 @@ SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
     server = nullptr;
     socket = nullptr;
     memory = nullptr;
-    instanceNumber = -1;
+    instanceNumber = 0;
 }
 
 SingleApplicationPrivate::~SingleApplicationPrivate()
 {
-    if( socket != nullptr ) {
+    if( socket != nullptr ){
         socket->close();
         delete socket;
     }
 
-    if( memory != nullptr ) {
+    if( memory != nullptr ){
         memory->lock();
         auto *inst = static_cast<InstancesInfo*>(memory->data());
-        if( server != nullptr ) {
+        if( server != nullptr ){
             server->close();
             delete server;
             inst->primary = false;
@@ -106,7 +117,7 @@ QString SingleApplicationPrivate::getUsername()
       struct passwd *pw = getpwuid( uid );
       if( pw )
           username = QString::fromLocal8Bit( pw->pw_name );
-      if ( username.isEmpty() ) {
+      if ( username.isEmpty() ){
 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
           username = QString::fromLocal8Bit( qgetenv( "USER" ) );
 #else
@@ -125,11 +136,14 @@ void SingleApplicationPrivate::genBlockServerName()
     appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
     appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
 
-    if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
+    if ( ! appDataList.isEmpty() )
+        appData.addData( appDataList.join( "" ).toUtf8() );
+
+    if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
         appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
     }
 
-    if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
+    if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
 #ifdef Q_OS_WIN
         appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
 #else
@@ -138,7 +152,7 @@ void SingleApplicationPrivate::genBlockServerName()
     }
 
     // User level block requires a user specific data in the hash
-    if( options & SingleApplication::Mode::User ) {
+    if( options & SingleApplication::Mode::User ){
         appData.addData( getUsername().toUtf8() );
     }
 
@@ -147,7 +161,7 @@ void SingleApplicationPrivate::genBlockServerName()
     blockServerName = appData.result().toBase64().replace("/", "_");
 }
 
-void SingleApplicationPrivate::initializeMemoryBlock()
+void SingleApplicationPrivate::initializeMemoryBlock() const
 {
     auto *inst = static_cast<InstancesInfo*>( memory->data() );
     inst->primary = false;
@@ -159,8 +173,14 @@ void SingleApplicationPrivate::initializeMemoryBlock()
 
 void SingleApplicationPrivate::startPrimary()
 {
-    Q_Q(SingleApplication);
+    // Reset the number of connections
+    auto *inst = static_cast <InstancesInfo*>( memory->data() );
 
+    inst->primary = true;
+    inst->primaryPid = QCoreApplication::applicationPid();
+    qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
+    inst->checksum = blockChecksum();
+    instanceNumber = 0;
     // Successful creation means that no main process exists
     // So we start a QLocalServer to listen for connections
     QLocalServer::removeServer( blockServerName );
@@ -168,10 +188,10 @@ void SingleApplicationPrivate::startPrimary()
 
     // Restrict access to the socket according to the
     // SingleApplication::Mode::User flag on User level or no restrictions
-    if( options & SingleApplication::Mode::User ) {
-      server->setSocketOptions( QLocalServer::UserAccessOption );
+    if( options & SingleApplication::Mode::User ){
+        server->setSocketOptions( QLocalServer::UserAccessOption );
     } else {
-      server->setSocketOptions( QLocalServer::WorldAccessOption );
+        server->setSocketOptions( QLocalServer::WorldAccessOption );
     }
 
     server->listen( blockServerName );
@@ -181,87 +201,95 @@ void SingleApplicationPrivate::startPrimary()
         this,
         &SingleApplicationPrivate::slotConnectionEstablished
     );
-
-    // Reset the number of connections
-    auto *inst = static_cast <InstancesInfo*>( memory->data() );
-
-    inst->primary = true;
-    inst->primaryPid = q->applicationPid();
-    strncpy( inst->primaryUser, getUsername().toUtf8().data(), 127 );
-    inst->primaryUser[127] = '\0';
-    inst->checksum = blockChecksum();
-
-    instanceNumber = 0;
 }
 
 void SingleApplicationPrivate::startSecondary()
 {
+  auto *inst = static_cast <InstancesInfo*>( memory->data() );
+
+  inst->secondary += 1;
+  inst->checksum = blockChecksum();
+  instanceNumber = inst->secondary;
 }
 
-void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
+bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
 {
+    QElapsedTimer time;
+    time.start();
+
     // Connect to the Local Server of the Primary Instance if not already
     // connected.
-    if( socket == nullptr ) {
+    if( socket == nullptr ){
         socket = new QLocalSocket();
     }
 
-    // If already connected - we are done;
-    if( socket->state() == QLocalSocket::ConnectedState )
-        return;
+    if( socket->state() == QLocalSocket::ConnectedState ) return true;
 
-    // If not connect
-    if( socket->state() == QLocalSocket::UnconnectedState ||
-        socket->state() == QLocalSocket::ClosingState ) {
-        socket->connectToServer( blockServerName );
-    }
+    if( socket->state() != QLocalSocket::ConnectedState ){
+
+        while( true ){
+            randomSleep();
 
-    // Wait for being connected
-    if( socket->state() == QLocalSocket::ConnectingState ) {
-        socket->waitForConnected( msecs );
+          if( socket->state() != QLocalSocket::ConnectingState )
+            socket->connectToServer( blockServerName );
+
+          if( socket->state() == QLocalSocket::ConnectingState ){
+              socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
+          }
+
+          // If connected break out of the loop
+          if( socket->state() == QLocalSocket::ConnectedState ) break;
+
+          // If elapsed time since start is longer than the method timeout return
+          if( time.elapsed() >= msecs ) return false;
+        }
     }
 
     // Initialisation message according to the SingleApplication protocol
-    if( socket->state() == QLocalSocket::ConnectedState ) {
-        // Notify the parent that a new instance had been started;
-        QByteArray initMsg;
-        QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
+    QByteArray initMsg;
+    QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
 
 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
-        writeStream.setVersion(QDataStream::Qt_5_6);
+    writeStream.setVersion(QDataStream::Qt_5_6);
 #endif
 
-        writeStream << blockServerName.toLatin1();
-        writeStream << static_cast<quint8>(connectionType);
-        writeStream << instanceNumber;
-        quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
-        writeStream << checksum;
+    writeStream << blockServerName.toLatin1();
+    writeStream << static_cast<quint8>(connectionType);
+    writeStream << instanceNumber;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
+#else
+    quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
+#endif
+    writeStream << checksum;
 
-        // The header indicates the message length that follows
-        QByteArray header;
-        QDataStream headerStream(&header, QIODevice::WriteOnly);
+    // The header indicates the message length that follows
+    QByteArray header;
+    QDataStream headerStream(&header, QIODevice::WriteOnly);
 
 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
-        headerStream.setVersion(QDataStream::Qt_5_6);
+    headerStream.setVersion(QDataStream::Qt_5_6);
 #endif
-        headerStream << static_cast <quint64>( initMsg.length() );
+    headerStream << static_cast <quint64>( initMsg.length() );
 
-        socket->write( header );
-        socket->write( initMsg );
-        socket->flush();
-        socket->waitForBytesWritten( msecs );
-    }
+    socket->write( header );
+    socket->write( initMsg );
+    bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
+    socket->flush();
+    return result;
 }
 
-quint16 SingleApplicationPrivate::blockChecksum()
+quint16 SingleApplicationPrivate::blockChecksum() const
 {
-    return qChecksum(
-       static_cast <const char *>( memory->data() ),
-       offsetof( InstancesInfo, checksum )
-   );
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
+#else
+    quint16 checksum = qChecksum(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum));
+#endif
+    return checksum;
 }
 
-qint64 SingleApplicationPrivate::primaryPid()
+qint64 SingleApplicationPrivate::primaryPid() const
 {
     qint64 pid;
 
@@ -273,7 +301,7 @@ qint64 SingleApplicationPrivate::primaryPid()
     return pid;
 }
 
-QString SingleApplicationPrivate::primaryUser()
+QString SingleApplicationPrivate::primaryUser() const
 {
     QByteArray username;
 
@@ -294,7 +322,7 @@ void SingleApplicationPrivate::slotConnectionEstablished()
     connectionMap.insert(nextConnSocket, ConnectionInfo());
 
     QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
-        [nextConnSocket, this]() {
+        [nextConnSocket, this](){
             auto &info = connectionMap[nextConnSocket];
             Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
         }
@@ -308,9 +336,9 @@ void SingleApplicationPrivate::slotConnectionEstablished()
     );
 
     QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
-        [nextConnSocket, this]() {
+        [nextConnSocket, this](){
             auto &info = connectionMap[nextConnSocket];
-            switch(info.stage) {
+            switch(info.stage){
             case StageHeader:
                 readInitMessageHeader(nextConnSocket);
                 break;
@@ -329,11 +357,11 @@ void SingleApplicationPrivate::slotConnectionEstablished()
 
 void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
 {
-    if (!connectionMap.contains( sock )) {
+    if (!connectionMap.contains( sock )){
         return;
     }
 
-    if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
+    if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
         return;
     }
 
@@ -350,7 +378,7 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
     info.stage = StageBody;
     info.msgLen = msgLen;
 
-    if ( sock->bytesAvailable() >= (qint64) msgLen ) {
+    if ( sock->bytesAvailable() >= (qint64) msgLen ){
         readInitMessageBody( sock );
     }
 }
@@ -359,12 +387,12 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
 {
     Q_Q(SingleApplication);
 
-    if (!connectionMap.contains( sock )) {
+    if (!connectionMap.contains( sock )){
         return;
     }
 
     ConnectionInfo &info = connectionMap[sock];
-    if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
+    if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
         return;
     }
 
@@ -394,13 +422,17 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
     quint16 msgChecksum = 0;
     readStream >> msgChecksum;
 
-    const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
+#else
+    const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
+#endif
 
     bool isValid = readStream.status() == QDataStream::Ok &&
                    QLatin1String(latin1Name) == blockServerName &&
                    msgChecksum == actualChecksum;
 
-    if( !isValid ) {
+    if( !isValid ){
         sock->close();
         return;
     }
@@ -415,7 +447,7 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
         Q_EMIT q->instanceStarted();
     }
 
-    if (sock->bytesAvailable() > 0) {
+    if (sock->bytesAvailable() > 0){
         Q_EMIT this->slotDataAvailable( sock, instanceId );
     }
 }
@@ -431,3 +463,23 @@ void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedS
     if( closedSocket->bytesAvailable() > 0 )
         Q_EMIT slotDataAvailable( closedSocket, instanceId  );
 }
+
+void SingleApplicationPrivate::randomSleep()
+{
+#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
+    QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
+#else
+    qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
+    QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
+#endif
+}
+
+void SingleApplicationPrivate::addAppData(const QString &data)
+{
+    appDataList.push_back(data);
+}
+
+QStringList SingleApplicationPrivate::appData() const
+{
+    return appDataList;
+}
diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication_p.h b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.h
index 29ba346b..c49a46dd 100644
--- a/third_party/SingleApplication-3.1.3.1/singleapplication_p.h
+++ b/third_party/SingleApplication-3.2.0-dc8042b/singleapplication_p.h
@@ -41,8 +41,8 @@ struct InstancesInfo {
     bool primary;
     quint32 secondary;
     qint64 primaryPid;
-    quint16 checksum;
     char primaryUser[128];
+    quint16 checksum; // Must be the last field
 };
 
 struct ConnectionInfo {
@@ -70,17 +70,20 @@ public:
     SingleApplicationPrivate( SingleApplication *q_ptr );
     ~SingleApplicationPrivate() override;
 
-    QString getUsername();
+    static QString getUsername();
     void genBlockServerName();
-    void initializeMemoryBlock();
+    void initializeMemoryBlock() const;
     void startPrimary();
     void startSecondary();
-    void connectToPrimary(int msecs, ConnectionType connectionType );
-    quint16 blockChecksum();
-    qint64 primaryPid();
-    QString primaryUser();
+    bool connectToPrimary( int msecs, ConnectionType connectionType );
+    quint16 blockChecksum() const;
+    qint64 primaryPid() const;
+    QString primaryUser() const;
     void readInitMessageHeader(QLocalSocket *socket);
     void readInitMessageBody(QLocalSocket *socket);
+    static void randomSleep();
+    void addAppData(const QString &data);
+    QStringList appData() const;
 
     SingleApplication *q_ptr;
     QSharedMemory *memory;
@@ -90,6 +93,7 @@ public:
     QString blockServerName;
     SingleApplication::Options options;
     QMap<QLocalSocket*, ConnectionInfo> connectionMap;
+    QStringList appDataList;
 
 public Q_SLOTS:
     void slotConnectionEstablished();